Repository: ptmt/react-native-macos Branch: master Commit: 1bb1d251116b Files: 2756 Total size: 11.4 MB Directory structure: gitextract_omq0812w/ ├── .buckconfig ├── .circleci/ │ └── config.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .gitattributes ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── bug.md │ └── feature.md ├── .gitignore ├── .npmignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ContainerShip/ │ ├── Dockerfile.android │ ├── Dockerfile.android-base │ ├── Dockerfile.javascript │ └── scripts/ │ ├── run-android-ci-instrumentation-tests.js │ ├── run-android-docker-instrumentation-tests.sh │ ├── run-android-docker-unit-tests.sh │ ├── run-ci-e2e-tests.sh │ └── run-instrumentation-tests-via-adb-shell.sh ├── DockerTests.md ├── IntegrationTests/ │ ├── AccessibilityManagerTest.js │ ├── AppEventsTest.js │ ├── AsyncStorageTest.js │ ├── ImageCachePolicyTest.js │ ├── ImageSnapshotTest.js │ ├── IntegrationTestHarnessTest.js │ ├── IntegrationTestsApp.js │ ├── LayoutEventsTest.js │ ├── LoggingTestModule.js │ ├── PromiseTest.js │ ├── PropertiesUpdateTest.js │ ├── RCTRootViewIntegrationTestApp.js │ ├── ReactContentSizeUpdateTest.js │ ├── SimpleSnapshotTest.js │ ├── SizeFlexibilityUpdateTest.js │ ├── SyncMethodTest.js │ ├── TimersTest.js │ ├── WebSocketTest.js │ ├── WebViewTest.js │ ├── launchWebSocketServer.command │ └── websocket_integration_test_server.js ├── Jenkinsfile ├── LICENSE ├── LICENSE-docs ├── Libraries/ │ ├── .eslintrc │ ├── ART/ │ │ ├── ART.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── ARTCGFloatArray.h │ │ ├── ARTContainer.h │ │ ├── ARTGroup.h │ │ ├── ARTGroup.m │ │ ├── ARTNode.h │ │ ├── ARTNode.m │ │ ├── ARTRenderable.h │ │ ├── ARTRenderable.m │ │ ├── ARTSerializablePath.js │ │ ├── ARTShape.h │ │ ├── ARTShape.m │ │ ├── ARTSurfaceView.h │ │ ├── ARTSurfaceView.m │ │ ├── ARTText.h │ │ ├── ARTText.m │ │ ├── ARTTextFrame.h │ │ ├── Brushes/ │ │ │ ├── ARTBrush.h │ │ │ ├── ARTBrush.m │ │ │ ├── ARTLinearGradient.h │ │ │ ├── ARTLinearGradient.m │ │ │ ├── ARTPattern.h │ │ │ ├── ARTPattern.m │ │ │ ├── ARTRadialGradient.h │ │ │ ├── ARTRadialGradient.m │ │ │ ├── ARTSolidColor.h │ │ │ └── ARTSolidColor.m │ │ ├── RCTConvert+ART.h │ │ ├── RCTConvert+ART.m │ │ ├── ReactNativeART.js │ │ └── ViewManagers/ │ │ ├── ARTGroupManager.h │ │ ├── ARTGroupManager.m │ │ ├── ARTNodeManager.h │ │ ├── ARTNodeManager.m │ │ ├── ARTRenderableManager.h │ │ ├── ARTRenderableManager.m │ │ ├── ARTShapeManager.h │ │ ├── ARTShapeManager.m │ │ ├── ARTSurfaceViewManager.h │ │ ├── ARTSurfaceViewManager.m │ │ ├── ARTTextManager.h │ │ └── ARTTextManager.m │ ├── ActionSheetIOS/ │ │ ├── ActionSheetIOS.js │ │ ├── RCTActionSheet.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTActionSheetManager.h │ │ └── RCTActionSheetManager.m │ ├── Alert/ │ │ ├── Alert.js │ │ ├── AlertIOS.js │ │ ├── RCTAlertManager.android.js │ │ └── RCTAlertManager.ios.js │ ├── Animated/ │ │ ├── examples/ │ │ │ ├── demo.html │ │ │ └── style.css │ │ ├── release/ │ │ │ ├── gulpfile.js │ │ │ └── package.json │ │ └── src/ │ │ ├── Animated.js │ │ ├── AnimatedEvent.js │ │ ├── AnimatedImplementation.js │ │ ├── AnimatedWeb.js │ │ ├── Easing.js │ │ ├── NativeAnimatedHelper.js │ │ ├── SpringConfig.js │ │ ├── __tests__/ │ │ │ ├── Animated-test.js │ │ │ ├── AnimatedNative-test.js │ │ │ ├── Easing-test.js │ │ │ ├── Interpolation-test.js │ │ │ └── bezier-test.js │ │ ├── animations/ │ │ │ ├── Animation.js │ │ │ ├── DecayAnimation.js │ │ │ ├── SpringAnimation.js │ │ │ └── TimingAnimation.js │ │ ├── bezier.js │ │ ├── createAnimatedComponent.js │ │ ├── nodes/ │ │ │ ├── AnimatedAddition.js │ │ │ ├── AnimatedDiffClamp.js │ │ │ ├── AnimatedDivision.js │ │ │ ├── AnimatedInterpolation.js │ │ │ ├── AnimatedModulo.js │ │ │ ├── AnimatedMultiplication.js │ │ │ ├── AnimatedNode.js │ │ │ ├── AnimatedProps.js │ │ │ ├── AnimatedStyle.js │ │ │ ├── AnimatedTracking.js │ │ │ ├── AnimatedTransform.js │ │ │ ├── AnimatedValue.js │ │ │ ├── AnimatedValueXY.js │ │ │ └── AnimatedWithChildren.js │ │ └── polyfills/ │ │ ├── InteractionManager.js │ │ ├── Set.js │ │ └── flattenStyle.js │ ├── AppState/ │ │ └── AppState.js │ ├── BatchedBridge/ │ │ ├── BatchedBridge.js │ │ ├── MessageQueue.js │ │ ├── NativeModules.js │ │ ├── __mocks__/ │ │ │ ├── MessageQueueTestConfig.js │ │ │ └── MessageQueueTestModule.js │ │ └── __tests__/ │ │ ├── MessageQueue-test.js │ │ └── NativeModules-test.js │ ├── Blob/ │ │ ├── Blob.js │ │ ├── BlobTypes.js │ │ ├── RCTBlob.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTBlobManager.h │ │ ├── RCTBlobManager.m │ │ └── URL.js │ ├── BugReporting/ │ │ ├── BugReporting.js │ │ ├── dumpReactTree.js │ │ └── getReactData.js │ ├── CameraRoll/ │ │ ├── CameraRoll.js │ │ ├── ImagePickerIOS.js │ │ ├── RCTAssetsLibraryRequestHandler.h │ │ ├── RCTAssetsLibraryRequestHandler.m │ │ ├── RCTCameraRoll.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTCameraRollManager.h │ │ ├── RCTCameraRollManager.m │ │ ├── RCTImagePickerManager.h │ │ ├── RCTImagePickerManager.m │ │ ├── RCTPhotoLibraryImageLoader.h │ │ └── RCTPhotoLibraryImageLoader.m │ ├── Components/ │ │ ├── AccessibilityInfo/ │ │ │ ├── AccessibilityInfo.android.js │ │ │ ├── AccessibilityInfo.ios.js │ │ │ └── AccessibilityInfo.macos.js │ │ ├── ActivityIndicator/ │ │ │ └── ActivityIndicator.js │ │ ├── AppleTV/ │ │ │ ├── TVEventHandler.android.js │ │ │ ├── TVEventHandler.ios.js │ │ │ ├── TVEventHandler.macos.js │ │ │ └── TVViewPropTypes.js │ │ ├── Button.js │ │ ├── CheckBox/ │ │ │ ├── CheckBox.android.js │ │ │ ├── CheckBox.ios.js │ │ │ └── CheckBox.macos.js │ │ ├── Clipboard/ │ │ │ └── Clipboard.js │ │ ├── Cursor/ │ │ │ └── Cursor.macos.js │ │ ├── DatePicker/ │ │ │ └── DatePickerIOS.macos.js │ │ ├── DatePickerAndroid/ │ │ │ ├── DatePickerAndroid.android.js │ │ │ ├── DatePickerAndroid.ios.js │ │ │ └── DatePickerAndroid.macos.js │ │ ├── DrawerAndroid/ │ │ │ ├── DrawerLayoutAndroid.android.js │ │ │ └── DrawerLayoutAndroid.macos.js │ │ ├── Intent/ │ │ │ └── IntentAndroid.macos.js │ │ ├── Keyboard/ │ │ │ ├── Keyboard.ios.js │ │ │ ├── Keyboard.macos.js │ │ │ └── KeyboardAvoidingView.js │ │ ├── LazyRenderer.js │ │ ├── MaskedView/ │ │ │ ├── MaskedViewIOS.android.js │ │ │ ├── MaskedViewIOS.ios.js │ │ │ └── MaskedViewIOS.macos.js │ │ ├── Navigation/ │ │ │ ├── NavigatorIOS.ios.js │ │ │ └── NavigatorIOS.macos.js │ │ ├── Picker/ │ │ │ ├── Picker.js │ │ │ ├── PickerAndroid.android.js │ │ │ ├── PickerAndroid.ios.js │ │ │ ├── PickerIOS.android.js │ │ │ └── PickerIOS.ios.js │ │ ├── ProgressBarAndroid/ │ │ │ ├── ProgressBarAndroid.android.js │ │ │ └── ProgressBarAndroid.macos.js │ │ ├── ProgressViewIOS/ │ │ │ ├── ProgressViewIOS.android.js │ │ │ ├── ProgressViewIOS.ios.js │ │ │ └── ProgressViewIOS.macos.js │ │ ├── RefreshControl/ │ │ │ ├── RefreshControl.js │ │ │ └── __mocks__/ │ │ │ └── RefreshControlMock.js │ │ ├── SafeAreaView/ │ │ │ ├── SafeAreaView.android.js │ │ │ ├── SafeAreaView.ios.js │ │ │ └── SafeAreaView.macos.js │ │ ├── ScrollResponder.js │ │ ├── ScrollView/ │ │ │ ├── RecyclerViewBackedScrollView.macos.js │ │ │ ├── ScrollView.js │ │ │ ├── ScrollViewStickyHeader.js │ │ │ ├── __mocks__/ │ │ │ │ └── ScrollViewMock.js │ │ │ └── processDecelerationRate.js │ │ ├── SegmentedControlIOS/ │ │ │ ├── SegmentedControlIOS.android.js │ │ │ └── SegmentedControlIOS.macos.js │ │ ├── Slider/ │ │ │ └── Slider.js │ │ ├── StaticContainer.react.js │ │ ├── StaticRenderer.js │ │ ├── StatusBar/ │ │ │ ├── StatusBar.js │ │ │ ├── StatusBarIOS.android.js │ │ │ ├── StatusBarIOS.ios.js │ │ │ └── StatusBarIOS.macos.js │ │ ├── Subscribable.js │ │ ├── Switch/ │ │ │ └── Switch.js │ │ ├── TabBarIOS/ │ │ │ ├── TabBarIOS.android.js │ │ │ ├── TabBarIOS.ios.js │ │ │ ├── TabBarIOS.macos.js │ │ │ ├── TabBarItemIOS.android.js │ │ │ └── TabBarItemIOS.ios.js │ │ ├── TextInput/ │ │ │ ├── TextInput.js │ │ │ └── TextInputState.js │ │ ├── TimePickerAndroid/ │ │ │ ├── TimePickerAndroid.android.js │ │ │ ├── TimePickerAndroid.ios.js │ │ │ └── TimePickerAndroid.macos.js │ │ ├── ToastAndroid/ │ │ │ ├── ToastAndroid.android.js │ │ │ └── ToastAndroid.ios.js │ │ ├── ToolbarAndroid/ │ │ │ ├── ToolbarAndroid.android.js │ │ │ └── ToolbarAndroid.ios.js │ │ ├── Touchable/ │ │ │ ├── BoundingDimensions.js │ │ │ ├── PooledClass.js │ │ │ ├── Position.js │ │ │ ├── Touchable.js │ │ │ ├── TouchableBounce.js │ │ │ ├── TouchableHighlight.js │ │ │ ├── TouchableNativeFeedback.android.js │ │ │ ├── TouchableNativeFeedback.ios.js │ │ │ ├── TouchableNativeFeedback.macos.js │ │ │ ├── TouchableOpacity.js │ │ │ ├── TouchableWithoutFeedback.js │ │ │ ├── __mocks__/ │ │ │ │ └── ensureComponentIsNative.js │ │ │ ├── __tests__/ │ │ │ │ ├── TouchableHighlight-test.js │ │ │ │ └── __snapshots__/ │ │ │ │ └── TouchableHighlight-test.js.snap │ │ │ ├── ensureComponentIsNative.js │ │ │ └── ensurePositiveDelayProps.js │ │ ├── UnimplementedViews/ │ │ │ └── UnimplementedView.js │ │ ├── View/ │ │ │ ├── PlatformViewPropTypes.android.js │ │ │ ├── PlatformViewPropTypes.ios.js │ │ │ ├── PlatformViewPropTypes.macos.js │ │ │ ├── ReactNativeStyleAttributes.js │ │ │ ├── ReactNativeViewAttributes.js │ │ │ ├── ShadowPropTypesIOS.js │ │ │ ├── View.js │ │ │ ├── View.js.flow │ │ │ ├── ViewAccessibility.js │ │ │ ├── ViewPropTypes.js │ │ │ └── ViewStylePropTypes.js │ │ ├── ViewPager/ │ │ │ ├── ViewPagerAndroid.android.js │ │ │ ├── ViewPagerAndroid.ios.js │ │ │ └── ViewPagerAndroid.macos.js │ │ └── WebView/ │ │ ├── WebView.android.js │ │ └── WebView.macos.js │ ├── Core/ │ │ ├── Devtools/ │ │ │ ├── __tests__/ │ │ │ │ └── parseErrorStack-test.js │ │ │ ├── getDevServer.js │ │ │ ├── openFileInEditor.js │ │ │ ├── parseErrorStack.js │ │ │ ├── setupDevtools.js │ │ │ └── symbolicateStackTrace.js │ │ ├── ExceptionsManager.js │ │ ├── InitializeCore.js │ │ ├── ReactNativeVersion.js │ │ ├── ReactNativeVersionCheck.js │ │ ├── Timers/ │ │ │ └── JSTimers.js │ │ ├── __mocks__/ │ │ │ └── ErrorUtils.js │ │ └── __tests__/ │ │ └── ReactNativeVersionCheck-test.js │ ├── EventEmitter/ │ │ ├── MissingNativeEventEmitterShim.js │ │ ├── NativeEventEmitter.js │ │ ├── RCTDeviceEventEmitter.js │ │ ├── RCTEventEmitter.js │ │ ├── RCTNativeAppEventEmitter.js │ │ └── __mocks__/ │ │ └── NativeEventEmitter.js │ ├── Experimental/ │ │ ├── Incremental.js │ │ ├── IncrementalExample.js │ │ ├── IncrementalGroup.js │ │ ├── IncrementalPresenter.js │ │ ├── SwipeableRow/ │ │ │ ├── SwipeableFlatList.js │ │ │ ├── SwipeableListView.js │ │ │ ├── SwipeableListViewDataSource.js │ │ │ ├── SwipeableQuickActionButton.js │ │ │ ├── SwipeableQuickActions.js │ │ │ └── SwipeableRow.js │ │ └── WindowedListView.js │ ├── Geolocation/ │ │ ├── Geolocation.js │ │ ├── RCTGeolocation.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTLocationObserver.h │ │ └── RCTLocationObserver.m │ ├── Image/ │ │ ├── AssetRegistry.js │ │ ├── AssetSourceResolver.js │ │ ├── Image.android.js │ │ ├── Image.macos.js │ │ ├── ImageBackground.js │ │ ├── ImageEditor.js │ │ ├── ImageResizeMode.js │ │ ├── ImageSource.js │ │ ├── ImageSourcePropType.js │ │ ├── ImageStore.js │ │ ├── ImageStylePropTypes.js │ │ ├── RCTGIFImageDecoder.h │ │ ├── RCTGIFImageDecoder.m │ │ ├── RCTImage.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTImageBlurUtils.h │ │ ├── RCTImageBlurUtils.m │ │ ├── RCTImageCache.h │ │ ├── RCTImageCache.m │ │ ├── RCTImageEditingManager.h │ │ ├── RCTImageEditingManager.m │ │ ├── RCTImageLoader.h │ │ ├── RCTImageLoader.m │ │ ├── RCTImageShadowView.h │ │ ├── RCTImageShadowView.m │ │ ├── RCTImageStoreManager.h │ │ ├── RCTImageStoreManager.m │ │ ├── RCTImageUtils.h │ │ ├── RCTImageUtils.m │ │ ├── RCTImageView.h │ │ ├── RCTImageView.m │ │ ├── RCTImageViewManager.h │ │ ├── RCTImageViewManager.m │ │ ├── RCTLocalAssetImageLoader.h │ │ ├── RCTLocalAssetImageLoader.m │ │ ├── RCTResizeMode.h │ │ ├── RCTResizeMode.m │ │ ├── RelativeImageStub.js │ │ ├── __tests__/ │ │ │ ├── __snapshots__/ │ │ │ │ └── assetRelativePathInSnapshot.js.snap │ │ │ ├── assetRelativePathInSnapshot.js │ │ │ └── resolveAssetSource-test.js │ │ ├── nativeImageSource.js │ │ └── resolveAssetSource.js │ ├── Inspector/ │ │ ├── BorderBox.js │ │ ├── BoxInspector.js │ │ ├── ElementBox.js │ │ ├── ElementProperties.js │ │ ├── Inspector.js │ │ ├── InspectorOverlay.js │ │ ├── InspectorPanel.js │ │ ├── NetworkOverlay.js │ │ ├── PerformanceOverlay.js │ │ ├── StyleInspector.js │ │ └── resolveBoxStyle.js │ ├── Interaction/ │ │ ├── Batchinator.js │ │ ├── BridgeSpyStallHandler.js │ │ ├── FrameRateLogger.js │ │ ├── InteractionManager.js │ │ ├── InteractionMixin.js │ │ ├── InteractionStallDebugger.js │ │ ├── JSEventLoopWatchdog.js │ │ ├── PanResponder.js │ │ ├── ReactPerfStallHandler.js │ │ ├── TaskQueue.js │ │ └── __tests__/ │ │ ├── Batchinator-test.js │ │ ├── InteractionManager-test.js │ │ ├── InteractionMixin-test.js │ │ └── TaskQueue-test.js │ ├── JSInspector/ │ │ ├── InspectorAgent.js │ │ ├── JSInspector.js │ │ └── NetworkAgent.js │ ├── LayoutAnimation/ │ │ └── LayoutAnimation.js │ ├── Linking/ │ │ └── Linking.js │ ├── LinkingIOS/ │ │ ├── RCTLinking.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTLinkingManager.h │ │ └── RCTLinkingManager.m │ ├── Lists/ │ │ ├── FillRateHelper.js │ │ ├── FlatList.js │ │ ├── ListView/ │ │ │ ├── ListView.js │ │ │ ├── ListViewDataSource.js │ │ │ └── __mocks__/ │ │ │ └── ListViewMock.js │ │ ├── MetroListView.js │ │ ├── SectionList.js │ │ ├── ViewabilityHelper.js │ │ ├── VirtualizeUtils.js │ │ ├── VirtualizedList.js │ │ ├── VirtualizedSectionList.js │ │ ├── __flowtests__/ │ │ │ ├── FlatList-flowtest.js │ │ │ └── SectionList-flowtest.js │ │ └── __tests__/ │ │ ├── FillRateHelper-test.js │ │ ├── FlatList-test.js │ │ ├── SectionList-test.js │ │ ├── ViewabilityHelper-test.js │ │ ├── VirtualizeUtils-test.js │ │ ├── VirtualizedList-test.js │ │ └── __snapshots__/ │ │ ├── FlatList-test.js.snap │ │ ├── SectionList-test.js.snap │ │ ├── VirtualizeUtils-test.js.snap │ │ └── VirtualizedList-test.js.snap │ ├── Menu/ │ │ └── MenuManager.js │ ├── Modal/ │ │ └── Modal.js │ ├── NativeAnimation/ │ │ ├── Drivers/ │ │ │ ├── RCTAnimationDriver.h │ │ │ ├── RCTDecayAnimation.h │ │ │ ├── RCTDecayAnimation.m │ │ │ ├── RCTEventAnimation.h │ │ │ ├── RCTEventAnimation.m │ │ │ ├── RCTFrameAnimation.h │ │ │ ├── RCTFrameAnimation.m │ │ │ ├── RCTSpringAnimation.h │ │ │ └── RCTSpringAnimation.m │ │ ├── Nodes/ │ │ │ ├── RCTAdditionAnimatedNode.h │ │ │ ├── RCTAdditionAnimatedNode.m │ │ │ ├── RCTAnimatedNode.h │ │ │ ├── RCTAnimatedNode.m │ │ │ ├── RCTDiffClampAnimatedNode.h │ │ │ ├── RCTDiffClampAnimatedNode.m │ │ │ ├── RCTDivisionAnimatedNode.h │ │ │ ├── RCTDivisionAnimatedNode.m │ │ │ ├── RCTInterpolationAnimatedNode.h │ │ │ ├── RCTInterpolationAnimatedNode.m │ │ │ ├── RCTModuloAnimatedNode.h │ │ │ ├── RCTModuloAnimatedNode.m │ │ │ ├── RCTMultiplicationAnimatedNode.h │ │ │ ├── RCTMultiplicationAnimatedNode.m │ │ │ ├── RCTPropsAnimatedNode.h │ │ │ ├── RCTPropsAnimatedNode.m │ │ │ ├── RCTStyleAnimatedNode.h │ │ │ ├── RCTStyleAnimatedNode.m │ │ │ ├── RCTTransformAnimatedNode.h │ │ │ ├── RCTTransformAnimatedNode.m │ │ │ ├── RCTValueAnimatedNode.h │ │ │ └── RCTValueAnimatedNode.m │ │ ├── RCTAnimation.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTAnimationUtils.h │ │ ├── RCTAnimationUtils.m │ │ ├── RCTNativeAnimatedModule.h │ │ ├── RCTNativeAnimatedModule.m │ │ ├── RCTNativeAnimatedNodesManager.h │ │ └── RCTNativeAnimatedNodesManager.m │ ├── NavigationExperimental/ │ │ └── NavigationReducer.js │ ├── Network/ │ │ ├── FormData.js │ │ ├── NetInfo.js │ │ ├── RCTDataRequestHandler.h │ │ ├── RCTDataRequestHandler.m │ │ ├── RCTFileRequestHandler.h │ │ ├── RCTFileRequestHandler.m │ │ ├── RCTHTTPRequestHandler.h │ │ ├── RCTHTTPRequestHandler.mm │ │ ├── RCTNetInfo.h │ │ ├── RCTNetInfo.m │ │ ├── RCTNetwork.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTNetworkTask.h │ │ ├── RCTNetworkTask.m │ │ ├── RCTNetworking.android.js │ │ ├── RCTNetworking.h │ │ ├── RCTNetworking.ios.js │ │ ├── RCTNetworking.macos.js │ │ ├── RCTNetworking.mm │ │ ├── XHRInterceptor.js │ │ ├── XMLHttpRequest.js │ │ ├── __tests__/ │ │ │ ├── FormData-test.js │ │ │ └── XMLHttpRequest-test.js │ │ ├── convertRequestBody.js │ │ └── fetch.js │ ├── Performance/ │ │ ├── QuickPerformanceLogger.js │ │ ├── SamplingProfiler.js │ │ └── Systrace.js │ ├── PermissionsAndroid/ │ │ └── PermissionsAndroid.js │ ├── Picker/ │ │ └── PickerIOS.macos.js │ ├── Promise.js │ ├── PushNotificationIOS/ │ │ ├── PushNotificationIOS.js │ │ ├── RCTPushNotification.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTPushNotificationManager.h │ │ └── RCTPushNotificationManager.m │ ├── RCTTest/ │ │ ├── FBSnapshotTestCase/ │ │ │ ├── FBSnapshotTestCase.h │ │ │ ├── FBSnapshotTestCase.m │ │ │ ├── FBSnapshotTestController.h │ │ │ ├── FBSnapshotTestController.m │ │ │ ├── UIImage+Compare.h │ │ │ ├── UIImage+Compare.m │ │ │ ├── UIImage+Diff.h │ │ │ └── UIImage+Diff.m │ │ ├── RCTSnapshotManager.h │ │ ├── RCTSnapshotManager.m │ │ ├── RCTTest.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTTestModule.h │ │ ├── RCTTestModule.m │ │ ├── RCTTestRunner.h │ │ ├── RCTTestRunner.m │ │ ├── SnapshotViewIOS.android.js │ │ ├── SnapshotViewIOS.ios.js │ │ └── SnapshotViewIOS.macos.js │ ├── ReactNative/ │ │ ├── AppContainer.js │ │ ├── AppRegistry.js │ │ ├── I18nManager.js │ │ ├── ReactNativeFeatureFlags.js │ │ ├── UIManager.js │ │ ├── UIManagerStatTracker.js │ │ ├── YellowBox.js │ │ ├── queryLayoutByID.js │ │ ├── renderApplication.js │ │ ├── requireNativeComponent.js │ │ └── verifyPropTypes.js │ ├── Renderer/ │ │ ├── REVISION │ │ ├── ReactNativeRenderer-dev.js │ │ ├── ReactNativeRenderer-prod.js │ │ └── shims/ │ │ ├── NativeMethodsMixin.js │ │ ├── ReactDebugTool.js │ │ ├── ReactFeatureFlags.js │ │ ├── ReactGlobalSharedState.js │ │ ├── ReactNative.js │ │ ├── ReactNativeBridgeEventPlugin.js │ │ ├── ReactNativeComponentTree.js │ │ ├── ReactNativePropRegistry.js │ │ ├── ReactNativeTypes.js │ │ ├── ReactPerf.js │ │ ├── ReactTypes.js │ │ ├── TouchHistoryMath.js │ │ ├── createReactNativeComponentClass.js │ │ └── takeSnapshot.js │ ├── Sample/ │ │ ├── Sample.android.js │ │ ├── Sample.h │ │ ├── Sample.ios.js │ │ ├── Sample.m │ │ ├── Sample.xcodeproj/ │ │ │ └── project.pbxproj │ │ └── package.json │ ├── Settings/ │ │ ├── RCTSettings.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTSettingsManager.h │ │ ├── RCTSettingsManager.m │ │ ├── Settings.android.js │ │ ├── Settings.ios.js │ │ └── Settings.macos.js │ ├── Share/ │ │ └── Share.js │ ├── Storage/ │ │ └── AsyncStorage.js │ ├── StyleSheet/ │ │ ├── ColorPropType.js │ │ ├── EdgeInsetsPropType.js │ │ ├── LayoutPropTypes.js │ │ ├── PointPropType.js │ │ ├── StyleSheet.js │ │ ├── StyleSheetPropType.js │ │ ├── StyleSheetTypes.js │ │ ├── StyleSheetValidation.js │ │ ├── TransformPropTypes.js │ │ ├── __tests__/ │ │ │ ├── __snapshots__/ │ │ │ │ └── processTransform-test.js.snap │ │ │ ├── flattenStyle-test.js │ │ │ ├── normalizeColor-test.js │ │ │ ├── processColor-test.js │ │ │ ├── processTransform-test.js │ │ │ └── setNormalizedColorAlpha-test.js │ │ ├── flattenStyle.js │ │ ├── normalizeColor.js │ │ ├── processColor.js │ │ ├── processTransform.js │ │ └── setNormalizedColorAlpha.js │ ├── SurfaceBackedComponent/ │ │ ├── RCTSurfaceBackedComponent.h │ │ ├── RCTSurfaceBackedComponent.mm │ │ ├── RCTSurfaceBackedComponentState.h │ │ └── RCTSurfaceBackedComponentState.mm │ ├── SurfaceHostingComponent/ │ │ ├── RCTSurfaceHostingComponent+Internal.h │ │ ├── RCTSurfaceHostingComponent.h │ │ ├── RCTSurfaceHostingComponent.mm │ │ ├── RCTSurfaceHostingComponentController.h │ │ ├── RCTSurfaceHostingComponentController.mm │ │ ├── RCTSurfaceHostingComponentOptions.h │ │ ├── RCTSurfaceHostingComponentState.h │ │ └── RCTSurfaceHostingComponentState.mm │ ├── Text/ │ │ ├── RCTConvert+Text.h │ │ ├── RCTConvert+Text.m │ │ ├── RCTFontAttributes.h │ │ ├── RCTFontAttributes.m │ │ ├── RCTFontAttributesDelegate.h │ │ ├── RCTRawTextShadowView.h │ │ ├── RCTRawTextShadowView.m │ │ ├── RCTRawTextViewManager.h │ │ ├── RCTRawTextViewManager.m │ │ ├── RCTSecureTextField.h │ │ ├── RCTSecureTextField.m │ │ ├── RCTText.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTTextField.h │ │ ├── RCTTextField.m │ │ ├── RCTTextFieldManager.m │ │ ├── RCTTextView.m │ │ ├── RCTTextViewManager.m │ │ ├── Text/ │ │ │ ├── RCTTextShadowView.h │ │ │ ├── RCTTextShadowView.m │ │ │ ├── RCTTextView.h │ │ │ ├── RCTTextView.m │ │ │ ├── RCTTextViewManager.h │ │ │ └── RCTTextViewManager.m │ │ ├── Text.js │ │ ├── TextInput/ │ │ │ ├── Multiline/ │ │ │ │ ├── RCTMultilineTextInputShadowView.h │ │ │ │ ├── RCTMultilineTextInputShadowView.m │ │ │ │ ├── RCTMultilineTextInputView.h │ │ │ │ ├── RCTMultilineTextInputView.m │ │ │ │ ├── RCTMultilineTextInputViewManager.h │ │ │ │ ├── RCTMultilineTextInputViewManager.m │ │ │ │ ├── RCTUITextView.h │ │ │ │ └── RCTUITextView.m │ │ │ ├── RCTBackedTextInputDelegate.h │ │ │ ├── RCTBackedTextInputDelegateAdapter.h │ │ │ ├── RCTBackedTextInputDelegateAdapter.m │ │ │ ├── RCTBackedTextInputViewProtocol.h │ │ │ ├── RCTBaseTextInputView.h │ │ │ ├── RCTBaseTextInputView.m │ │ │ ├── RCTBaseTextInputViewManager.h │ │ │ ├── RCTBaseTextInputViewManager.m │ │ │ ├── RCTTextSelection.h │ │ │ ├── RCTTextSelection.m │ │ │ └── Singleline/ │ │ │ ├── RCTSinglelineTextInputShadowView.h │ │ │ ├── RCTSinglelineTextInputShadowView.m │ │ │ ├── RCTSinglelineTextInputView.h │ │ │ ├── RCTSinglelineTextInputView.m │ │ │ ├── RCTSinglelineTextInputViewManager.h │ │ │ ├── RCTSinglelineTextInputViewManager.m │ │ │ ├── RCTUITextField.h │ │ │ └── RCTUITextField.m │ │ ├── TextInputLegacy/ │ │ │ ├── RCTMultilineTextInputView.h │ │ │ ├── RCTMultilineTextInputView.m │ │ │ ├── RCTSecureTextField.h │ │ │ ├── RCTSecureTextField.m │ │ │ ├── RCTShadowTextField.h │ │ │ ├── RCTShadowTextField.m │ │ │ ├── RCTShadowTextView.h │ │ │ ├── RCTShadowTextView.m │ │ │ ├── RCTTextField.h │ │ │ ├── RCTTextField.m │ │ │ ├── RCTTextFieldManager.h │ │ │ ├── RCTTextFieldManager.m │ │ │ ├── RCTTextManager.h │ │ │ ├── RCTTextManager.m │ │ │ ├── RCTTextSelection.h │ │ │ ├── RCTTextSelection.m │ │ │ ├── RCTTextViewManager.h │ │ │ ├── RCTTextViewManager.m │ │ │ ├── RCTUITextView.h │ │ │ └── RCTUITextView.m │ │ ├── TextProps.js │ │ ├── TextStylePropTypes.js │ │ └── TextUpdateTest.js │ ├── Types/ │ │ └── CoreEventTypes.js │ ├── Utilities/ │ │ ├── Appearance.js │ │ ├── BackAndroid.js │ │ ├── BackHandler.android.js │ │ ├── BackHandler.ios.js │ │ ├── BackHandler.macos.js │ │ ├── CSSVarConfig.js │ │ ├── DebugEnvironment.js │ │ ├── DeviceInfo.js │ │ ├── Dimensions.js │ │ ├── HMRClient.js │ │ ├── HMRLoadingView.android.js │ │ ├── HMRLoadingView.ios.js │ │ ├── HMRLoadingView.macos.js │ │ ├── HeapCapture.js │ │ ├── MatrixMath.js │ │ ├── PerformanceLogger.js │ │ ├── PixelRatio.js │ │ ├── Platform.android.js │ │ ├── Platform.ios.js │ │ ├── Platform.macos.js │ │ ├── PlatformOS.android.js │ │ ├── PlatformOS.ios.js │ │ ├── RCTLog.js │ │ ├── SceneTracker.js │ │ ├── __mocks__/ │ │ │ ├── BackHandler.js │ │ │ └── PixelRatio.js │ │ ├── __tests__/ │ │ │ ├── MatrixMath-test.js │ │ │ ├── Platform-test.js │ │ │ ├── SceneTracker-test.js │ │ │ ├── buildStyleInterpolator-test.js │ │ │ ├── deepFreezeAndThrowOnMutationInDev-test.js │ │ │ ├── groupByEveryN-test.js │ │ │ ├── mapWithSeparator-test.js │ │ │ ├── truncate-test.js │ │ │ └── utf8-test.js │ │ ├── asyncRequire.js │ │ ├── binaryToBase64.js │ │ ├── buildStyleInterpolator.js │ │ ├── clamp.js │ │ ├── createStrictShapeTypeChecker.js │ │ ├── deepFreezeAndThrowOnMutationInDev.js │ │ ├── defineLazyObjectProperty.js │ │ ├── deprecatedPropType.js │ │ ├── differ/ │ │ │ ├── __tests__/ │ │ │ │ └── deepDiffer-test.js │ │ │ ├── deepDiffer.js │ │ │ ├── insetsDiffer.js │ │ │ ├── matricesDiffer.js │ │ │ ├── pointsDiffer.js │ │ │ └── sizesDiffer.js │ │ ├── dismissKeyboard.js │ │ ├── groupByEveryN.js │ │ ├── infoLog.js │ │ ├── logError.js │ │ ├── mapWithSeparator.js │ │ ├── mergeFast.js │ │ ├── mergeIntoFast.js │ │ ├── stringifySafe.js │ │ ├── truncate.js │ │ └── utf8.js │ ├── Vibration/ │ │ ├── RCTVibration.h │ │ ├── RCTVibration.m │ │ ├── RCTVibration.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── Vibration.js │ │ ├── VibrationIOS.android.js │ │ ├── VibrationIOS.ios.js │ │ └── VibrationIOS.macos.js │ ├── WebSocket/ │ │ ├── RCTReconnectingWebSocket.h │ │ ├── RCTReconnectingWebSocket.m │ │ ├── RCTSRWebSocket.h │ │ ├── RCTSRWebSocket.m │ │ ├── RCTWebSocket.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── RCTWebSocketExecutor.h │ │ ├── RCTWebSocketExecutor.m │ │ ├── RCTWebSocketModule.h │ │ ├── RCTWebSocketModule.m │ │ ├── WebSocket.js │ │ ├── WebSocketEvent.js │ │ ├── WebSocketInterceptor.js │ │ ├── __mocks__/ │ │ │ └── event-target-shim.js │ │ └── __tests__/ │ │ └── WebSocket-test.js │ ├── Wrapper/ │ │ ├── Example/ │ │ │ ├── RCTWrapperExampleView.h │ │ │ ├── RCTWrapperExampleView.m │ │ │ ├── RCTWrapperExampleViewController.h │ │ │ ├── RCTWrapperExampleViewController.m │ │ │ ├── RCTWrapperReactRootViewController.h │ │ │ ├── RCTWrapperReactRootViewController.m │ │ │ ├── RCTWrapperReactRootViewManager.h │ │ │ └── RCTWrapperReactRootViewManager.m │ │ ├── RCTWrapper.h │ │ ├── RCTWrapperShadowView.h │ │ ├── RCTWrapperShadowView.m │ │ ├── RCTWrapperView.h │ │ ├── RCTWrapperView.m │ │ ├── RCTWrapperViewControllerHostingView.h │ │ ├── RCTWrapperViewControllerHostingView.m │ │ ├── RCTWrapperViewManager.h │ │ └── RCTWrapperViewManager.m │ ├── fishhook/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── fishhook.c │ │ └── fishhook.h │ ├── polyfills/ │ │ ├── Array.es6.js │ │ ├── Array.prototype.es6.js │ │ ├── Number.es6.js │ │ ├── Object.es6.js │ │ ├── Object.es7.js │ │ ├── String.prototype.es6.js │ │ ├── __tests__/ │ │ │ └── Object.es7-test.js │ │ ├── babelHelpers.js │ │ ├── console.js │ │ └── error-guard.js │ ├── promiseRejectionIsError.js │ ├── react-native/ │ │ ├── React.js │ │ ├── react-native-implementation.js │ │ └── react-native-interface.js │ └── vendor/ │ ├── core/ │ │ ├── ErrorUtils.js │ │ ├── Map.js │ │ ├── Set.js │ │ ├── _shouldPolyfillES6Collection.js │ │ ├── getObjectValues.js │ │ ├── guid.js │ │ ├── isEmpty.js │ │ ├── merge.js │ │ ├── mergeHelpers.js │ │ ├── mergeInto.js │ │ └── toIterator.js │ ├── document/ │ │ └── selection/ │ │ └── DocumentSelectionState.js │ ├── emitter/ │ │ ├── EmitterSubscription.js │ │ ├── EventEmitter.js │ │ ├── EventEmitterWithHolding.js │ │ ├── EventHolder.js │ │ ├── EventSubscription.js │ │ ├── EventSubscriptionVendor.js │ │ ├── EventValidator.js │ │ └── mixInEventEmitter.js │ ├── react/ │ │ └── vendor/ │ │ └── core/ │ │ └── ExecutionEnvironment.macos.js │ └── tinycolor/ │ └── tinycolor.js ├── README.md ├── RNTester/ │ ├── .eslintrc │ ├── README.md │ ├── RNTester/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj/ │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── NavBarButtonPlus.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── story-background.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── tabnav_list.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── tabnav_notification.imageset/ │ │ │ │ └── Contents.json │ │ │ └── tabnav_settings.imageset/ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── NativeExampleViews/ │ │ │ ├── FlexibleSizeExampleView.h │ │ │ ├── FlexibleSizeExampleView.m │ │ │ ├── UpdatePropertiesExampleView.h │ │ │ └── UpdatePropertiesExampleView.m │ │ ├── RNTesterBundle/ │ │ │ ├── Info.plist │ │ │ └── OtherImages.xcassets/ │ │ │ ├── Contents.json │ │ │ └── ImageInAssetCatalog.imageset/ │ │ │ └── Contents.json │ │ └── main.m │ ├── RNTester-tvOS/ │ │ └── Info.plist │ ├── RNTester.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── RNTester-tvOS.xcscheme │ │ └── RNTester.xcscheme │ ├── RNTesterIntegrationTests/ │ │ ├── Info.plist │ │ ├── RCTLoggingTests.m │ │ ├── RCTRootViewIntegrationTests.m │ │ ├── RCTUIManagerScenarioTests.m │ │ ├── RNTesterIntegrationTests.m │ │ ├── RNTesterSnapshotTests.m │ │ └── RNTesterTestModule.m │ ├── RNTesterLegacy.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── RNTester-tvOS.xcscheme │ │ └── RNTester.xcscheme │ ├── RNTesterUnitTests/ │ │ ├── Info.plist │ │ ├── OCMock/ │ │ │ ├── NSNotificationCenter+OCMAdditions.h │ │ │ ├── OCMArg.h │ │ │ ├── OCMConstraint.h │ │ │ ├── OCMLocation.h │ │ │ ├── OCMMacroState.h │ │ │ ├── OCMRecorder.h │ │ │ ├── OCMStubRecorder.h │ │ │ ├── OCMock.h │ │ │ └── OCMockObject.h │ │ ├── RCTAllocationTests.m │ │ ├── RCTAnimationUtilsTests.m │ │ ├── RCTBridgeTests.m │ │ ├── RCTBundleURLProviderTests.m │ │ ├── RCTComponentPropsTests.m │ │ ├── RCTConvert_NSURLTests.m │ │ ├── RCTConvert_YGValueTests.m │ │ ├── RCTDevMenuTests.m │ │ ├── RCTEventDispatcherTests.m │ │ ├── RCTFontTests.m │ │ ├── RCTGzipTests.m │ │ ├── RCTImageLoaderHelpers.h │ │ ├── RCTImageLoaderHelpers.m │ │ ├── RCTImageLoaderTests.m │ │ ├── RCTImageUtilTests.m │ │ ├── RCTJSCExecutorTests.m │ │ ├── RCTJSONTests.m │ │ ├── RCTMethodArgumentTests.m │ │ ├── RCTModuleInitNotificationRaceTests.m │ │ ├── RCTModuleInitTests.m │ │ ├── RCTModuleMethodTests.mm │ │ ├── RCTMultipartStreamReaderTests.m │ │ ├── RCTNativeAnimatedNodesManagerTests.m │ │ ├── RCTShadowViewTests.m │ │ ├── RCTUIManagerTests.m │ │ ├── RCTURLUtilsTests.m │ │ ├── RCTUnicodeDecodeTests.m │ │ ├── RNTesterUnitTestsBundle.js │ │ └── libOCMock.a │ ├── android/ │ │ └── app/ │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── react.gradle │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── facebook/ │ │ │ └── react/ │ │ │ └── uiapp/ │ │ │ ├── RNTesterActivity.java │ │ │ └── RNTesterApplication.java │ │ └── res/ │ │ ├── layout/ │ │ │ └── activity_main.xml │ │ └── values/ │ │ ├── strings.xml │ │ └── styles.xml │ └── js/ │ ├── ARTExample.js │ ├── AccessibilityAndroidExample.android.js │ ├── AccessibilityIOSExample.js │ ├── ActionSheetIOSExample.js │ ├── ActivityIndicatorExample.js │ ├── AlertExample.js │ ├── AlertIOSExample.js │ ├── AlertOSXExample.js │ ├── AnimatedExample.js │ ├── AnimatedGratuitousApp/ │ │ ├── AnExApp.js │ │ ├── AnExBobble.js │ │ ├── AnExChained.js │ │ ├── AnExScroll.js │ │ ├── AnExSet.js │ │ ├── AnExSlides.md │ │ └── AnExTilt.js │ ├── AppStateExample.js │ ├── AppearanceContext.js │ ├── AppearanceExample.macos.js │ ├── AssetScaledImageExample.js │ ├── AsyncStorageExample.js │ ├── BorderExample.js │ ├── BoxShadowExample.js │ ├── ButtonExample.js │ ├── CameraRollExample.js │ ├── CameraRollView.js │ ├── CheckBoxExample.js │ ├── ClipboardExample.js │ ├── DatePickerAndroidExample.js │ ├── DatePickerIOSExample.js │ ├── DimensionsExample.js │ ├── DragnDropExample.macos.js │ ├── ExampleTypes.js │ ├── FlatListExample.js │ ├── GeolocationExample.js │ ├── ImageCapInsetsExample.js │ ├── ImageEditingExample.js │ ├── ImageExample.js │ ├── KeyboardAvoidingViewExample.js │ ├── LayoutAnimationExample.js │ ├── LayoutEventsExample.js │ ├── LayoutExample.js │ ├── LinkingExample.js │ ├── ListExampleShared.js │ ├── ListViewExample.js │ ├── ListViewGridLayoutExample.js │ ├── ListViewPagingExample.js │ ├── MaskedViewExample.js │ ├── MenuExample.macos.js │ ├── ModalExample.js │ ├── MultiColumnExample.js │ ├── NativeAnimationsExample.js │ ├── NavigatorIOSBarStyleExample.js │ ├── NavigatorIOSColorsExample.js │ ├── NavigatorIOSExample.js │ ├── NetInfoExample.js │ ├── OrientationChangeExample.js │ ├── PanResponderExample.js │ ├── PermissionsExampleAndroid.android.js │ ├── PickerExample.js │ ├── PickerIOSExample.js │ ├── PointerEventsExample.js │ ├── ProgressBarAndroidExample.android.js │ ├── ProgressViewIOSExample.js │ ├── PushNotificationIOSExample.js │ ├── RCTRootViewIOSExample.js │ ├── RNTesterActions.js │ ├── RNTesterApp.android.js │ ├── RNTesterApp.ios.js │ ├── RNTesterApp.macos.js │ ├── RNTesterBlock.js │ ├── RNTesterButton.js │ ├── RNTesterExampleContainer.js │ ├── RNTesterExampleList.js │ ├── RNTesterList.android.js │ ├── RNTesterList.ios.js │ ├── RNTesterList.macos.js │ ├── RNTesterNavigationReducer.js │ ├── RNTesterPage.js │ ├── RNTesterSettingSwitchRow.js │ ├── RNTesterStatePersister.js │ ├── RNTesterTitle.js │ ├── RTLExample.js │ ├── RefreshControlExample.js │ ├── RootViewSizeFlexibilityExampleApp.js │ ├── SafeAreaViewExample.js │ ├── ScrollViewExample.js │ ├── ScrollViewSimpleExample.js │ ├── SectionListExample.js │ ├── SegmentedControlIOSExample.js │ ├── SetPropertiesExampleApp.js │ ├── ShareExample.js │ ├── SliderExample.js │ ├── SnapshotExample.js │ ├── StatusBarExample.js │ ├── SwipeableFlatListExample.js │ ├── SwipeableListViewExample.js │ ├── SwitchExample.js │ ├── TVEventHandlerExample.js │ ├── TabBarIOSBarStyleExample.js │ ├── TabBarIOSExample.js │ ├── TextExample.android.js │ ├── TextExample.ios.js │ ├── TextExample.macos.js │ ├── TextInputExample.android.js │ ├── TextInputExample.ios.js │ ├── TextInputExample.macos.js │ ├── TimePickerAndroidExample.js │ ├── TimerExample.js │ ├── ToastAndroidExample.android.js │ ├── ToolbarAndroidExample.android.js │ ├── TouchableExample.js │ ├── TransformExample.js │ ├── TransparentHitTestExample.js │ ├── URIActionMap.js │ ├── VibrationExample.js │ ├── VibrationIOSExample.js │ ├── ViewExample.js │ ├── ViewPagerAndroidExample.android.js │ ├── WebSocketExample.js │ ├── WebViewExample.js │ ├── XHRExample.js │ ├── XHRExampleBinaryUpload.js │ ├── XHRExampleCookies.js │ ├── XHRExampleDownload.js │ ├── XHRExampleFetch.js │ ├── XHRExampleFormData.js │ ├── XHRExampleHeaders.js │ ├── XHRExampleOnTimeOut.js │ ├── createExamplePage.js │ ├── helloworld.html │ ├── http_test_server.js │ ├── messagingtest.html │ └── websocket_test_server.js ├── React/ │ ├── Base/ │ │ ├── RCTAssert.h │ │ ├── RCTAssert.m │ │ ├── RCTBatchedBridge.mm │ │ ├── RCTBridge+Private.h │ │ ├── RCTBridge.h │ │ ├── RCTBridge.m │ │ ├── RCTBridgeDelegate.h │ │ ├── RCTBridgeMethod.h │ │ ├── RCTBridgeModule.h │ │ ├── RCTBundleURLProvider.h │ │ ├── RCTBundleURLProvider.m │ │ ├── RCTConvert.h │ │ ├── RCTConvert.m │ │ ├── RCTCxxConvert.h │ │ ├── RCTCxxConvert.m │ │ ├── RCTDefines.h │ │ ├── RCTDisplayLink.h │ │ ├── RCTDisplayLink.m │ │ ├── RCTErrorCustomizer.h │ │ ├── RCTErrorInfo.h │ │ ├── RCTErrorInfo.m │ │ ├── RCTEventDispatcher.h │ │ ├── RCTEventDispatcher.m │ │ ├── RCTFrameUpdate.h │ │ ├── RCTFrameUpdate.m │ │ ├── RCTImageSource.h │ │ ├── RCTImageSource.m │ │ ├── RCTInvalidating.h │ │ ├── RCTJSCErrorHandling.h │ │ ├── RCTJSCErrorHandling.mm │ │ ├── RCTJSStackFrame.h │ │ ├── RCTJSStackFrame.m │ │ ├── RCTJavaScriptExecutor.h │ │ ├── RCTJavaScriptLoader.h │ │ ├── RCTJavaScriptLoader.mm │ │ ├── RCTKeyCommands.h │ │ ├── RCTKeyCommands.m │ │ ├── RCTLog.h │ │ ├── RCTLog.mm │ │ ├── RCTManagedPointer.h │ │ ├── RCTManagedPointer.mm │ │ ├── RCTModuleData.h │ │ ├── RCTModuleData.mm │ │ ├── RCTModuleMethod.h │ │ ├── RCTModuleMethod.m │ │ ├── RCTModuleMethod.mm │ │ ├── RCTMultipartDataTask.h │ │ ├── RCTMultipartDataTask.m │ │ ├── RCTMultipartStreamReader.h │ │ ├── RCTMultipartStreamReader.m │ │ ├── RCTNullability.h │ │ ├── RCTParserUtils.h │ │ ├── RCTParserUtils.m │ │ ├── RCTPerformanceLogger.h │ │ ├── RCTPerformanceLogger.m │ │ ├── RCTPlatform.h │ │ ├── RCTPlatform.m │ │ ├── RCTReloadCommand.h │ │ ├── RCTReloadCommand.m │ │ ├── RCTRootContentView.h │ │ ├── RCTRootContentView.m │ │ ├── RCTRootView.h │ │ ├── RCTRootView.m │ │ ├── RCTRootViewDelegate.h │ │ ├── RCTRootViewInternal.h │ │ ├── RCTTVRemoteHandler.h │ │ ├── RCTTVRemoteHandler.m │ │ ├── RCTTouchEvent.h │ │ ├── RCTTouchEvent.m │ │ ├── RCTTouchHandler.h │ │ ├── RCTTouchHandler.m │ │ ├── RCTURLRequestDelegate.h │ │ ├── RCTURLRequestHandler.h │ │ ├── RCTUtils.h │ │ ├── RCTUtils.m │ │ ├── RCTVersion.h │ │ ├── Surface/ │ │ │ ├── RCTSurface.h │ │ │ ├── RCTSurface.mm │ │ │ ├── RCTSurfaceDelegate.h │ │ │ ├── RCTSurfaceRootShadowView.h │ │ │ ├── RCTSurfaceRootShadowView.m │ │ │ ├── RCTSurfaceRootShadowViewDelegate.h │ │ │ ├── RCTSurfaceRootView.h │ │ │ ├── RCTSurfaceRootView.mm │ │ │ ├── RCTSurfaceStage.h │ │ │ ├── RCTSurfaceStage.m │ │ │ ├── RCTSurfaceView+Internal.h │ │ │ ├── RCTSurfaceView.h │ │ │ ├── RCTSurfaceView.mm │ │ │ └── SurfaceHostingView/ │ │ │ ├── RCTSurfaceHostingView.h │ │ │ ├── RCTSurfaceHostingView.mm │ │ │ └── RCTSurfaceSizeMeasureMode.h │ │ ├── UIImageUtils.h │ │ └── UIImageUtils.m │ ├── CxxBridge/ │ │ ├── NSDataBigString.h │ │ ├── NSDataBigString.mm │ │ ├── RCTCxxBridge.mm │ │ ├── RCTCxxBridgeDelegate.h │ │ ├── RCTJSCHelpers.h │ │ ├── RCTJSCHelpers.mm │ │ ├── RCTMessageThread.h │ │ ├── RCTMessageThread.mm │ │ ├── RCTObjcExecutor.h │ │ └── RCTObjcExecutor.mm │ ├── CxxModule/ │ │ ├── DispatchMessageQueueThread.h │ │ ├── RCTCxxMethod.h │ │ ├── RCTCxxMethod.mm │ │ ├── RCTCxxModule.h │ │ ├── RCTCxxModule.mm │ │ ├── RCTCxxUtils.h │ │ ├── RCTCxxUtils.mm │ │ ├── RCTNativeModule.h │ │ └── RCTNativeModule.mm │ ├── CxxUtils/ │ │ ├── RCTFollyConvert.h │ │ └── RCTFollyConvert.mm │ ├── DevSupport/ │ │ ├── RCTDevLoadingView.h │ │ ├── RCTDevLoadingView.m │ │ ├── RCTDevMenu.h │ │ ├── RCTDevMenu.m │ │ ├── RCTInspectorDevServerHelper.h │ │ ├── RCTInspectorDevServerHelper.mm │ │ ├── RCTPackagerClient.h │ │ ├── RCTPackagerClient.m │ │ ├── RCTPackagerConnection.h │ │ └── RCTPackagerConnection.mm │ ├── Executors/ │ │ ├── RCTJSCExecutor.h │ │ ├── RCTJSCExecutor.mm │ │ ├── RCTWebViewExecutor.h │ │ └── RCTWebViewExecutor.m │ ├── Inspector/ │ │ ├── RCTInspector.h │ │ ├── RCTInspector.mm │ │ ├── RCTInspectorPackagerConnection.h │ │ └── RCTInspectorPackagerConnection.m │ ├── Modules/ │ │ ├── RCTAccessibilityManager.h │ │ ├── RCTAccessibilityManager.m │ │ ├── RCTAlertManager.h │ │ ├── RCTAlertManager.m │ │ ├── RCTAppState.h │ │ ├── RCTAppState.m │ │ ├── RCTAppearance.h │ │ ├── RCTAppearance.m │ │ ├── RCTAsyncLocalStorage.h │ │ ├── RCTAsyncLocalStorage.m │ │ ├── RCTClipboard.h │ │ ├── RCTClipboard.m │ │ ├── RCTCursorManager.h │ │ ├── RCTCursorManager.m │ │ ├── RCTDevSettings.h │ │ ├── RCTDevSettings.mm │ │ ├── RCTDeviceInfo.h │ │ ├── RCTDeviceInfo.m │ │ ├── RCTEventEmitter.h │ │ ├── RCTEventEmitter.m │ │ ├── RCTExceptionsManager.h │ │ ├── RCTExceptionsManager.m │ │ ├── RCTI18nManager.h │ │ ├── RCTI18nManager.m │ │ ├── RCTI18nUtil.h │ │ ├── RCTI18nUtil.m │ │ ├── RCTJSCSamplingProfiler.h │ │ ├── RCTJSCSamplingProfiler.m │ │ ├── RCTKeyboardObserver.h │ │ ├── RCTKeyboardObserver.m │ │ ├── RCTLayoutAnimation.h │ │ ├── RCTLayoutAnimation.m │ │ ├── RCTLayoutAnimationGroup.h │ │ ├── RCTLayoutAnimationGroup.m │ │ ├── RCTMenuManager.h │ │ ├── RCTMenuManager.m │ │ ├── RCTRedBox.h │ │ ├── RCTRedBox.m │ │ ├── RCTRedBoxExtraDataViewController.h │ │ ├── RCTRedBoxExtraDataViewController.m │ │ ├── RCTSourceCode.h │ │ ├── RCTSourceCode.m │ │ ├── RCTStatusBarManager.h │ │ ├── RCTStatusBarManager.m │ │ ├── RCTTVNavigationEventEmitter.h │ │ ├── RCTTVNavigationEventEmitter.m │ │ ├── RCTTiming.h │ │ ├── RCTTiming.m │ │ ├── RCTUIManager.h │ │ ├── RCTUIManager.m │ │ ├── RCTUIManagerObserverCoordinator.h │ │ ├── RCTUIManagerObserverCoordinator.mm │ │ ├── RCTUIManagerUtils.h │ │ └── RCTUIManagerUtils.m │ ├── Profiler/ │ │ ├── RCTFPSGraph.h │ │ ├── RCTFPSGraph.m │ │ ├── RCTJSCProfiler.m │ │ ├── RCTMacros.h │ │ ├── RCTPerfMonitor.m │ │ ├── RCTProfile.h │ │ ├── RCTProfile.m │ │ ├── RCTProfileTrampoline-arm.S │ │ ├── RCTProfileTrampoline-arm64.S │ │ ├── RCTProfileTrampoline-i386.S │ │ └── RCTProfileTrampoline-x86_64.S │ ├── React.xcodeproj/ │ │ └── project.pbxproj │ ├── ReactLegacy.xcodeproj/ │ │ └── project.pbxproj │ ├── Views/ │ │ ├── NSView+NSViewAnimationWithBlocks.h │ │ ├── NSView+NSViewAnimationWithBlocks.m │ │ ├── NSView+Private.h │ │ ├── NSView+React.h │ │ ├── NSView+React.m │ │ ├── RCTActivityIndicatorView.h │ │ ├── RCTActivityIndicatorView.m │ │ ├── RCTActivityIndicatorViewManager.h │ │ ├── RCTActivityIndicatorViewManager.m │ │ ├── RCTAnimationType.h │ │ ├── RCTAutoInsetsProtocol.h │ │ ├── RCTBorderDrawing.h │ │ ├── RCTBorderDrawing.m │ │ ├── RCTBorderStyle.h │ │ ├── RCTButton.h │ │ ├── RCTButton.m │ │ ├── RCTButtonManager.h │ │ ├── RCTButtonManager.m │ │ ├── RCTComponent.h │ │ ├── RCTComponentData.h │ │ ├── RCTComponentData.m │ │ ├── RCTConvert+CoreLocation.h │ │ ├── RCTConvert+CoreLocation.m │ │ ├── RCTConvert+Transform.h │ │ ├── RCTConvert+Transform.m │ │ ├── RCTDatePicker.h │ │ ├── RCTDatePicker.m │ │ ├── RCTDatePickerManager.h │ │ ├── RCTDatePickerManager.m │ │ ├── RCTFont.h │ │ ├── RCTFont.mm │ │ ├── RCTMaskedView.h │ │ ├── RCTMaskedView.m │ │ ├── RCTMaskedViewManager.h │ │ ├── RCTMaskedViewManager.m │ │ ├── RCTModalHostView.h │ │ ├── RCTModalHostView.m │ │ ├── RCTModalHostViewController.h │ │ ├── RCTModalHostViewController.m │ │ ├── RCTModalHostViewManager.h │ │ ├── RCTModalHostViewManager.m │ │ ├── RCTModalManager.h │ │ ├── RCTModalManager.m │ │ ├── RCTNavItem.h │ │ ├── RCTNavItem.m │ │ ├── RCTNavItemManager.h │ │ ├── RCTNavItemManager.m │ │ ├── RCTNavigator.h │ │ ├── RCTNavigator.m │ │ ├── RCTNavigatorManager.h │ │ ├── RCTNavigatorManager.m │ │ ├── RCTPicker.h │ │ ├── RCTPicker.m │ │ ├── RCTPickerManager.h │ │ ├── RCTPickerManager.m │ │ ├── RCTPointerEvents.h │ │ ├── RCTProgressViewManager.h │ │ ├── RCTProgressViewManager.m │ │ ├── RCTRefreshControl.m │ │ ├── RCTRefreshControlManager.m │ │ ├── RCTRootShadowView.h │ │ ├── RCTRootShadowView.m │ │ ├── RCTSegmentedControl.h │ │ ├── RCTSegmentedControl.m │ │ ├── RCTSegmentedControlManager.h │ │ ├── RCTSegmentedControlManager.m │ │ ├── RCTShadowView+Internal.h │ │ ├── RCTShadowView+Internal.m │ │ ├── RCTShadowView+Layout.h │ │ ├── RCTShadowView+Layout.m │ │ ├── RCTShadowView.h │ │ ├── RCTShadowView.m │ │ ├── RCTSlider.h │ │ ├── RCTSlider.m │ │ ├── RCTSliderManager.h │ │ ├── RCTSliderManager.m │ │ ├── RCTSwitch.h │ │ ├── RCTSwitch.m │ │ ├── RCTSwitchManager.h │ │ ├── RCTSwitchManager.m │ │ ├── RCTTVView.h │ │ ├── RCTTVView.m │ │ ├── RCTTabBar.h │ │ ├── RCTTabBar.m │ │ ├── RCTTabBarItem.h │ │ ├── RCTTabBarItem.m │ │ ├── RCTTabBarItemManager.h │ │ ├── RCTTabBarItemManager.m │ │ ├── RCTTabBarManager.h │ │ ├── RCTTabBarManager.m │ │ ├── RCTTextDecorationLineType.h │ │ ├── RCTView.h │ │ ├── RCTView.m │ │ ├── RCTViewControllerProtocol.h │ │ ├── RCTViewManager.h │ │ ├── RCTViewManager.m │ │ ├── RCTWebView.h │ │ ├── RCTWebView.m │ │ ├── RCTWebViewManager.h │ │ ├── RCTWebViewManager.m │ │ ├── RCTWrapperViewController.h │ │ ├── RCTWrapperViewController.m │ │ ├── SafeAreaView/ │ │ │ ├── RCTSafeAreaShadowView.h │ │ │ ├── RCTSafeAreaShadowView.m │ │ │ ├── RCTSafeAreaView.h │ │ │ ├── RCTSafeAreaView.m │ │ │ ├── RCTSafeAreaViewLocalData.h │ │ │ ├── RCTSafeAreaViewLocalData.m │ │ │ ├── RCTSafeAreaViewManager.h │ │ │ └── RCTSafeAreaViewManager.m │ │ └── ScrollView/ │ │ ├── RCTScrollContentShadowView.h │ │ ├── RCTScrollContentShadowView.m │ │ ├── RCTScrollContentView.h │ │ ├── RCTScrollContentView.m │ │ ├── RCTScrollContentViewManager.h │ │ ├── RCTScrollContentViewManager.m │ │ ├── RCTScrollView.h │ │ ├── RCTScrollView.m │ │ ├── RCTScrollViewManager.h │ │ ├── RCTScrollViewManager.m │ │ └── RCTScrollableProtocol.h │ └── third-party.xcconfig ├── React.podspec ├── ReactAndroid/ │ ├── DEFS │ ├── DevExperience.md │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ ├── libs/ │ │ └── BUCK │ ├── release.gradle │ └── src/ │ ├── androidTest/ │ │ ├── assets/ │ │ │ └── BUCK │ │ ├── buck-runner/ │ │ │ └── BUCK │ │ ├── java/ │ │ │ └── com/ │ │ │ └── facebook/ │ │ │ └── react/ │ │ │ ├── testing/ │ │ │ │ ├── AbstractScrollViewTestCase.java │ │ │ │ ├── BUCK │ │ │ │ ├── FakeWebSocketModule.java │ │ │ │ ├── InstanceSpecForTestPackage.java │ │ │ │ ├── ReactAppInstrumentationTestCase.java │ │ │ │ ├── ReactAppTestActivity.java │ │ │ │ ├── ReactInstanceSpecForTest.java │ │ │ │ ├── ReactIntegrationTestCase.java │ │ │ │ ├── ReactSettingsForTests.java │ │ │ │ ├── ReactTestAppShell.java │ │ │ │ ├── ReactTestApplicationImpl.java │ │ │ │ ├── ReactTestFactory.java │ │ │ │ ├── ReactTestHelper.java │ │ │ │ ├── ScreenshotingFrameLayout.java │ │ │ │ ├── idledetection/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── IdleWaiter.java │ │ │ │ │ ├── ReactBridgeIdleSignaler.java │ │ │ │ │ └── ReactIdleDetectionUtil.java │ │ │ │ └── network/ │ │ │ │ ├── BUCK │ │ │ │ └── NetworkRecordingModuleMock.java │ │ │ └── tests/ │ │ │ ├── BUCK │ │ │ ├── CatalystMeasureLayoutTest.java │ │ │ ├── CatalystMultitouchHandlingTestCase.java │ │ │ ├── CatalystNativeJSToJavaParametersTestCase.java │ │ │ ├── CatalystNativeJavaToJSArgumentsTestCase.java │ │ │ ├── CatalystNativeJavaToJSReturnValuesTestCase.java │ │ │ ├── CatalystSubviewsClippingTestCase.java │ │ │ ├── CatalystTouchBubblingTestCase.java │ │ │ ├── CatalystUIManagerTestCase.java │ │ │ ├── DatePickerDialogTestCase.java │ │ │ ├── InitialPropsTestCase.java │ │ │ ├── JSLocaleTest.java │ │ │ ├── JSResponderTestCase.java │ │ │ ├── LayoutEventsTestCase.java │ │ │ ├── NativeIdTestCase.java │ │ │ ├── ProgressBarTestCase.java │ │ │ ├── ReactPickerTestCase.java │ │ │ ├── ReactRootViewTestCase.java │ │ │ ├── ReactSwipeRefreshLayoutTestCase.java │ │ │ ├── ShareTestCase.java │ │ │ ├── TestIdTestCase.java │ │ │ ├── TextInputTestCase.java │ │ │ ├── TimePickerDialogTestCase.java │ │ │ └── ViewRenderingTestCase.java │ │ └── js/ │ │ ├── Asserts.js │ │ ├── CatalystRootViewTestModule.js │ │ ├── DatePickerDialogTestModule.js │ │ ├── InitialPropsTestApp.js │ │ ├── JSResponderTestApp.js │ │ ├── LayoutEventsTestApp.js │ │ ├── MeasureLayoutTestModule.js │ │ ├── MultitouchHandlingTestAppModule.js │ │ ├── NativeIdTestModule.js │ │ ├── PickerAndroidTestModule.js │ │ ├── ProgressBarTestModule.js │ │ ├── ScrollViewTestModule.js │ │ ├── ShareTestModule.js │ │ ├── SubviewsClippingTestModule.js │ │ ├── SwipeRefreshLayoutTestModule.js │ │ ├── TestBundle.js │ │ ├── TestIdTestModule.js │ │ ├── TestJSLocaleModule.js │ │ ├── TestJSToJavaParametersModule.js │ │ ├── TestJavaToJSArgumentsModule.js │ │ ├── TestJavaToJSReturnValuesModule.js │ │ ├── TextInputTestModule.js │ │ ├── TimePickerDialogTestModule.js │ │ ├── TouchBubblingTestAppModule.js │ │ ├── UIManagerTestModule.js │ │ └── ViewRenderingTestModule.js │ ├── main/ │ │ ├── android_res/ │ │ │ └── com/ │ │ │ └── facebook/ │ │ │ └── catalyst/ │ │ │ └── appcompat/ │ │ │ └── BUCK │ │ ├── java/ │ │ │ └── com/ │ │ │ └── facebook/ │ │ │ ├── BUCK │ │ │ ├── debug/ │ │ │ │ ├── debugoverlay/ │ │ │ │ │ └── model/ │ │ │ │ │ ├── BUCK │ │ │ │ │ └── DebugOverlayTag.java │ │ │ │ ├── holder/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── NoopPrinter.java │ │ │ │ │ ├── Printer.java │ │ │ │ │ └── PrinterHolder.java │ │ │ │ └── tags/ │ │ │ │ ├── BUCK │ │ │ │ └── ReactDebugOverlayTags.java │ │ │ ├── jni/ │ │ │ │ ├── BUCK │ │ │ │ ├── Countable.java │ │ │ │ ├── CpuCapabilitiesJni.java │ │ │ │ ├── DestructorThread.java │ │ │ │ ├── HybridClassBase.java │ │ │ │ ├── HybridData.java │ │ │ │ ├── IteratorHelper.java │ │ │ │ ├── JniTerminateHandler.java │ │ │ │ ├── MapIteratorHelper.java │ │ │ │ ├── NativeRunnable.java │ │ │ │ ├── ThreadScopeSupport.java │ │ │ │ └── fbjni.pro │ │ │ ├── perftest/ │ │ │ │ └── BUCK │ │ │ ├── proguard/ │ │ │ │ └── annotations/ │ │ │ │ └── BUCK │ │ │ ├── react/ │ │ │ │ ├── BUCK │ │ │ │ ├── CompositeReactPackage.java │ │ │ │ ├── CoreModulesPackage.java │ │ │ │ ├── DebugCorePackage.java │ │ │ │ ├── EagerModuleProvider.java │ │ │ │ ├── HeadlessJsTaskService.java │ │ │ │ ├── LazyReactPackage.java │ │ │ │ ├── MemoryPressureRouter.java │ │ │ │ ├── NativeModuleRegistryBuilder.java │ │ │ │ ├── ReactActivity.java │ │ │ │ ├── ReactActivityDelegate.java │ │ │ │ ├── ReactApplication.java │ │ │ │ ├── ReactFragmentActivity.java │ │ │ │ ├── ReactInstanceManager.java │ │ │ │ ├── ReactInstanceManagerBuilder.java │ │ │ │ ├── ReactInstancePackage.java │ │ │ │ ├── ReactNativeHost.java │ │ │ │ ├── ReactPackage.java │ │ │ │ ├── ReactPackageLogger.java │ │ │ │ ├── ReactRootView.java │ │ │ │ ├── ViewManagerOnDemandReactPackage.java │ │ │ │ ├── animated/ │ │ │ │ │ ├── AdditionAnimatedNode.java │ │ │ │ │ ├── AnimatedNode.java │ │ │ │ │ ├── AnimatedNodeValueListener.java │ │ │ │ │ ├── AnimationDriver.java │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── DecayAnimation.java │ │ │ │ │ ├── DiffClampAnimatedNode.java │ │ │ │ │ ├── DivisionAnimatedNode.java │ │ │ │ │ ├── EventAnimationDriver.java │ │ │ │ │ ├── FrameBasedAnimationDriver.java │ │ │ │ │ ├── InterpolationAnimatedNode.java │ │ │ │ │ ├── ModulusAnimatedNode.java │ │ │ │ │ ├── MultiplicationAnimatedNode.java │ │ │ │ │ ├── NativeAnimatedModule.java │ │ │ │ │ ├── NativeAnimatedNodesManager.java │ │ │ │ │ ├── PropsAnimatedNode.java │ │ │ │ │ ├── SpringAnimation.java │ │ │ │ │ ├── StyleAnimatedNode.java │ │ │ │ │ ├── TransformAnimatedNode.java │ │ │ │ │ └── ValueAnimatedNode.java │ │ │ │ ├── animation/ │ │ │ │ │ ├── AbstractFloatPairPropertyUpdater.java │ │ │ │ │ ├── AbstractSingleFloatProperyUpdater.java │ │ │ │ │ ├── AnimationPropertyUpdater.java │ │ │ │ │ └── BUCK │ │ │ │ ├── bridge/ │ │ │ │ │ ├── ActivityEventListener.java │ │ │ │ │ ├── Arguments.java │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── BaseActivityEventListener.java │ │ │ │ │ ├── BaseJavaModule.java │ │ │ │ │ ├── CallbackImpl.java │ │ │ │ │ ├── CatalystInstance.java │ │ │ │ │ ├── CatalystInstanceImpl.java │ │ │ │ │ ├── ContextBaseJavaModule.java │ │ │ │ │ ├── CxxCallbackImpl.java │ │ │ │ │ ├── CxxModuleWrapper.java │ │ │ │ │ ├── CxxModuleWrapperBase.java │ │ │ │ │ ├── Dynamic.java │ │ │ │ │ ├── DynamicFromArray.java │ │ │ │ │ ├── DynamicFromMap.java │ │ │ │ │ ├── FallbackJSBundleLoader.java │ │ │ │ │ ├── GuardedRunnable.java │ │ │ │ │ ├── Inspector.java │ │ │ │ │ ├── JSBundleLoader.java │ │ │ │ │ ├── JSCJavaScriptExecutor.java │ │ │ │ │ ├── JSCJavaScriptExecutorFactory.java │ │ │ │ │ ├── JSInstance.java │ │ │ │ │ ├── JavaJSExecutor.java │ │ │ │ │ ├── JavaMethodWrapper.java │ │ │ │ │ ├── JavaModuleWrapper.java │ │ │ │ │ ├── JavaOnlyArray.java │ │ │ │ │ ├── JavaOnlyMap.java │ │ │ │ │ ├── JavaScriptContextHolder.java │ │ │ │ │ ├── JavaScriptExecutor.java │ │ │ │ │ ├── JavaScriptExecutorFactory.java │ │ │ │ │ ├── JavaScriptModuleRegistry.java │ │ │ │ │ ├── JsonWriter.java │ │ │ │ │ ├── JsonWriterHelper.java │ │ │ │ │ ├── LifecycleEventListener.java │ │ │ │ │ ├── MemoryPressure.java │ │ │ │ │ ├── MemoryPressureListener.java │ │ │ │ │ ├── ModuleHolder.java │ │ │ │ │ ├── ModuleSpec.java │ │ │ │ │ ├── NativeArray.java │ │ │ │ │ ├── NativeMap.java │ │ │ │ │ ├── NativeModule.java │ │ │ │ │ ├── NativeModuleRegistry.java │ │ │ │ │ ├── PerformanceCounter.java │ │ │ │ │ ├── Promise.java │ │ │ │ │ ├── ProxyJavaScriptExecutor.java │ │ │ │ │ ├── ReactBridge.java │ │ │ │ │ ├── ReactCallback.java │ │ │ │ │ ├── ReactContext.java │ │ │ │ │ ├── ReactMarker.java │ │ │ │ │ ├── ReactMarkerConstants.java │ │ │ │ │ ├── ReactMethod.java │ │ │ │ │ ├── ReactModuleWithSpec.java │ │ │ │ │ ├── ReadableArray.java │ │ │ │ │ ├── ReadableMap.java │ │ │ │ │ ├── ReadableNativeArray.java │ │ │ │ │ ├── ReadableNativeMap.java │ │ │ │ │ ├── WritableNativeArray.java │ │ │ │ │ ├── WritableNativeMap.java │ │ │ │ │ ├── bridge.pro │ │ │ │ │ ├── queue/ │ │ │ │ │ │ ├── MessageQueueThread.java │ │ │ │ │ │ ├── MessageQueueThreadImpl.java │ │ │ │ │ │ ├── MessageQueueThreadSpec.java │ │ │ │ │ │ ├── NativeRunnable.java │ │ │ │ │ │ ├── ReactQueueConfiguration.java │ │ │ │ │ │ ├── ReactQueueConfigurationImpl.java │ │ │ │ │ │ └── ReactQueueConfigurationSpec.java │ │ │ │ │ └── reactnative.pro │ │ │ │ ├── common/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ClearableSynchronizedPool.java │ │ │ │ │ ├── DebugServerException.java │ │ │ │ │ ├── JavascriptException.java │ │ │ │ │ ├── LifecycleState.java │ │ │ │ │ ├── ShakeDetector.java │ │ │ │ │ ├── SingleThreadAsserter.java │ │ │ │ │ ├── SystemClock.java │ │ │ │ │ ├── build/ │ │ │ │ │ │ └── ReactBuildConfig.java │ │ │ │ │ └── network/ │ │ │ │ │ ├── BUCK │ │ │ │ │ └── OkHttpCallUtil.java │ │ │ │ ├── devsupport/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── BundleDownloader.java │ │ │ │ │ ├── DebugOverlayController.java │ │ │ │ │ ├── DevInternalSettings.java │ │ │ │ │ ├── DevLoadingViewController.java │ │ │ │ │ ├── DevServerHelper.java │ │ │ │ │ ├── DevSettingsActivity.java │ │ │ │ │ ├── DevSupportManagerFactory.java │ │ │ │ │ ├── DevSupportManagerImpl.java │ │ │ │ │ ├── DisabledDevSupportManager.java │ │ │ │ │ ├── DoubleTapReloadRecognizer.java │ │ │ │ │ ├── FpsView.java │ │ │ │ │ ├── InspectorPackagerConnection.java │ │ │ │ │ ├── JSCHeapCapture.java │ │ │ │ │ ├── JSCSamplingProfiler.java │ │ │ │ │ ├── JSDebuggerWebSocketClient.java │ │ │ │ │ ├── JSException.java │ │ │ │ │ ├── MultipartStreamReader.java │ │ │ │ │ ├── ReactInstanceManagerDevHelper.java │ │ │ │ │ ├── RedBoxDialog.java │ │ │ │ │ ├── RedBoxHandler.java │ │ │ │ │ ├── StackTraceHelper.java │ │ │ │ │ ├── WebsocketJavaScriptExecutor.java │ │ │ │ │ ├── WindowOverlayCompat.java │ │ │ │ │ └── interfaces/ │ │ │ │ │ ├── DevBundleDownloadListener.java │ │ │ │ │ ├── DevOptionHandler.java │ │ │ │ │ ├── DevSupportManager.java │ │ │ │ │ ├── ErrorCustomizer.java │ │ │ │ │ ├── PackagerStatusCallback.java │ │ │ │ │ └── StackFrame.java │ │ │ │ ├── flat/ │ │ │ │ │ ├── AbstractDrawBorder.java │ │ │ │ │ ├── AbstractDrawCommand.java │ │ │ │ │ ├── AndroidView.java │ │ │ │ │ ├── AttachDetachListener.java │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── BitmapUpdateListener.java │ │ │ │ │ ├── ClippingDrawCommandManager.java │ │ │ │ │ ├── DrawBackgroundColor.java │ │ │ │ │ ├── DrawBorder.java │ │ │ │ │ ├── DrawCommand.java │ │ │ │ │ ├── DrawCommandManager.java │ │ │ │ │ ├── DrawImage.java │ │ │ │ │ ├── DrawImageWithDrawee.java │ │ │ │ │ ├── DrawTextLayout.java │ │ │ │ │ ├── DrawView.java │ │ │ │ │ ├── DraweeRequestHelper.java │ │ │ │ │ ├── ElementsList.java │ │ │ │ │ ├── FlatARTSurfaceViewManager.java │ │ │ │ │ ├── FlatARTSurfaceViewShadowNode.java │ │ │ │ │ ├── FlatMeasuredViewGroup.java │ │ │ │ │ ├── FlatNativeViewHierarchyManager.java │ │ │ │ │ ├── FlatReactModalShadowNode.java │ │ │ │ │ ├── FlatRootShadowNode.java │ │ │ │ │ ├── FlatRootViewManager.java │ │ │ │ │ ├── FlatShadowNode.java │ │ │ │ │ ├── FlatTextShadowNode.java │ │ │ │ │ ├── FlatUIImplementation.java │ │ │ │ │ ├── FlatUIViewOperationQueue.java │ │ │ │ │ ├── FlatViewGroup.java │ │ │ │ │ ├── FlatViewManager.java │ │ │ │ │ ├── FontStylingSpan.java │ │ │ │ │ ├── HitSlopNodeRegion.java │ │ │ │ │ ├── HorizontalDrawCommandManager.java │ │ │ │ │ ├── InlineImageSpanWithPipeline.java │ │ │ │ │ ├── MoveProxy.java │ │ │ │ │ ├── NativeViewWrapper.java │ │ │ │ │ ├── NodeRegion.java │ │ │ │ │ ├── PipelineRequestHelper.java │ │ │ │ │ ├── RCTImageView.java │ │ │ │ │ ├── RCTImageViewManager.java │ │ │ │ │ ├── RCTModalHostManager.java │ │ │ │ │ ├── RCTRawText.java │ │ │ │ │ ├── RCTRawTextManager.java │ │ │ │ │ ├── RCTText.java │ │ │ │ │ ├── RCTTextInlineImage.java │ │ │ │ │ ├── RCTTextInlineImageManager.java │ │ │ │ │ ├── RCTTextInput.java │ │ │ │ │ ├── RCTTextInputManager.java │ │ │ │ │ ├── RCTTextManager.java │ │ │ │ │ ├── RCTView.java │ │ │ │ │ ├── RCTViewManager.java │ │ │ │ │ ├── RCTViewPagerManager.java │ │ │ │ │ ├── RCTVirtualText.java │ │ │ │ │ ├── RCTVirtualTextManager.java │ │ │ │ │ ├── README.md │ │ │ │ │ ├── ShadowStyleSpan.java │ │ │ │ │ ├── StateBuilder.java │ │ │ │ │ ├── TextNodeRegion.java │ │ │ │ │ ├── TypefaceCache.java │ │ │ │ │ ├── VerticalDrawCommandManager.java │ │ │ │ │ ├── ViewResolver.java │ │ │ │ │ └── VirtualViewManager.java │ │ │ │ ├── jstasks/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── HeadlessJsTaskConfig.java │ │ │ │ │ ├── HeadlessJsTaskContext.java │ │ │ │ │ └── HeadlessJsTaskEventListener.java │ │ │ │ ├── module/ │ │ │ │ │ ├── annotations/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── ReactModule.java │ │ │ │ │ │ └── ReactModuleList.java │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── ReactModuleInfo.java │ │ │ │ │ │ └── ReactModuleInfoProvider.java │ │ │ │ │ └── processing/ │ │ │ │ │ ├── BUCK │ │ │ │ │ └── ReactModuleSpecProcessor.java │ │ │ │ ├── modules/ │ │ │ │ │ ├── accessibilityinfo/ │ │ │ │ │ │ ├── AccessibilityInfoModule.java │ │ │ │ │ │ └── BUCK │ │ │ │ │ ├── appregistry/ │ │ │ │ │ │ ├── AppRegistry.java │ │ │ │ │ │ └── BUCK │ │ │ │ │ ├── appstate/ │ │ │ │ │ │ ├── AppStateModule.java │ │ │ │ │ │ └── BUCK │ │ │ │ │ ├── blob/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── BlobModule.java │ │ │ │ │ │ └── BlobProvider.java │ │ │ │ │ ├── camera/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── CameraRollManager.java │ │ │ │ │ │ ├── ImageEditingManager.java │ │ │ │ │ │ └── ImageStoreManager.java │ │ │ │ │ ├── clipboard/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── ClipboardModule.java │ │ │ │ │ ├── common/ │ │ │ │ │ │ └── BUCK │ │ │ │ │ ├── core/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── ChoreographerCompat.java │ │ │ │ │ │ ├── DeviceEventManagerModule.java │ │ │ │ │ │ ├── ExceptionsManagerModule.java │ │ │ │ │ │ ├── HeadlessJsTaskSupportModule.java │ │ │ │ │ │ ├── JSTimers.java │ │ │ │ │ │ ├── PermissionAwareActivity.java │ │ │ │ │ │ ├── PermissionListener.java │ │ │ │ │ │ ├── ReactChoreographer.java │ │ │ │ │ │ └── Timing.java │ │ │ │ │ ├── datepicker/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── DatePickerDialogFragment.java │ │ │ │ │ │ ├── DatePickerDialogModule.java │ │ │ │ │ │ ├── DatePickerMode.java │ │ │ │ │ │ └── DismissableDatePickerDialog.java │ │ │ │ │ ├── debug/ │ │ │ │ │ │ ├── AnimationsDebugModule.java │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── DidJSUpdateUiDuringFrameDetector.java │ │ │ │ │ │ ├── FpsDebugFrameCallback.java │ │ │ │ │ │ ├── SourceCodeModule.java │ │ │ │ │ │ └── interfaces/ │ │ │ │ │ │ └── DeveloperSettings.java │ │ │ │ │ ├── deviceinfo/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── DeviceInfoModule.java │ │ │ │ │ ├── dialog/ │ │ │ │ │ │ ├── AlertFragment.java │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── DialogModule.java │ │ │ │ │ │ └── SupportAlertFragment.java │ │ │ │ │ ├── fresco/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── FrescoModule.java │ │ │ │ │ │ ├── ReactNetworkImageRequest.java │ │ │ │ │ │ ├── ReactOkHttpNetworkFetcher.java │ │ │ │ │ │ └── SystraceRequestListener.java │ │ │ │ │ ├── i18nmanager/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── I18nManagerModule.java │ │ │ │ │ │ └── I18nUtil.java │ │ │ │ │ ├── image/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── ImageLoaderModule.java │ │ │ │ │ ├── intent/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── IntentModule.java │ │ │ │ │ ├── location/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── LocationModule.java │ │ │ │ │ │ └── PositionError.java │ │ │ │ │ ├── netinfo/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── NetInfoModule.java │ │ │ │ │ ├── network/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── CookieJarContainer.java │ │ │ │ │ │ ├── NetworkInterceptorCreator.java │ │ │ │ │ │ ├── NetworkingModule.java │ │ │ │ │ │ ├── OkHttpClientProvider.java │ │ │ │ │ │ ├── ProgressListener.java │ │ │ │ │ │ ├── ProgressRequestBody.java │ │ │ │ │ │ ├── ProgressResponseBody.java │ │ │ │ │ │ ├── ReactCookieJarContainer.java │ │ │ │ │ │ ├── RequestBodyUtil.java │ │ │ │ │ │ ├── ResponseUtil.java │ │ │ │ │ │ └── TLSSocketFactory.java │ │ │ │ │ ├── permissions/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── PermissionsModule.java │ │ │ │ │ ├── share/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── ShareModule.java │ │ │ │ │ ├── statusbar/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── StatusBarModule.java │ │ │ │ │ ├── storage/ │ │ │ │ │ │ ├── AsyncLocalStorageUtil.java │ │ │ │ │ │ ├── AsyncStorageModule.java │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── ReactDatabaseSupplier.java │ │ │ │ │ ├── systeminfo/ │ │ │ │ │ │ ├── AndroidInfoHelpers.java │ │ │ │ │ │ ├── AndroidInfoModule.java │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── ReactNativeVersion.java │ │ │ │ │ ├── timepicker/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ ├── DismissableTimePickerDialog.java │ │ │ │ │ │ ├── TimePickerDialogFragment.java │ │ │ │ │ │ ├── TimePickerDialogModule.java │ │ │ │ │ │ └── TimePickerMode.java │ │ │ │ │ ├── toast/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── ToastModule.java │ │ │ │ │ ├── vibration/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── VibrationModule.java │ │ │ │ │ └── websocket/ │ │ │ │ │ ├── BUCK │ │ │ │ │ └── WebSocketModule.java │ │ │ │ ├── packagerconnection/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── FileIoHandler.java │ │ │ │ │ ├── JSPackagerClient.java │ │ │ │ │ ├── NotificationOnlyHandler.java │ │ │ │ │ ├── PackagerConnectionSettings.java │ │ │ │ │ ├── ReconnectingWebSocket.java │ │ │ │ │ ├── RequestHandler.java │ │ │ │ │ ├── RequestOnlyHandler.java │ │ │ │ │ ├── Responder.java │ │ │ │ │ └── SamplingProfilerPackagerMethod.java │ │ │ │ ├── processing/ │ │ │ │ │ ├── BUCK │ │ │ │ │ └── ReactPropertyProcessor.java │ │ │ │ ├── shell/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── MainPackageConfig.java │ │ │ │ │ └── MainReactPackage.java │ │ │ │ ├── touch/ │ │ │ │ │ └── BUCK │ │ │ │ ├── uimanager/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── BaseViewManager.java │ │ │ │ │ ├── DisplayMetricsHolder.java │ │ │ │ │ ├── FloatUtil.java │ │ │ │ │ ├── GuardedFrameCallback.java │ │ │ │ │ ├── JSTouchDispatcher.java │ │ │ │ │ ├── LayoutShadowNode.java │ │ │ │ │ ├── MatrixMathHelper.java │ │ │ │ │ ├── MeasureSpecProvider.java │ │ │ │ │ ├── NativeViewHierarchyManager.java │ │ │ │ │ ├── NativeViewHierarchyOptimizer.java │ │ │ │ │ ├── OnLayoutEvent.java │ │ │ │ │ ├── ReactClippingViewGroup.java │ │ │ │ │ ├── ReactClippingViewGroupHelper.java │ │ │ │ │ ├── ReactCompoundView.java │ │ │ │ │ ├── ReactRootViewTagGenerator.java │ │ │ │ │ ├── ReactShadowNode.java │ │ │ │ │ ├── ReactShadowNodeImpl.java │ │ │ │ │ ├── ReactStylesDiffMap.java │ │ │ │ │ ├── ReactZIndexedViewGroup.java │ │ │ │ │ ├── ShadowNodeRegistry.java │ │ │ │ │ ├── SizeMonitoringFrameLayout.java │ │ │ │ │ ├── Spacing.java │ │ │ │ │ ├── ThemedReactContext.java │ │ │ │ │ ├── TouchTargetHelper.java │ │ │ │ │ ├── TransformHelper.java │ │ │ │ │ ├── UIBlock.java │ │ │ │ │ ├── UIImplementation.java │ │ │ │ │ ├── UIImplementationProvider.java │ │ │ │ │ ├── UIManagerModule.java │ │ │ │ │ ├── UIManagerModuleConstants.java │ │ │ │ │ ├── UIManagerModuleConstantsHelper.java │ │ │ │ │ ├── UIManagerModuleListener.java │ │ │ │ │ ├── UIViewOperationQueue.java │ │ │ │ │ ├── ViewGroupDrawingOrderHelper.java │ │ │ │ │ ├── ViewGroupManager.java │ │ │ │ │ ├── ViewHierarchyDumper.java │ │ │ │ │ ├── ViewManager.java │ │ │ │ │ ├── ViewManagerPropertyUpdater.java │ │ │ │ │ ├── ViewManagerRegistry.java │ │ │ │ │ ├── ViewManagersPropertyCache.java │ │ │ │ │ ├── ViewProps.java │ │ │ │ │ ├── YogaNodePool.java │ │ │ │ │ ├── annotations/ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ └── ReactProp.java │ │ │ │ │ ├── events/ │ │ │ │ │ │ ├── ContentSizeChangeEvent.java │ │ │ │ │ │ ├── Event.java │ │ │ │ │ │ ├── EventDispatcher.java │ │ │ │ │ │ ├── EventDispatcherListener.java │ │ │ │ │ │ ├── TouchEvent.java │ │ │ │ │ │ ├── TouchEventCoalescingKeyHelper.java │ │ │ │ │ │ └── TouchesHelper.java │ │ │ │ │ ├── layoutanimation/ │ │ │ │ │ │ ├── AbstractLayoutAnimation.java │ │ │ │ │ │ ├── BaseLayoutAnimation.java │ │ │ │ │ │ ├── LayoutAnimationController.java │ │ │ │ │ │ ├── LayoutAnimationListener.java │ │ │ │ │ │ ├── LayoutAnimationType.java │ │ │ │ │ │ ├── LayoutDeleteAnimation.java │ │ │ │ │ │ ├── OpacityAnimation.java │ │ │ │ │ │ └── PositionAndSizeAnimation.java │ │ │ │ │ └── util/ │ │ │ │ │ ├── BUCK │ │ │ │ │ └── ReactFindViewUtil.java │ │ │ │ ├── util/ │ │ │ │ │ ├── BUCK │ │ │ │ │ └── JSStackTrace.java │ │ │ │ └── views/ │ │ │ │ ├── art/ │ │ │ │ │ ├── ARTGroupShadowNode.java │ │ │ │ │ ├── ARTGroupViewManager.java │ │ │ │ │ ├── ARTRenderableViewManager.java │ │ │ │ │ ├── ARTShapeShadowNode.java │ │ │ │ │ ├── ARTShapeViewManager.java │ │ │ │ │ ├── ARTSurfaceView.java │ │ │ │ │ ├── ARTSurfaceViewManager.java │ │ │ │ │ ├── ARTSurfaceViewShadowNode.java │ │ │ │ │ ├── ARTTextViewManager.java │ │ │ │ │ ├── ARTVirtualNode.java │ │ │ │ │ └── BUCK │ │ │ │ ├── checkbox/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ReactCheckBox.java │ │ │ │ │ ├── ReactCheckBoxEvent.java │ │ │ │ │ └── ReactCheckBoxManager.java │ │ │ │ ├── common/ │ │ │ │ │ ├── BUCK │ │ │ │ │ └── ViewHelper.java │ │ │ │ ├── drawer/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ReactDrawerLayoutManager.java │ │ │ │ │ └── events/ │ │ │ │ │ ├── DrawerClosedEvent.java │ │ │ │ │ ├── DrawerOpenedEvent.java │ │ │ │ │ ├── DrawerSlideEvent.java │ │ │ │ │ └── DrawerStateChangedEvent.java │ │ │ │ ├── image/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── GlobalImageLoadListener.java │ │ │ │ │ ├── ImageLoadEvent.java │ │ │ │ │ ├── ImageResizeMethod.java │ │ │ │ │ ├── ImageResizeMode.java │ │ │ │ │ ├── ReactImageManager.java │ │ │ │ │ └── ReactImageView.java │ │ │ │ ├── imagehelper/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ImageSource.java │ │ │ │ │ ├── MultiSourceHelper.java │ │ │ │ │ └── ResourceDrawableIdHelper.java │ │ │ │ ├── modal/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ModalHostHelper.java │ │ │ │ │ ├── ModalHostShadowNode.java │ │ │ │ │ ├── ReactModalHostManager.java │ │ │ │ │ ├── ReactModalHostView.java │ │ │ │ │ ├── RequestCloseEvent.java │ │ │ │ │ └── ShowEvent.java │ │ │ │ ├── picker/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ReactDialogPickerManager.java │ │ │ │ │ ├── ReactDropdownPickerManager.java │ │ │ │ │ ├── ReactPicker.java │ │ │ │ │ ├── ReactPickerManager.java │ │ │ │ │ └── events/ │ │ │ │ │ └── PickerItemSelectEvent.java │ │ │ │ ├── progressbar/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ProgressBarContainerView.java │ │ │ │ │ ├── ProgressBarShadowNode.java │ │ │ │ │ └── ReactProgressBarViewManager.java │ │ │ │ ├── scroll/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── FpsListener.java │ │ │ │ │ ├── OnScrollDispatchHelper.java │ │ │ │ │ ├── ReactHorizontalScrollContainerView.java │ │ │ │ │ ├── ReactHorizontalScrollContainerViewManager.java │ │ │ │ │ ├── ReactHorizontalScrollView.java │ │ │ │ │ ├── ReactHorizontalScrollViewManager.java │ │ │ │ │ ├── ReactScrollView.java │ │ │ │ │ ├── ReactScrollViewCommandHelper.java │ │ │ │ │ ├── ReactScrollViewHelper.java │ │ │ │ │ ├── ReactScrollViewManager.java │ │ │ │ │ ├── ScrollEvent.java │ │ │ │ │ ├── ScrollEventType.java │ │ │ │ │ └── VelocityHelper.java │ │ │ │ ├── slider/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ReactSlider.java │ │ │ │ │ ├── ReactSliderEvent.java │ │ │ │ │ ├── ReactSliderManager.java │ │ │ │ │ └── ReactSlidingCompleteEvent.java │ │ │ │ ├── swiperefresh/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ReactSwipeRefreshLayout.java │ │ │ │ │ ├── RefreshEvent.java │ │ │ │ │ └── SwipeRefreshLayoutManager.java │ │ │ │ ├── switchview/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ReactSwitch.java │ │ │ │ │ ├── ReactSwitchEvent.java │ │ │ │ │ └── ReactSwitchManager.java │ │ │ │ ├── text/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── CustomLineHeightSpan.java │ │ │ │ │ ├── ReactBaseTextShadowNode.java │ │ │ │ │ ├── ReactRawTextManager.java │ │ │ │ │ ├── ReactRawTextShadowNode.java │ │ │ │ │ ├── ReactTextAnchorViewManager.java │ │ │ │ │ ├── ReactTextInlineImageShadowNode.java │ │ │ │ │ ├── ReactTextShadowNode.java │ │ │ │ │ ├── ReactTextUpdate.java │ │ │ │ │ ├── ReactTextView.java │ │ │ │ │ ├── ReactTextViewManager.java │ │ │ │ │ ├── ReactVirtualTextShadowNode.java │ │ │ │ │ ├── ReactVirtualTextViewManager.java │ │ │ │ │ ├── TextInlineImageSpan.java │ │ │ │ │ └── frescosupport/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── FrescoBasedReactTextInlineImageShadowNode.java │ │ │ │ │ ├── FrescoBasedReactTextInlineImageSpan.java │ │ │ │ │ └── FrescoBasedReactTextInlineImageViewManager.java │ │ │ │ ├── textinput/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ContentSizeWatcher.java │ │ │ │ │ ├── ReactContentSizeChangedEvent.java │ │ │ │ │ ├── ReactEditText.java │ │ │ │ │ ├── ReactEditTextInputConnectionWrapper.java │ │ │ │ │ ├── ReactTextChangedEvent.java │ │ │ │ │ ├── ReactTextInputBlurEvent.java │ │ │ │ │ ├── ReactTextInputEndEditingEvent.java │ │ │ │ │ ├── ReactTextInputEvent.java │ │ │ │ │ ├── ReactTextInputFocusEvent.java │ │ │ │ │ ├── ReactTextInputKeyPressEvent.java │ │ │ │ │ ├── ReactTextInputLocalData.java │ │ │ │ │ ├── ReactTextInputManager.java │ │ │ │ │ ├── ReactTextInputSelectionEvent.java │ │ │ │ │ ├── ReactTextInputShadowNode.java │ │ │ │ │ ├── ReactTextInputSubmitEditingEvent.java │ │ │ │ │ └── ScrollWatcher.java │ │ │ │ ├── toolbar/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── ReactToolbar.java │ │ │ │ │ ├── ReactToolbarManager.java │ │ │ │ │ └── events/ │ │ │ │ │ └── ToolbarClickEvent.java │ │ │ │ ├── view/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── MeasureUtil.java │ │ │ │ │ ├── ReactViewBackgroundDrawable.java │ │ │ │ │ ├── ReactViewBackgroundManager.java │ │ │ │ │ ├── ReactViewGroup.java │ │ │ │ │ └── ReactViewManager.java │ │ │ │ ├── viewpager/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── PageScrollEvent.java │ │ │ │ │ ├── PageScrollStateChangedEvent.java │ │ │ │ │ ├── PageSelectedEvent.java │ │ │ │ │ ├── ReactViewPager.java │ │ │ │ │ └── ReactViewPagerManager.java │ │ │ │ └── webview/ │ │ │ │ ├── BUCK │ │ │ │ ├── ReactWebViewManager.java │ │ │ │ └── events/ │ │ │ │ ├── TopLoadingErrorEvent.java │ │ │ │ ├── TopLoadingFinishEvent.java │ │ │ │ ├── TopLoadingStartEvent.java │ │ │ │ └── TopMessageEvent.java │ │ │ ├── systrace/ │ │ │ │ ├── BUCK │ │ │ │ └── Systrace.java │ │ │ └── yoga/ │ │ │ ├── YogaAlign.java │ │ │ ├── YogaBaselineFunction.java │ │ │ ├── YogaConfig.java │ │ │ ├── YogaConstants.java │ │ │ ├── YogaDimension.java │ │ │ ├── YogaDirection.java │ │ │ ├── YogaDisplay.java │ │ │ ├── YogaEdge.java │ │ │ ├── YogaExperimentalFeature.java │ │ │ ├── YogaFlexDirection.java │ │ │ ├── YogaJustify.java │ │ │ ├── YogaLogLevel.java │ │ │ ├── YogaLogger.java │ │ │ ├── YogaMeasureFunction.java │ │ │ ├── YogaMeasureMode.java │ │ │ ├── YogaMeasureOutput.java │ │ │ ├── YogaNode.java │ │ │ ├── YogaNodeType.java │ │ │ ├── YogaOverflow.java │ │ │ ├── YogaPositionType.java │ │ │ ├── YogaPrintOptions.java │ │ │ ├── YogaUnit.java │ │ │ ├── YogaValue.java │ │ │ └── YogaWrap.java │ │ ├── jni/ │ │ │ ├── Application.mk │ │ │ ├── first-party/ │ │ │ │ ├── fb/ │ │ │ │ │ ├── Android.mk │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── Doxyfile │ │ │ │ │ ├── include/ │ │ │ │ │ │ ├── fb/ │ │ │ │ │ │ │ ├── ALog.h │ │ │ │ │ │ │ ├── Build.h │ │ │ │ │ │ │ ├── Countable.h │ │ │ │ │ │ │ ├── Doxyfile │ │ │ │ │ │ │ ├── Environment.h │ │ │ │ │ │ │ ├── ProgramLocation.h │ │ │ │ │ │ │ ├── RefPtr.h │ │ │ │ │ │ │ ├── StaticInitialized.h │ │ │ │ │ │ │ ├── ThreadLocal.h │ │ │ │ │ │ │ ├── assert.h │ │ │ │ │ │ │ ├── fbjni/ │ │ │ │ │ │ │ │ ├── Boxed.h │ │ │ │ │ │ │ │ ├── ByteBuffer.h │ │ │ │ │ │ │ │ ├── Common.h │ │ │ │ │ │ │ │ ├── Context.h │ │ │ │ │ │ │ │ ├── CoreClasses-inl.h │ │ │ │ │ │ │ │ ├── CoreClasses.h │ │ │ │ │ │ │ │ ├── Exceptions.h │ │ │ │ │ │ │ │ ├── File.h │ │ │ │ │ │ │ │ ├── Hybrid.h │ │ │ │ │ │ │ │ ├── Iterator-inl.h │ │ │ │ │ │ │ │ ├── Iterator.h │ │ │ │ │ │ │ │ ├── JThread.h │ │ │ │ │ │ │ │ ├── JWeakReference.h │ │ │ │ │ │ │ │ ├── Meta-forward.h │ │ │ │ │ │ │ │ ├── Meta-inl.h │ │ │ │ │ │ │ │ ├── Meta.h │ │ │ │ │ │ │ │ ├── MetaConvert.h │ │ │ │ │ │ │ │ ├── NativeRunnable.h │ │ │ │ │ │ │ │ ├── ReferenceAllocators-inl.h │ │ │ │ │ │ │ │ ├── ReferenceAllocators.h │ │ │ │ │ │ │ │ ├── References-forward.h │ │ │ │ │ │ │ │ ├── References-inl.h │ │ │ │ │ │ │ │ ├── References.h │ │ │ │ │ │ │ │ ├── Registration-inl.h │ │ │ │ │ │ │ │ ├── Registration.h │ │ │ │ │ │ │ │ └── TypeTraits.h │ │ │ │ │ │ │ ├── fbjni.h │ │ │ │ │ │ │ ├── log.h │ │ │ │ │ │ │ ├── lyra.h │ │ │ │ │ │ │ ├── noncopyable.h │ │ │ │ │ │ │ ├── nonmovable.h │ │ │ │ │ │ │ └── visibility.h │ │ │ │ │ │ └── jni/ │ │ │ │ │ │ ├── Countable.h │ │ │ │ │ │ ├── GlobalReference.h │ │ │ │ │ │ ├── JniTerminateHandler.h │ │ │ │ │ │ ├── LocalReference.h │ │ │ │ │ │ ├── LocalString.h │ │ │ │ │ │ ├── Registration.h │ │ │ │ │ │ ├── WeakReference.h │ │ │ │ │ │ └── jni_helpers.h │ │ │ │ │ ├── jni/ │ │ │ │ │ │ ├── ByteBuffer.cpp │ │ │ │ │ │ ├── Countable.cpp │ │ │ │ │ │ ├── Environment.cpp │ │ │ │ │ │ ├── Exceptions.cpp │ │ │ │ │ │ ├── Hybrid.cpp │ │ │ │ │ │ ├── LocalString.cpp │ │ │ │ │ │ ├── OnLoad.cpp │ │ │ │ │ │ ├── References.cpp │ │ │ │ │ │ ├── WeakReference.cpp │ │ │ │ │ │ ├── android/ │ │ │ │ │ │ │ ├── CpuCapabilities.cpp │ │ │ │ │ │ │ └── ReferenceChecking.cpp │ │ │ │ │ │ ├── fbjni.cpp │ │ │ │ │ │ ├── java/ │ │ │ │ │ │ │ ├── BUCK │ │ │ │ │ │ │ ├── CppException.java │ │ │ │ │ │ │ ├── CppSystemErrorException.java │ │ │ │ │ │ │ └── UnknownCppException.java │ │ │ │ │ │ └── jni_helpers.cpp │ │ │ │ │ ├── lyra/ │ │ │ │ │ │ └── lyra.cpp │ │ │ │ │ └── onload.cpp │ │ │ │ ├── fbgloginit/ │ │ │ │ │ ├── Android.mk │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── fb/ │ │ │ │ │ │ └── glog_init.h │ │ │ │ │ └── glog_init.cpp │ │ │ │ ├── jni-hack/ │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── README.md │ │ │ │ │ ├── jni.h │ │ │ │ │ └── real/ │ │ │ │ │ └── jni.h │ │ │ │ └── yogajni/ │ │ │ │ ├── Android.mk │ │ │ │ ├── BUCK │ │ │ │ └── jni/ │ │ │ │ └── YGJNI.cpp │ │ │ ├── packagerconnection/ │ │ │ │ ├── Android.mk │ │ │ │ ├── BUCK │ │ │ │ ├── JSPackagerClientResponder.cpp │ │ │ │ ├── JSPackagerClientResponder.h │ │ │ │ ├── OnLoad.cpp │ │ │ │ ├── SamplingProfilerJniMethod.cpp │ │ │ │ └── SamplingProfilerJniMethod.h │ │ │ ├── prebuilt/ │ │ │ │ └── BUCK │ │ │ ├── react/ │ │ │ │ ├── jni/ │ │ │ │ │ ├── Android.mk │ │ │ │ │ ├── AndroidJSCFactory.cpp │ │ │ │ │ ├── AndroidJSCFactory.h │ │ │ │ │ ├── BUCK │ │ │ │ │ ├── CatalystInstanceImpl.cpp │ │ │ │ │ ├── CatalystInstanceImpl.h │ │ │ │ │ ├── CxxModuleWrapper.cpp │ │ │ │ │ ├── CxxModuleWrapper.h │ │ │ │ │ ├── CxxModuleWrapperBase.h │ │ │ │ │ ├── CxxSharedModuleWrapper.h │ │ │ │ │ ├── JCallback.h │ │ │ │ │ ├── JInspector.cpp │ │ │ │ │ ├── JInspector.h │ │ │ │ │ ├── JMessageQueueThread.cpp │ │ │ │ │ ├── JMessageQueueThread.h │ │ │ │ │ ├── JNativeRunnable.h │ │ │ │ │ ├── JSCPerfLogging.cpp │ │ │ │ │ ├── JSCPerfLogging.h │ │ │ │ │ ├── JSLoader.cpp │ │ │ │ │ ├── JSLoader.h │ │ │ │ │ ├── JSLogging.cpp │ │ │ │ │ ├── JSLogging.h │ │ │ │ │ ├── JavaModuleWrapper.cpp │ │ │ │ │ ├── JavaModuleWrapper.h │ │ │ │ │ ├── JavaScriptExecutorHolder.h │ │ │ │ │ ├── JniJSModulesUnbundle.cpp │ │ │ │ │ ├── JniJSModulesUnbundle.h │ │ │ │ │ ├── MethodInvoker.cpp │ │ │ │ │ ├── MethodInvoker.h │ │ │ │ │ ├── ModuleRegistryBuilder.cpp │ │ │ │ │ ├── ModuleRegistryBuilder.h │ │ │ │ │ ├── NativeArray.cpp │ │ │ │ │ ├── NativeArray.h │ │ │ │ │ ├── NativeCommon.cpp │ │ │ │ │ ├── NativeCommon.h │ │ │ │ │ ├── NativeMap.cpp │ │ │ │ │ ├── NativeMap.h │ │ │ │ │ ├── OnLoad.cpp │ │ │ │ │ ├── OnLoad.h │ │ │ │ │ ├── ProxyExecutor.cpp │ │ │ │ │ ├── ProxyExecutor.h │ │ │ │ │ ├── ReactMarker.cpp │ │ │ │ │ ├── ReactMarker.h │ │ │ │ │ ├── ReadableNativeArray.cpp │ │ │ │ │ ├── ReadableNativeArray.h │ │ │ │ │ ├── ReadableNativeMap.cpp │ │ │ │ │ ├── ReadableNativeMap.h │ │ │ │ │ ├── WritableNativeArray.cpp │ │ │ │ │ ├── WritableNativeArray.h │ │ │ │ │ ├── WritableNativeMap.cpp │ │ │ │ │ └── WritableNativeMap.h │ │ │ │ └── perftests/ │ │ │ │ ├── BUCK │ │ │ │ └── OnLoad.cpp │ │ │ └── third-party/ │ │ │ ├── android-ndk/ │ │ │ │ └── BUCK │ │ │ ├── boost/ │ │ │ │ └── Android.mk │ │ │ ├── folly/ │ │ │ │ └── Android.mk │ │ │ └── glibc/ │ │ │ └── BUCK │ │ ├── libraries/ │ │ │ ├── fbcore/ │ │ │ │ └── src/ │ │ │ │ ├── main/ │ │ │ │ │ └── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── facebook/ │ │ │ │ │ └── common/ │ │ │ │ │ └── logging/ │ │ │ │ │ └── BUCK │ │ │ │ └── test/ │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── facebook/ │ │ │ │ └── powermock/ │ │ │ │ └── BUCK │ │ │ ├── fresco/ │ │ │ │ └── fresco-react-native/ │ │ │ │ └── BUCK │ │ │ ├── soloader/ │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── facebook/ │ │ │ │ └── soloader/ │ │ │ │ └── BUCK │ │ │ └── textlayoutbuilder/ │ │ │ └── BUCK │ │ ├── res/ │ │ │ ├── BUCK │ │ │ ├── devsupport/ │ │ │ │ ├── layout/ │ │ │ │ │ ├── dev_loading_view.xml │ │ │ │ │ └── redbox_view.xml │ │ │ │ ├── values/ │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ └── xml/ │ │ │ │ └── preferences.xml │ │ │ ├── shell/ │ │ │ │ └── values/ │ │ │ │ └── styles.xml │ │ │ └── views/ │ │ │ ├── modal/ │ │ │ │ ├── anim/ │ │ │ │ │ ├── catalyst_fade_in.xml │ │ │ │ │ ├── catalyst_fade_out.xml │ │ │ │ │ ├── catalyst_slide_down.xml │ │ │ │ │ └── catalyst_slide_up.xml │ │ │ │ └── values/ │ │ │ │ └── themes.xml │ │ │ └── uimanager/ │ │ │ └── values/ │ │ │ └── ids.xml │ │ └── third-party/ │ │ ├── android/ │ │ │ ├── support/ │ │ │ │ ├── v4/ │ │ │ │ │ └── BUCK │ │ │ │ └── v7/ │ │ │ │ └── appcompat-orig/ │ │ │ │ └── BUCK │ │ │ └── support-annotations/ │ │ │ └── BUCK │ │ └── java/ │ │ ├── asm/ │ │ │ └── BUCK │ │ ├── buck-android-support/ │ │ │ ├── BUCK │ │ │ └── buck-android-support.jar │ │ ├── fest/ │ │ │ └── BUCK │ │ ├── infer-annotations/ │ │ │ ├── BUCK │ │ │ └── infer-annotations-4.0.jar │ │ ├── javapoet/ │ │ │ └── BUCK │ │ ├── jsr-305/ │ │ │ └── BUCK │ │ ├── jsr-330/ │ │ │ └── BUCK │ │ ├── junit/ │ │ │ └── BUCK │ │ ├── mockito/ │ │ │ └── BUCK │ │ ├── okhttp/ │ │ │ └── BUCK │ │ ├── okio/ │ │ │ └── BUCK │ │ ├── robolectric3/ │ │ │ └── robolectric/ │ │ │ └── BUCK │ │ ├── sqlite/ │ │ │ └── BUCK │ │ └── testing-support-lib/ │ │ └── BUCK │ └── test/ │ └── java/ │ ├── com/ │ │ └── facebook/ │ │ ├── common/ │ │ │ └── logging/ │ │ │ ├── BUCK │ │ │ └── FakeLoggingDelegate.java │ │ └── react/ │ │ ├── BUCK │ │ ├── CompositeReactPackageTest.java │ │ ├── RootViewTest.java │ │ ├── animated/ │ │ │ ├── BUCK │ │ │ ├── NativeAnimatedInterpolationTest.java │ │ │ └── NativeAnimatedNodeTraversalTest.java │ │ ├── bridge/ │ │ │ ├── BUCK │ │ │ ├── BaseJavaModuleTest.java │ │ │ ├── FallbackJSBundleLoaderTest.java │ │ │ ├── JavaOnlyArrayTest.java │ │ │ ├── JsonWriterTest.java │ │ │ └── ModuleSpecTest.java │ │ ├── devsupport/ │ │ │ ├── BUCK │ │ │ ├── JSDebuggerWebSocketClientTest.java │ │ │ ├── MultipartStreamReaderTest.java │ │ │ └── StackTraceHelperTest.java │ │ ├── modules/ │ │ │ ├── BUCK │ │ │ ├── camera/ │ │ │ │ └── ImageStoreManagerTest.java │ │ │ ├── clipboard/ │ │ │ │ └── ClipboardModuleTest.java │ │ │ ├── dialog/ │ │ │ │ └── DialogModuleTest.java │ │ │ ├── network/ │ │ │ │ └── NetworkingModuleTest.java │ │ │ ├── share/ │ │ │ │ └── ShareModuleTest.java │ │ │ └── timing/ │ │ │ └── TimingModuleTest.java │ │ ├── packagerconnection/ │ │ │ ├── BUCK │ │ │ └── JSPackagerClientTest.java │ │ ├── uimanager/ │ │ │ ├── BUCK │ │ │ ├── LayoutPropertyApplicatorTest.java │ │ │ ├── MatrixMathHelperTest.java │ │ │ ├── ReactPropConstantsTest.java │ │ │ ├── ReactPropForShadowNodeSetterTest.java │ │ │ ├── ReactPropForShadowNodeSpecTest.java │ │ │ ├── UIManagerModuleConstantsTest.java │ │ │ └── UIManagerModuleTest.java │ │ └── views/ │ │ ├── BUCK │ │ ├── image/ │ │ │ ├── ImageResizeModeTest.java │ │ │ └── ReactImagePropertyTest.java │ │ ├── slider/ │ │ │ └── ReactSliderPropertyTest.java │ │ ├── text/ │ │ │ ├── CustomLineHeightSpanTest.java │ │ │ └── ReactTextTest.java │ │ └── textinput/ │ │ ├── ReactTextInputPropertyTest.java │ │ └── TextInputTest.java │ └── org/ │ └── mockito/ │ └── configuration/ │ └── BUCK ├── ReactCommon/ │ ├── DEFS │ ├── cxxreact/ │ │ ├── Android.mk │ │ ├── BUCK │ │ ├── CxxModule.h │ │ ├── CxxNativeModule.cpp │ │ ├── CxxNativeModule.h │ │ ├── Instance.cpp │ │ ├── Instance.h │ │ ├── JSBigString.cpp │ │ ├── JSBigString.h │ │ ├── JSBundleType.cpp │ │ ├── JSBundleType.h │ │ ├── JSCExecutor.cpp │ │ ├── JSCExecutor.h │ │ ├── JSCLegacyTracing.cpp │ │ ├── JSCLegacyTracing.h │ │ ├── JSCMemory.cpp │ │ ├── JSCMemory.h │ │ ├── JSCNativeModules.cpp │ │ ├── JSCNativeModules.h │ │ ├── JSCPerfStats.cpp │ │ ├── JSCPerfStats.h │ │ ├── JSCSamplingProfiler.cpp │ │ ├── JSCSamplingProfiler.h │ │ ├── JSCTracing.cpp │ │ ├── JSCTracing.h │ │ ├── JSCUtils.cpp │ │ ├── JSCUtils.h │ │ ├── JSExecutor.h │ │ ├── JSIndexedRAMBundle.cpp │ │ ├── JSIndexedRAMBundle.h │ │ ├── JSModulesUnbundle.h │ │ ├── JsArgumentHelpers-inl.h │ │ ├── JsArgumentHelpers.h │ │ ├── MessageQueueThread.h │ │ ├── MethodCall.cpp │ │ ├── MethodCall.h │ │ ├── ModuleRegistry.cpp │ │ ├── ModuleRegistry.h │ │ ├── NativeModule.h │ │ ├── NativeToJsBridge.cpp │ │ ├── NativeToJsBridge.h │ │ ├── Platform.cpp │ │ ├── Platform.h │ │ ├── RAMBundleRegistry.cpp │ │ ├── RAMBundleRegistry.h │ │ ├── RecoverableError.h │ │ ├── SampleCxxModule.cpp │ │ ├── SampleCxxModule.h │ │ ├── SharedProxyCxxModule.h │ │ ├── SystraceSection.h │ │ ├── oss-compat-util.h │ │ └── tests/ │ │ ├── BUCK │ │ ├── RecoverableErrorTest.cpp │ │ ├── jsarg_helpers.cpp │ │ ├── jsbigstring.cpp │ │ ├── jscexecutor.cpp │ │ ├── jsclogging.cpp │ │ ├── methodcall.cpp │ │ └── value.cpp │ ├── jschelpers/ │ │ ├── Android.mk │ │ ├── BUCK │ │ ├── JSCHelpers.cpp │ │ ├── JSCHelpers.h │ │ ├── JSCWrapper.cpp │ │ ├── JSCWrapper.h │ │ ├── JavaScriptCore.h │ │ ├── Unicode.cpp │ │ ├── Unicode.h │ │ ├── Value.cpp │ │ ├── Value.h │ │ ├── noncopyable.h │ │ └── systemJSCWrapper.cpp │ ├── jsinspector/ │ │ ├── Android.mk │ │ ├── BUCK │ │ ├── InspectorInterfaces.cpp │ │ └── InspectorInterfaces.h │ ├── microprofiler/ │ │ ├── BUCK │ │ ├── MicroProfiler.cpp │ │ └── MicroProfiler.h │ ├── privatedata/ │ │ ├── Android.mk │ │ ├── BUCK │ │ ├── PrivateDataBase.cpp │ │ └── PrivateDataBase.h │ └── yoga/ │ ├── Android.mk │ ├── BUCK │ ├── yoga/ │ │ ├── YGEnums.cpp │ │ ├── YGEnums.h │ │ ├── YGMacros.h │ │ ├── YGNode.cpp │ │ ├── YGNode.h │ │ ├── YGNodePrint.cpp │ │ ├── YGNodePrint.h │ │ ├── Yoga-internal.h │ │ ├── Yoga.cpp │ │ └── Yoga.h │ └── yoga.podspec ├── Releases.md ├── babel-preset/ │ ├── README.md │ ├── configs/ │ │ ├── hmr.js │ │ └── main.js │ ├── index.js │ ├── lib/ │ │ └── resolvePlugins.js │ ├── package.json │ ├── plugins.js │ └── transforms/ │ └── transform-symbol-member.js ├── blog/ │ ├── 2015-03-26-react-native-bringing-modern-web-techniques-to-mobile.md │ ├── 2015-09-14-react-native-for-android.md │ ├── 2015-11-23-making-react-native-apps-accessible.md │ ├── 2016-03-24-introducing-hot-reloading.md │ ├── 2016-03-28-dive-into-react-native-performance.md │ ├── 2016-04-13-react-native-a-year-in-review.md │ ├── 2016-07-06-toward-better-documentation.md │ ├── 2016-08-12-react-native-meetup-san-francisco.md │ ├── 2016-08-19-right-to-left-support-for-react-native-apps.md │ ├── 2016-09-08-exponent-talks-unraveling-navigation.md │ ├── 2016-10-25-0.36-headless-js-the-keyboard-api-and-more.md │ ├── 2016-11-08-introducing-button-yarn-and-a-public-roadmap.md │ ├── 2016-12-05-easier-upgrades.md │ ├── 2017-01-07-monthly-release-cadence.md │ ├── 2017-02-14-using-native-driver-for-animated.md │ ├── 2017-03-13-better-list-views.md │ ├── 2017-03-13-idx-the-existential-function.md │ ├── 2017-03-13-introducing-create-react-native-app.md │ ├── 2017-06-21-react-native-monthly-1.md │ ├── 2017-07-28-react-native-monthly-2.md │ ├── 2017-08-07-react-native-performance-in-marketplace.md │ ├── 2017-08-30-react-native-monthly-3.md │ ├── 2017-09-21-react-native-monthly-4.md │ └── 2017-11-06-react-native-monthly-5.md ├── bots/ │ ├── IssueCommands.txt │ ├── NewIssueGreeting.md │ ├── QuestionGreeting.md │ ├── code-analysis-bot.js │ ├── pr-inactivity-bookmarklet.js │ └── question-bookmarklet.js ├── breaking-changes.md ├── build.gradle ├── cli.js ├── danger/ │ ├── .babelrc │ ├── README.md │ ├── dangerfile.js │ └── package.json ├── flow/ │ ├── Map.js │ ├── Position.js │ ├── Promise.js │ ├── Set.js │ ├── console.js │ ├── create-react-class.js │ ├── fbjs.js │ └── prop-types.js ├── flow-github/ │ └── metro.js ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jest/ │ ├── .eslintrc │ ├── assetFileTransformer.js │ ├── mockComponent.js │ ├── preprocessor.js │ └── setup.js ├── jest-preset.json ├── keystores/ │ ├── BUCK │ ├── debug.keystore │ └── debug.keystore.properties ├── lib/ │ ├── InitializeJavaScriptAppEngine.js │ ├── RCTEventEmitter.js │ ├── README │ ├── TextInputState.js │ ├── UIManager.js │ ├── UIManagerStatTracker.js │ ├── View.js │ ├── deepDiffer.js │ ├── deepFreezeAndThrowOnMutationInDev.js │ └── flattenStyle.js ├── local-cli/ │ ├── .eslintrc │ ├── __mocks__/ │ │ ├── beeper.js │ │ └── fs.js │ ├── __tests__/ │ │ └── fs-mock-test.js │ ├── bundle/ │ │ ├── __mocks__/ │ │ │ └── sign.js │ │ ├── __tests__/ │ │ │ ├── filterPlatformAssetScales-test.js │ │ │ ├── getAssetDestPathAndroid-test.js │ │ │ └── getAssetDestPathIOS-test.js │ │ ├── assetPathUtils.js │ │ ├── buildBundle.js │ │ ├── bundle.js │ │ ├── bundleCommandLineArgs.js │ │ ├── filterPlatformAssetScales.js │ │ ├── getAssetDestPathAndroid.js │ │ ├── getAssetDestPathIOS.js │ │ ├── saveAssets.js │ │ ├── types.flow.js │ │ └── unbundle.js │ ├── cli.js │ ├── cliEntry.js │ ├── commands.js │ ├── core/ │ │ ├── Constants.js │ │ ├── __fixtures__/ │ │ │ ├── android.js │ │ │ ├── commands.js │ │ │ ├── dependencies.js │ │ │ ├── files/ │ │ │ │ ├── AndroidManifest-debug.xml │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── Main.java │ │ │ │ ├── ReactPackage.java │ │ │ │ ├── ReactPackage.kt │ │ │ │ ├── package.json │ │ │ │ └── project.pbxproj │ │ │ ├── ios.js │ │ │ └── projects.js │ │ ├── __tests__/ │ │ │ ├── android/ │ │ │ │ ├── findAndroidAppFolder.spec.js │ │ │ │ ├── findManifest.spec.js │ │ │ │ ├── findPackageClassName.spec.js │ │ │ │ ├── getDependencyConfig.spec.js │ │ │ │ ├── getProjectConfig.spec.js │ │ │ │ └── readManifest.spec.js │ │ │ ├── findAssets.spec.js │ │ │ ├── findPlugins.spec.js │ │ │ ├── ios/ │ │ │ │ ├── findPodfilePath.spec.js │ │ │ │ ├── findPodspecName.spec.js │ │ │ │ ├── findProject.spec.js │ │ │ │ └── getProjectConfig.spec.js │ │ │ └── makeCommand.spec.js │ │ ├── android/ │ │ │ ├── findAndroidAppFolder.js │ │ │ ├── findManifest.js │ │ │ ├── findPackageClassName.js │ │ │ ├── index.js │ │ │ └── readManifest.js │ │ ├── default.config.js │ │ ├── findAssets.js │ │ ├── findPlugins.js │ │ ├── index.js │ │ ├── ios/ │ │ │ ├── findPodfilePath.js │ │ │ ├── findPodspecName.js │ │ │ ├── findProject.js │ │ │ └── index.js │ │ ├── macos/ │ │ │ ├── findProject.js │ │ │ └── index.js │ │ ├── makeCommand.js │ │ ├── windows/ │ │ │ ├── findNamespace.js │ │ │ ├── findPackageClassName.js │ │ │ ├── findProject.js │ │ │ ├── findWindowsSolution.js │ │ │ ├── generateGUID.js │ │ │ └── index.js │ │ └── wrapCommands.js │ ├── dependencies/ │ │ └── dependencies.js │ ├── eject/ │ │ └── eject.js │ ├── generator/ │ │ ├── copyProjectTemplateAndReplace.js │ │ ├── printRunInstructions.js │ │ ├── promptSync.js │ │ └── templates.js │ ├── index.js │ ├── info/ │ │ └── info.js │ ├── init/ │ │ └── init.js │ ├── install/ │ │ ├── install.js │ │ └── uninstall.js │ ├── library/ │ │ └── library.js │ ├── link/ │ │ ├── __fixtures__/ │ │ │ ├── Info.plist │ │ │ ├── android/ │ │ │ │ ├── 0.17/ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── patchedMainActivity.java │ │ │ │ ├── 0.18/ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── patchedMainActivity.java │ │ │ │ ├── 0.20/ │ │ │ │ │ └── MainActivity.java │ │ │ │ ├── build.gradle │ │ │ │ ├── patchedBuild.gradle │ │ │ │ ├── patchedSettings.gradle │ │ │ │ └── settings.gradle │ │ │ ├── linearGradient.pbxproj │ │ │ ├── pods/ │ │ │ │ ├── PodfileSimple │ │ │ │ ├── PodfileWithFunction │ │ │ │ ├── PodfileWithMarkers │ │ │ │ └── PodfileWithTarget │ │ │ └── project.pbxproj │ │ ├── __tests__/ │ │ │ ├── android/ │ │ │ │ ├── applyPatch.spec.js │ │ │ │ ├── isInstalled.spec.js │ │ │ │ ├── makeBuildPatch.spec.js │ │ │ │ ├── makeImportPatch.spec.js │ │ │ │ ├── makePackagePatch.spec.js │ │ │ │ ├── makeSettingsPatch.spec.js │ │ │ │ └── makeStringsPatch.spec.js │ │ │ ├── getDependencyConfig.spec.js │ │ │ ├── getProjectDependencies.spec.js │ │ │ ├── groupFilesByType.spec.js │ │ │ ├── ios/ │ │ │ │ ├── addFileToProject.spec.js │ │ │ │ ├── addProjectToLibraries.spec.js │ │ │ │ ├── addSharedLibraries.spec.js │ │ │ │ ├── createGroup.spec.js │ │ │ │ ├── getBuildProperty.spec.js │ │ │ │ ├── getGroup.spec.js │ │ │ │ ├── getHeaderSearchPath.spec.js │ │ │ │ ├── getHeadersInFolder.spec.js │ │ │ │ ├── getPlist.spec.js │ │ │ │ ├── getPlistPath.spec.js │ │ │ │ ├── getProducts.spec.js │ │ │ │ ├── hasLibraryImported.spec.js │ │ │ │ ├── isInstalled.spec.js │ │ │ │ ├── mapHeaderSearchPaths.spec.js │ │ │ │ ├── removeProjectFromLibraries.js │ │ │ │ ├── removeProjectFromProject.spec.js │ │ │ │ ├── removeSharedLibrary.spec.js │ │ │ │ └── writePlist.spec.js │ │ │ ├── link.spec.js │ │ │ ├── pods/ │ │ │ │ ├── findLineToAddPod.spec.js │ │ │ │ ├── findMarkedLinesInPodfile.spec.js │ │ │ │ ├── findPodTargetLine.spec.js │ │ │ │ ├── isInstalled.spec.js │ │ │ │ └── removePodEntry.spec.js │ │ │ └── promiseWaterfall.spec.js │ │ ├── android/ │ │ │ ├── copyAssets.js │ │ │ ├── fs.js │ │ │ ├── isInstalled.js │ │ │ ├── patches/ │ │ │ │ ├── applyParams.js │ │ │ │ ├── applyPatch.js │ │ │ │ ├── makeBuildPatch.js │ │ │ │ ├── makeImportPatch.js │ │ │ │ ├── makePackagePatch.js │ │ │ │ ├── makeSettingsPatch.js │ │ │ │ ├── makeStringsPatch.js │ │ │ │ └── revokePatch.js │ │ │ ├── registerNativeModule.js │ │ │ ├── unlinkAssets.js │ │ │ └── unregisterNativeModule.js │ │ ├── commandStub.js │ │ ├── getDependencyConfig.js │ │ ├── getProjectDependencies.js │ │ ├── groupFilesByType.js │ │ ├── ios/ │ │ │ ├── addFileToProject.js │ │ │ ├── addProjectToLibraries.js │ │ │ ├── addSharedLibraries.js │ │ │ ├── addToHeaderSearchPaths.js │ │ │ ├── copyAssets.js │ │ │ ├── createGroup.js │ │ │ ├── createGroupWithMessage.js │ │ │ ├── getBuildProperty.js │ │ │ ├── getGroup.js │ │ │ ├── getHeaderSearchPath.js │ │ │ ├── getHeadersInFolder.js │ │ │ ├── getPlist.js │ │ │ ├── getPlistPath.js │ │ │ ├── getProducts.js │ │ │ ├── hasLibraryImported.js │ │ │ ├── isInstalled.js │ │ │ ├── mapHeaderSearchPaths.js │ │ │ ├── registerNativeModule.js │ │ │ ├── removeFromHeaderSearchPaths.js │ │ │ ├── removeFromPbxItemContainerProxySection.js │ │ │ ├── removeFromPbxReferenceProxySection.js │ │ │ ├── removeFromProjectReferences.js │ │ │ ├── removeFromStaticLibraries.js │ │ │ ├── removeProductGroup.js │ │ │ ├── removeProjectFromLibraries.js │ │ │ ├── removeProjectFromProject.js │ │ │ ├── removeSharedLibraries.js │ │ │ ├── unlinkAssets.js │ │ │ ├── unregisterNativeModule.js │ │ │ └── writePlist.js │ │ ├── link.js │ │ ├── pods/ │ │ │ ├── addPodEntry.js │ │ │ ├── findLineToAddPod.js │ │ │ ├── findMarkedLinesInPodfile.js │ │ │ ├── findPodTargetLine.js │ │ │ ├── isInstalled.js │ │ │ ├── readPodfile.js │ │ │ ├── registerNativeModule.js │ │ │ ├── removePodEntry.js │ │ │ ├── savePodFile.js │ │ │ └── unregisterNativeModule.js │ │ ├── pollParams.js │ │ ├── promiseWaterfall.js │ │ ├── promisify.js │ │ ├── unlink.js │ │ └── windows/ │ │ ├── isInstalled.js │ │ ├── patches/ │ │ │ ├── applyParams.js │ │ │ ├── applyPatch.js │ │ │ ├── makePackagePatch.js │ │ │ ├── makeProjectPatch.js │ │ │ ├── makeSolutionPatch.js │ │ │ ├── makeUsingPatch.js │ │ │ └── revokePatch.js │ │ ├── registerNativeModule.js │ │ └── unregisterNativeModule.js │ ├── logAndroid/ │ │ └── logAndroid.js │ ├── logIOS/ │ │ └── logIOS.js │ ├── platform.js │ ├── runAndroid/ │ │ ├── adb.js │ │ └── runAndroid.js │ ├── runIOS/ │ │ ├── __tests__/ │ │ │ ├── findMatchingSimulator-test.js │ │ │ ├── findXcodeProject-test.js │ │ │ └── parseIOSDevicesList-test.js │ │ ├── findMatchingSimulator.js │ │ ├── findXcodeProject.js │ │ ├── parseIOSDevicesList.js │ │ └── runIOS.js │ ├── runMacOS/ │ │ ├── findXcodeProject.js │ │ └── runMacOS.js │ ├── server/ │ │ ├── checkNodeVersion.js │ │ ├── middleware/ │ │ │ ├── copyToClipBoardMiddleware.js │ │ │ ├── getDevToolsMiddleware.js │ │ │ ├── getFlowTypeCheckMiddleware.js │ │ │ ├── index.html │ │ │ ├── indexPage.js │ │ │ ├── loadRawBodyMiddleware.js │ │ │ ├── openStackFrameInEditorMiddleware.js │ │ │ ├── statusPageMiddleware.js │ │ │ ├── systraceProfileMiddleware.js │ │ │ └── unless.js │ │ ├── runServer.js │ │ ├── server.js │ │ └── util/ │ │ ├── attachWebsocketServer.js │ │ ├── copyToClipBoard.js │ │ ├── debugger-ui/ │ │ │ ├── DeltaPatcher.js │ │ │ ├── debuggerWorker.js │ │ │ ├── deltaUrlToBlobUrl.js │ │ │ └── index.html │ │ ├── external/ │ │ │ └── xsel │ │ ├── jsPackagerClient.js │ │ ├── launchChrome.js │ │ ├── launchEditor.js │ │ ├── messageSocket.js │ │ └── webSocketProxy.js │ ├── setup_env.bat │ ├── setup_env.sh │ ├── templates/ │ │ ├── HelloNavigation/ │ │ │ ├── App.js │ │ │ ├── README.md │ │ │ ├── components/ │ │ │ │ ├── KeyboardSpacer.js │ │ │ │ └── ListItem.js │ │ │ ├── dependencies.json │ │ │ ├── lib/ │ │ │ │ └── Backend.js │ │ │ └── views/ │ │ │ ├── HomeScreenTabNavigator.js │ │ │ ├── chat/ │ │ │ │ ├── ChatListScreen.js │ │ │ │ └── ChatScreen.js │ │ │ └── welcome/ │ │ │ ├── WelcomeScreen.js │ │ │ ├── WelcomeText.android.js │ │ │ └── WelcomeText.ios.js │ │ ├── HelloWorld/ │ │ │ ├── App.js │ │ │ ├── __tests__/ │ │ │ │ ├── App.js │ │ │ │ └── index.macos.js │ │ │ ├── _babelrc │ │ │ ├── _flowconfig │ │ │ ├── _gitattributes │ │ │ ├── _gitignore │ │ │ ├── _watchmanconfig │ │ │ ├── android/ │ │ │ │ └── app/ │ │ │ │ ├── build.gradle │ │ │ │ └── src/ │ │ │ │ └── main/ │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── helloworld/ │ │ │ │ └── MainApplication.java │ │ │ ├── index.js │ │ │ ├── ios/ │ │ │ │ └── HelloWorld/ │ │ │ │ ├── AppDelegate.m │ │ │ │ └── Images.xcassets/ │ │ │ │ └── Contents.json │ │ │ ├── macos/ │ │ │ │ ├── HelloWorld/ │ │ │ │ │ ├── AppDelegate.h │ │ │ │ │ ├── AppDelegate.m │ │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Info.plist │ │ │ │ │ └── main.m │ │ │ │ ├── HelloWorld-tvOS/ │ │ │ │ │ └── Info.plist │ │ │ │ ├── HelloWorld-tvOSTests/ │ │ │ │ │ └── Info.plist │ │ │ │ ├── HelloWorld.xcodeproj/ │ │ │ │ │ ├── project.pbxproj │ │ │ │ │ └── xcshareddata/ │ │ │ │ │ └── xcschemes/ │ │ │ │ │ ├── HelloWorld-tvOS.xcscheme │ │ │ │ │ └── HelloWorld.xcscheme │ │ │ │ └── HelloWorldTests/ │ │ │ │ ├── HelloWorldTests.m │ │ │ │ └── Info.plist │ │ │ └── rn-cli.config.js │ │ └── README.md │ ├── upgrade/ │ │ └── upgrade.js │ ├── util/ │ │ ├── Config.js │ │ ├── PackageManager.js │ │ ├── __mocks__/ │ │ │ └── log.js │ │ ├── __tests__/ │ │ │ └── findSymlinkedModules-test.js │ │ ├── assertRequiredOptions.js │ │ ├── copyAndReplace.js │ │ ├── findReactNativeScripts.js │ │ ├── findSymlinkedModules.js │ │ ├── findSymlinksPaths.js │ │ ├── isPackagerRunning.js │ │ ├── isValidPackageName.js │ │ ├── log.js │ │ ├── parseCommandLine.js │ │ ├── walk.js │ │ └── yarn.js │ └── wrong-react-native.js ├── package.json ├── react-native-git-upgrade/ │ ├── README.md │ ├── checks.js │ ├── cli.js │ ├── cliEntry.js │ ├── index.js │ ├── package.json │ └── yarn.js ├── react-native-macos-cli/ │ ├── index.js │ └── package.json ├── react.gradle ├── rn-cli.config.js ├── rn-get-polyfills.js ├── runXcodeTests.sh ├── scripts/ │ ├── android-e2e-test.js │ ├── bump-oss-version.js │ ├── circle-ci-android-setup.sh │ ├── e2e-sinopia.config.yml │ ├── e2e-test.sh │ ├── ios-configure-glog.sh │ ├── ios-install-third-party.sh │ ├── launchPackager.bat │ ├── launchPackager.command │ ├── macos-configure-folly.sh │ ├── macos-configure-glog.sh │ ├── macos-install-third-party.sh │ ├── objc-test-ios.sh │ ├── objc-test-macos.sh │ ├── objc-test-tvos.sh │ ├── objc-test.sh │ ├── packager.sh │ ├── process-podspecs.sh │ ├── publish-npm.js │ ├── react-native-xcode.sh │ ├── run-android-ci-instrumentation-tests.js │ ├── run-android-emulator.sh │ ├── run-android-local-integration-tests.sh │ ├── run-android-local-unit-tests.sh │ ├── run-ci-e2e-tests.js │ ├── run-instrumentation-tests-via-adb-shell.sh │ ├── sync-css-layout.sh │ ├── test-manual-e2e.sh │ ├── try-n-times.js │ ├── validate-android-device-env.sh │ ├── validate-android-sdk.sh │ ├── validate-android-test-env.sh │ └── versiontemplates/ │ ├── RCTVersion.h.template │ ├── ReactNativeVersion.java.template │ └── ReactNativeVersion.js.template ├── settings.gradle ├── setupBabel.js └── third-party-podspecs/ ├── DoubleConversion.podspec ├── Folly.podspec └── GLog.podspec ================================================ FILE CONTENTS ================================================ ================================================ FILE: .buckconfig ================================================ [android] target = Google Inc.:Google APIs:23 [download] max_number_of_retries = 3 [maven_repositories] central = https://repo1.maven.org/maven2 [alias] rntester = //RNTester/android/app:app ================================================ FILE: .circleci/config.yml ================================================ aliases: - &restore-node-cache keys: - v1-dependencies-{{ arch }}-{{ checksum "package.json" }} # Fallback in case checksum fails - v1-dependencies-{{ arch }}- - &save-node-cache paths: - node_modules key: v1-dependencies-{{ arch }}-{{ checksum "package.json" }} - &restore-cache-analysis keys: - v1-analysis-dependencies-{{ arch }}-{{ checksum "package.json" }}{{ checksum "danger/package.json" }} # Fallback in case checksum fails - v1-analysis-dependencies-{{ arch }}- - &save-cache-analysis paths: - danger/node_modules - node_modules key: v1-analysis-dependencies-{{ arch }}-{{ checksum "package.json" }}{{ checksum "danger/package.json" }} - &restore-cache-android-packages keys: - v2-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }} # Fallback in case checksum fails - v2-android-sdkmanager-packages-{{ arch }}- - &save-cache-android-packages paths: - /opt/android/sdk key: v2-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }} - &restore-cache-ndk keys: - v1-android-ndk-{{ arch }}-r10e-32-64 - &install-ndk | source scripts/circle-ci-android-setup.sh && getAndroidNDK - &save-cache-ndk paths: - /opt/ndk key: v1-android-ndk-{{ arch }}-r10e-32-64 - &restore-cache-buck keys: - v2-buck-{{ arch }}-v2017.11.16.01 - &save-cache-buck paths: - ~/buck key: v2-buck-{{ arch }}-v2017.11.16.01 - &restore-cache-watchman keys: - v1-watchman-{{ arch }}-v4.9.0 - &save-cache-watchman paths: - ~/watchman key: v1-watchman-{{ arch }}-v4.9.0 - &install-node-dependencies | npm install --no-package-lock --no-spin --no-progress - &install-buck | if [[ ! -e ~/buck ]]; then git clone https://github.com/facebook/buck.git ~/buck --branch v2017.11.16.01 --depth=1 fi cd ~/buck && ant buck --version - &install-node | curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - sudo apt-get install -y nodejs - &run-node-tests | npm test -- --maxWorkers=2 - &run-lint-checks | npm run lint - &run-flow-checks | npm run flow -- check - &filter-only-master-stable branches: only: - /.*-stable/ - master - &filter-only-stable branches: only: - /.*-stable/ - &filter-ignore-gh-pages branches: ignore: gh-pages - &filter-ignore-master-stable branches: ignore: - master - /.*-stable/ - gh-pages - &create-ndk-directory | if [[ ! -e /opt/ndk ]]; then sudo mkdir /opt/ndk fi sudo chown ${USER:=$(/usr/bin/id -run)}:$USER /opt/ndk # CircleCI does not support interpolating env variables in the environment # https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables - &configure-android-path | echo 'export PATH=${ANDROID_NDK}:~/react-native/gradle-2.9/bin:~/buck/bin:$PATH' >> $BASH_ENV source $BASH_ENV - &install-android-packages | source scripts/circle-ci-android-setup.sh && getAndroidSDK - &install-build-dependencies | sudo apt-get update -y sudo apt-get install ant autoconf automake g++ gcc libqt5widgets5 lib32z1 lib32stdc++6 make maven python-dev python3-dev qml-module-qtquick-controls qtdeclarative5-dev file -y - &build-android-app name: Build Android App command: | buck build ReactAndroid/src/main/java/com/facebook/react buck build ReactAndroid/src/main/java/com/facebook/react/shell - &wait-for-avd name: Wait for Android Virtual Device command: source scripts/circle-ci-android-setup.sh && waitForAVD - &check-js-bundle name: Check for JavaScript Bundle command: | if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then echo "JavaScript bundle missing, verify build-js-bundle step"; exit 1; else echo "JavaScript bundle found."; fi - &compile-native-libs name: Compile Native Libs for Unit and Integration Tests command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS -Pcom.android.build.threadPoolSize=1 no_output_timeout: 6m - &run-android-unit-tests name: Unit Tests command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS - &run-android-integration-tests name: Build and Install Test APK command: source scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS - &collect-android-test-results name: Collect Test Results command: | mkdir -p ~/junit/ find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ~/junit/ \; find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ~/junit/ \; when: always defaults: &defaults working_directory: ~/react-native android_defaults: &android_defaults <<: *defaults docker: - image: circleci/android:api-26-alpha resource_class: "large" environment: - TERM: "dumb" - ADB_INSTALL_TIMEOUT: 10 - _JAVA_OPTIONS: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap" - GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError"' - ANDROID_NDK: '/opt/ndk/android-ndk-r10e' - BUILD_THREADS: 2 version: 2 jobs: # Runs JavaScript lint and flow checks run-js-checks: <<: *defaults docker: - image: circleci/node:8 steps: - checkout - restore-cache: *restore-node-cache - run: *install-node-dependencies - save-cache: *save-node-cache - run: *run-lint-checks - run: *run-flow-checks # Runs JavaScript tests on Node 8 test-js-node-8: <<: *defaults docker: - image: circleci/node:8 steps: - checkout - restore-cache: *restore-node-cache - run: *install-node-dependencies - save-cache: *save-node-cache - run: *run-node-tests # Runs JavaScript tests on Node 6 test-js-node-6: <<: *defaults docker: - image: circleci/node:6 steps: - checkout - restore-cache: *restore-node-cache - run: *install-node-dependencies - save-cache: *save-node-cache - run: *run-node-tests # Runs unit tests on iOS devices test-objc-ios: <<: *defaults macos: xcode: "9.0" dependencies: pre: - xcrun instruments -w "iPhone 5s (10.3.1)" || true steps: - checkout - restore-cache: *restore-node-cache - run: *install-node-dependencies - save-cache: *save-node-cache - run: ./scripts/objc-test-ios.sh # Runs end to end tests test-objc-e2e: <<: *defaults macos: xcode: "9.0" dependencies: pre: - xcrun instruments -w "iPhone 5s (10.3.1)" || true steps: - checkout - restore-cache: *restore-node-cache - run: *install-node-dependencies - save-cache: *save-node-cache - run: node ./scripts/run-ci-e2e-tests.js --ios --js --retries 3; # # Checks podspec # test-podspec: # <<: *defaults # macos: # xcode: "9.0" # steps: # - checkout # - restore-cache: *restore-node-cache # - run: *install-node-dependencies # - save-cache: *save-node-cache # - run: ./scripts/process-podspecs.sh # # # Publishes new version onto npm # deploy: # <<: *android_defaults # steps: # - checkout # # # Configure Android dependencies # - run: *configure-android-path # - run: *install-build-dependencies # - restore-cache: *restore-cache-android-packages # - run: *install-android-packages # - save-cache: *save-cache-android-packages # - run: *create-ndk-directory # - restore-cache: *restore-cache-ndk # - run: *install-ndk # - save-cache: *save-cache-ndk # - restore-cache: *restore-cache-buck # - run: *install-buck # - save-cache: *save-cache-buck # - run: *install-node # - restore-cache: *restore-node-cache # - run: *install-node-dependencies # - save-cache: *save-node-cache # - run: buck fetch ReactAndroid/src/test/java/com/facebook/react/modules # - run: buck fetch ReactAndroid/src/main/java/com/facebook/react # - run: buck fetch ReactAndroid/src/main/java/com/facebook/react/shell # - run: buck fetch ReactAndroid/src/test/... # - run: buck fetch ReactAndroid/src/androidTest/... # - run: ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders # # - run: # name: Publish React Native Package # command: | # if [ -z "$CIRCLE_PULL_REQUEST" ]; then # echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc # git config --global user.email "reactjs-bot@users.noreply.github.com" # git config --global user.name "npm Deployment Script" # echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc # node ./scripts/publish-npm.js # else # echo "Skipping deploy." # fi # Workflows enables us to run multiple jobs in parallel workflows: version: 2 build: jobs: # Run lint and flow checks - run-js-checks: filters: *filter-ignore-gh-pages # Test JavaScript on Node 8 and 6 - test-js-node-8: filters: *filter-ignore-gh-pages - test-js-node-6: filters: *filter-ignore-gh-pages # Test iOS & tvOS - test-objc-ios: filters: *filter-ignore-gh-pages - test-objc-e2e: filters: *filter-ignore-gh-pages # # If we are on a stable branch, deploy to `npm` # - hold: # type: approval # - deploy: # filters: *filter-only-stable # requires: # - hold - analyze-pull-request: filters: *filter-ignore-master-stable ================================================ FILE: .editorconfig ================================================ # EditorConfig is awesome: http://EditorConfig.org # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true indent_style = space indent_size = 2 [*.gradle] indent_size = 4 ================================================ FILE: .eslintignore ================================================ # node_modules ignored by default **/staticBundle.js **/main.js Libraries/vendor/**/* Libraries/Renderer/* pr-inactivity-bookmarklet.js question-bookmarklet.js flow/ danger/ ================================================ FILE: .eslintrc ================================================ { "root": true, "parser": "babel-eslint", "ecmaFeatures": { "jsx": true }, "env": { "es6": true, "jest": true, }, "plugins": [ "eslint-comments", "flowtype", "prettier", "react", "jest" ], // Map from global var to bool specifying if it can be redefined "globals": { "__DEV__": true, "__dirname": false, "__fbBatchedBridgeConfig": false, "alert": false, "cancelAnimationFrame": false, "cancelIdleCallback": false, "clearImmediate": true, "clearInterval": false, "clearTimeout": false, "console": false, "document": false, "escape": false, "Event": false, "EventTarget": false, "exports": false, "fetch": false, "FormData": false, "global": false, "jest": false, "Map": true, "module": false, "navigator": false, "process": false, "Promise": true, "requestAnimationFrame": true, "requestIdleCallback": true, "require": false, "Set": true, "setImmediate": true, "setInterval": false, "setTimeout": false, "window": false, "XMLHttpRequest": false, "pit": false }, "rules": { // General // This must be disallowed in this repo because the minimum supported // version of node is 4 which doesn't support trailing commas. // Once the minimum supported version is 8 or greater this can be changed "comma-dangle": [2, { // disallow trailing commas in object literals "arrays": "ignore", "objects": "ignore", "imports": "ignore", "exports": "ignore", "functions": "never" }], "no-cond-assign": 1, // disallow assignment in conditional expressions "no-console": 0, // disallow use of console (off by default in the node environment) "no-const-assign": 2, // disallow assignment to const-declared variables "no-constant-condition": 0, // disallow use of constant expressions in conditions "no-control-regex": 1, // disallow control characters in regular expressions "no-debugger": 1, // disallow use of debugger "no-dupe-keys": 2, // disallow duplicate keys when creating object literals "no-empty": 0, // disallow empty statements "no-ex-assign": 1, // disallow assigning to the exception in a catch block "no-extra-boolean-cast": 1, // disallow double-negation boolean casts in a boolean context "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) "no-extra-semi": 1, // disallow unnecessary semicolons "no-func-assign": 1, // disallow overwriting functions written as function declarations "no-inner-declarations": 0, // disallow function or variable declarations in nested blocks "no-invalid-regexp": 1, // disallow invalid regular expression strings in the RegExp constructor "no-negated-in-lhs": 1, // disallow negation of the left operand of an in expression "no-obj-calls": 1, // disallow the use of object properties of the global object (Math and JSON) as functions "no-regex-spaces": 1, // disallow multiple spaces in a regular expression literal "no-reserved-keys": 0, // disallow reserved words being used as object literal keys (off by default) "no-sparse-arrays": 1, // disallow sparse arrays "no-unreachable": 2, // disallow unreachable statements after a return, throw, continue, or break statement "use-isnan": 1, // disallow comparisons with the value NaN "valid-jsdoc": 0, // Ensure JSDoc comments are valid (off by default) "valid-typeof": 1, // Ensure that the results of typeof are compared against a valid string // Best Practices // These are rules designed to prevent you from making mistakes. They either prescribe a better way of doing something or help you avoid footguns. "block-scoped-var": 0, // treat var statements as if they were block scoped (off by default) "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) "consistent-return": 0, // require return statements to either always or never specify values "curly": 1, // specify curly brace conventions for all control statements "default-case": 0, // require default case in switch statements (off by default) "dot-notation": 1, // encourages use of dot notation whenever possible "eqeqeq": [1, "allow-null"], // require the use of === and !== "guard-for-in": 0, // make sure for-in loops have an if statement (off by default) "no-alert": 1, // disallow the use of alert, confirm, and prompt "no-caller": 1, // disallow use of arguments.caller or arguments.callee "no-div-regex": 1, // disallow division operators explicitly at beginning of regular expression (off by default) "no-else-return": 0, // disallow else after a return in an if (off by default) "no-eq-null": 0, // disallow comparisons to null without a type-checking operator (off by default) "no-eval": 2, // disallow use of eval() "no-extend-native": 1, // disallow adding to native types "no-extra-bind": 1, // disallow unnecessary function binding "no-fallthrough": 1, // disallow fallthrough of case statements "no-floating-decimal": 1, // disallow the use of leading or trailing decimal points in numeric literals (off by default) "no-implied-eval": 1, // disallow use of eval()-like methods "no-labels": 1, // disallow use of labeled statements "no-iterator": 1, // disallow usage of __iterator__ property "no-lone-blocks": 1, // disallow unnecessary nested blocks "no-loop-func": 0, // disallow creation of functions within loops "no-multi-str": 0, // disallow use of multiline strings "no-native-reassign": 0, // disallow reassignments of native objects "no-new": 1, // disallow use of new operator when not part of the assignment or comparison "no-new-func": 2, // disallow use of new operator for Function object "no-new-wrappers": 1, // disallows creating new instances of String,Number, and Boolean "no-octal": 1, // disallow use of octal literals "no-octal-escape": 1, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; "no-proto": 1, // disallow usage of __proto__ property "no-redeclare": 0, // disallow declaring the same variable more then once "no-return-assign": 1, // disallow use of assignment in return statement "no-script-url": 1, // disallow use of javascript: urls. "no-self-compare": 1, // disallow comparisons where both sides are exactly the same (off by default) "no-sequences": 1, // disallow use of comma operator "no-unused-expressions": 0, // disallow usage of expressions in statement position "no-void": 1, // disallow use of void operator (off by default) "no-warning-comments": 0, // disallow usage of configurable warning terms in comments": 1, // e.g. TODO or FIXME (off by default) "no-with": 1, // disallow use of the with statement "radix": 1, // require use of the second argument for parseInt() (off by default) "semi-spacing": 1, // require a space after a semi-colon "vars-on-top": 0, // requires to declare all vars on top of their containing scope (off by default) "wrap-iife": 0, // require immediate function invocation to be wrapped in parentheses (off by default) "yoda": 1, // require or disallow Yoda conditions // Variables // These rules have to do with variable declarations. "no-catch-shadow": 1, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) "no-delete-var": 1, // disallow deletion of variables "no-label-var": 1, // disallow labels that share a name with a variable "no-shadow": 1, // disallow declaration of variables already declared in the outer scope "no-shadow-restricted-names": 1, // disallow shadowing of names such as arguments "no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block "no-undefined": 0, // disallow use of undefined variable (off by default) "no-undef-init": 1, // disallow use of undefined when initializing variables "no-unused-vars": [1, {"vars": "all", "args": "none"}], // disallow declaration of variables that are not used in the code "no-use-before-define": 0, // disallow use of variables before they are defined // Node.js // These rules are specific to JavaScript running on Node.js. "handle-callback-err": 1, // enforces error handling in callbacks (off by default) (on by default in the node environment) "no-mixed-requires": 1, // disallow mixing regular variable and require declarations (off by default) (on by default in the node environment) "no-new-require": 1, // disallow use of new operator with the require function (off by default) (on by default in the node environment) "no-path-concat": 1, // disallow string concatenation with __dirname and __filename (off by default) (on by default in the node environment) "no-process-exit": 0, // disallow process.exit() (on by default in the node environment) "no-restricted-modules": 1, // restrict usage of specified node modules (off by default) "no-sync": 0, // disallow use of synchronous methods (off by default) // ESLint Comments Plugin // The following rules are made available via `eslint-plugin-eslint-comments` 'eslint-comments/no-aggregating-enable': 1, // disallows eslint-enable comments for multiple eslint-disable comments 'eslint-comments/no-unlimited-disable': 1, // disallows eslint-disable comments without rule names 'eslint-comments/no-unused-disable': 1, // disallow disables that don't cover any errors 'eslint-comments/no-unused-enable': 1, // // disallow enables that don't enable anything or enable rules that weren't disabled // Flow Plugin // The following rules are made available via `eslint-plugin-flowtype` "flowtype/define-flow-type": 1, "flowtype/use-flow-type": 1, // Prettier Plugin // https://github.com/prettier/eslint-plugin-prettier "prettier/prettier": [2, "fb", "@format"], // Stylistic Issues // These rules are purely matters of style and are quite subjective. "key-spacing": 0, "keyword-spacing": 1, // enforce spacing before and after keywords "jsx-quotes": [1, "prefer-double"], // enforces the usage of double quotes for all JSX attribute values which doesn’t contain a double quote "comma-spacing": 0, "no-multi-spaces": 0, "brace-style": 0, // enforce one true brace style (off by default) "camelcase": 0, // require camel case names "consistent-this": 1, // enforces consistent naming when capturing the current execution context (off by default) "eol-last": 1, // enforce newline at the end of file, with no multiple empty lines "func-names": 0, // require function expressions to have a name (off by default) "func-style": 0, // enforces use of function declarations or expressions (off by default) "new-cap": 0, // require a capital letter for constructors "new-parens": 1, // disallow the omission of parentheses when invoking a constructor with no arguments "no-nested-ternary": 0, // disallow nested ternary expressions (off by default) "no-array-constructor": 1, // disallow use of the Array constructor 'no-empty-character-class': 1, // disallow the use of empty character classes in regular expressions "no-lonely-if": 0, // disallow if as the only statement in an else block (off by default) "no-new-object": 1, // disallow use of the Object constructor "no-spaced-func": 1, // disallow space between function identifier and application "no-ternary": 0, // disallow the use of ternary operators (off by default) "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines "no-underscore-dangle": 0, // disallow dangling underscores in identifiers "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation "quotes": [1, "single", "avoid-escape"], // specify whether double or single quotes should be used "quote-props": 0, // require quotes around object literal property names (off by default) "semi": 1, // require or disallow use of semicolons instead of ASI "sort-vars": 0, // sort variables within the same declaration block (off by default) "space-in-brackets": 0, // require or disallow spaces inside brackets (off by default) "space-in-parens": 0, // require or disallow spaces inside parentheses (off by default) "space-infix-ops": 1, // require spaces around operators "space-unary-ops": [1, { "words": true, "nonwords": false }], // require or disallow spaces before/after unary operators (words on by default, nonwords off by default) "max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested (off by default) "one-var": 0, // allow just one var statement per function (off by default) "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) // Legacy // The following rules are included for compatibility with JSHint and JSLint. While the names of the rules may not match up with the JSHint/JSLint counterpart, the functionality is the same. "max-depth": 0, // specify the maximum depth that blocks can be nested (off by default) "max-len": 0, // specify the maximum length of a line in your program (off by default) "max-params": 0, // limits the number of parameters that can be used in the function declaration. (off by default) "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) "no-bitwise": 1, // disallow use of bitwise operators (off by default) "no-plusplus": 0, // disallow use of unary operators, ++ and -- (off by default) // React Plugin // The following rules are made available via `eslint-plugin-react`. "react/display-name": 0, "react/jsx-boolean-value": 0, "react/jsx-no-comment-textnodes": 1, "react/jsx-no-duplicate-props": 2, "react/jsx-no-undef": 2, "react/jsx-sort-props": 0, "react/jsx-uses-react": 1, "react/jsx-uses-vars": 1, "react/no-did-mount-set-state": 1, "react/no-did-update-set-state": 1, "react/no-multi-comp": 0, "react/no-string-refs": 1, "react/no-unknown-property": 0, "react/prop-types": 0, "react/react-in-jsx-scope": 1, "react/self-closing-comp": 1, "react/wrap-multilines": 0, // Jest Plugin // The following rules are made available via `eslint-plugin-jest`. "jest/no-disabled-tests": 1, "jest/no-focused-tests": 1, "jest/no-identical-title": 1, "jest/valid-expect": 1, } } ================================================ FILE: .flowconfig ================================================ [ignore] ; We fork some components by platform .*/*[.]android.js .*/*[.]ios.js ; Ignore templates for 'react-native init' .*/local-cli/templates/.* ; Ignore the Dangerfile /danger/dangerfile.js ; Ignore "BUCK" generated dirs /\.buckd/ ; Ignore unexpected extra "@providesModule" .*/node_modules/.*/node_modules/fbjs/.* ; Ignore duplicate module providers ; For RN Apps installed via npm, "Libraries" folder is inside ; "node_modules/react-native" but in the source repo it is in the root .*/Libraries/react-native/React.js ; Ignore polyfills .*/Libraries/polyfills/.* ; Ignore metro .*/node_modules/metro/.* [include] [libs] Libraries/react-native/react-native-interface.js flow/ flow-github/ [options] emoji=true module.system=haste munge_underscores=true module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' module.name_mapper='react-native' -> 'react-native-macos' suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeState suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\) suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError [version] ^0.66.0 ================================================ FILE: .gitattributes ================================================ # Force LF line endings for Bash scripts. On Windows the rest of the source # files will typically have CR+LF endings (Git default on Windows), but Bash # scripts need to have LF endings to work (under Cygwin), thus override to force # that. gradlew text eol=lf *.sh text eol=lf ================================================ FILE: .github/ISSUE_TEMPLATE/bug.md ================================================ --- name: 🐛 Bug report labels: "bug" about: Create a report to help us improve --- ## 🐛 Bug Report (A clear and concise description of what the bug is.) ## To Reproduce Steps to reproduce the behavior: ## Expected Behavior (A clear and concise description of what you expected to happen.) ## Minimal Reproduction (For bugs that cannot be reproduced within RNTester, please provide a [minimal](https://stackoverflow.com/help/mcve) repository.) ## Environment - macOS v10.x.x - react-native-macos v0.x.x ================================================ FILE: .github/ISSUE_TEMPLATE/feature.md ================================================ --- name: 🚀 Feature Proposal labels: "proposal" about: Submit a proposal for a new feature --- ## 🚀 Feature Proposal (A clear and concise description of what the feature is.) ## Motivation (Please outline the motivation for the proposal.) ## Example (Please provide an example for how this feature would be used.) ================================================ FILE: .gitignore ================================================ # Xcode !**/*.xcodeproj !**/*.pbxproj !**/*.xcworkspacedata !**/*.xcsettings !**/*.xcscheme *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate project.xcworkspace # Gradle /build/ /RNTester/android/app/build/ /RNTester/android/app/gradle/ /RNTester/android/app/gradlew /RNTester/android/app/gradlew.bat /ReactAndroid/build/ # Buck .buckd buck-out /ReactAndroid/src/main/jni/prebuilt/lib/armeabi-v7a/ /ReactAndroid/src/main/jni/prebuilt/lib/x86/ /ReactAndroid/src/main/gen # Android .idea .gradle local.properties *.iml /android/ # Node node_modules *.log .nvm /danger/node_modules/ # OS X .DS_Store *.tgz # Test generated files /ReactAndroid/src/androidTest/assets/AndroidTestBundle.js *.js.meta /coverage /third-party ================================================ FILE: .npmignore ================================================ # rnpm /local-cli/rnpm /local-cli/server/middleware/heapCapture/bundle.js ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # [Open Source Code of Conduct](https://code.facebook.com/codeofconduct) This code of conduct outlines our expectations for participants within the **Facebook Open Source** community, as well as steps to reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community. Our open source community strives to: * **Be friendly and patient.** * **Be welcoming:** We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. * **Be considerate:** Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we’re a world-wide community, so you might not be communicating in someone else’s primary language. * **Be respectful:** Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. * **Be careful in the words that you choose:** we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren’t acceptable. This includes, but is not limited to: * Violent threats or language directed against another person. * Discriminatory jokes and language. * Posting sexually explicit or violent material. * Posting (or threatening to post) other people’s personally identifying information (“doxing”). * Personal insults, especially those using racist or sexist terms. * Unwelcome sexual attention. * Advocating for, or encouraging, any of the above behavior. * Repeated harassment of others. In general, if someone asks you to stop, then stop. * **When we disagree, try to understand why:** Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. * **Remember that we’re different.** The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. This code is not exhaustive or complete. It serves to distill our common understanding of a collaborative, shared environment, and goals. We expect it to be followed in spirit as much as in the letter. ## Diversity Statement We encourage everyone to participate and are committed to building a community for all. Although we may not be able to satisfy everyone, we all agree that everyone is equal. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong. Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected characteristics above, including participants with disabilities. ## Reporting Issues If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via opensource@fb.com. All reports will be handled with discretion. In your report please include: * Your contact information. * Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link. * Any additional information that may be helpful. After filing a report, a representative will contact you personally. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. A representative will then review the incident, follow up with any additional questions, and make a decision as to how to respond. We will respect confidentiality requests for the purpose of protecting victims of abuse. Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the representative may take any action they deem appropriate, up to and including a permanent ban from our community without warning. _This Code Of Conduct follows the [template](http://todogroup.org/opencodeofconduct/) established by the [TODO Group](http://todogroup.org/)._ ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to React Native React Native is one of Facebook's first open source projects that is both under very active development and is also being used to ship code to everybody using Facebook's mobile apps. If you're interested in contributing to React Native, hopefully this document makes the process for contributing clear. ## [Code of Conduct](https://code.facebook.com/codeofconduct) Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated. ## Get involved There are many ways to contribute to React Native, and many of them do not involve writing any code. Here's a few ideas to get started: * Simply start using React Native. Go through the [Getting Started](https://facebook.github.io/react-native/docs/getting-started.html) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](https://facebook.github.io/react-native/docs/contributing.html#reporting-new-issues). * Look through the [open issues](https://github.com/facebook/react-native/issues). Provide workarounds, ask for clarification, or suggest labels. Help [triage issues](https://facebook.github.io/react-native/docs/contributing.html#triaging-issues-and-pull-requests). * If you find an issue you would like to fix, [open a pull request](https://facebook.github.io/react-native/docs/contributing.html#your-first-pull-request). Issues tagged as [_Good first issue_](https://github.com/facebook/react-native/labels/Good%20first%20issue) are a good place to get started. * Read through the [React Native docs](https://facebook.github.io/react-native/docs/getting-started.html). If you find anything that is confusing or can be improved, you can make edits by clicking "Improve this page" at the bottom of most docs. * Browse [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native) and answer questions. This will help you get familiarized with common pitfalls or misunderstandings, which can be useful when contributing updates to the documentation. * Take a look at the [features requested](https://react-native.canny.io/feature-requests) by others in the community and consider opening a pull request if you see something you want to work on. Contributions are very welcome. If you think you need help planning your contribution, please hop into [#react-native](https://discord.gg/0ZcbPKXt5bZjGY5n) and let people know you're looking for a mentor. Core contributors to React Native meet monthly and post their meeting notes on the [React Native blog](https://facebook.github.io/react-native/blog). You can also find ad hoc discussions in the [React Native Core Contributors](https://www.facebook.com/groups/reactnativeoss/) Facebook group. ### Triaging issues and pull requests One great way you can contribute to the project without writing any code is to help triage issues and pull requests as they come in. * Ask for more information if the issue does not provide all the details required by the template. * Suggest [labels](https://github.com/facebook/react-native/labels) that can help categorize issues. * Flag issues that are stale or that should be closed. * Ask for test plans and review code. You can learn more about handling issues in the [maintainer's guide](docs/maintainers.html#handling-issues). ## Our development process Some of the core team will be working directly on [GitHub](https://github.com/facebook/react-native). These changes will be public from the beginning. Other changesets will come via a bridge with Facebook's internal source control. This is a necessity as it allows engineers at Facebook outside of the core team to move fast and contribute from an environment they are comfortable in. When a change made on GitHub is approved, it will first be imported into Facebook's internal source control. The change will eventually sync back to GitHub as a single commit once it has passed all internal tests. ### Branch organization We will do our best to keep `master` in good shape, with tests passing at all times. But in order to move fast, we will make API changes that your application might not be compatible with. We will do our best to [communicate these changes](https://github.com/facebook/react-native/releases) and version appropriately so you can lock into a specific version if need be. To see what changes are coming and provide better feedback to React Native contributors, use the [latest release candidate](https://facebook.github.io/react-native/versions.html) when possible. By the time a release candidate is released, the changes it contains will have been shipped in production Facebook apps for over two weeks. ## Bugs We use [GitHub Issues](https://github.com/facebook/react-native/issues) for our public bugs. If you would like to report a problem, take a look around and see if someone already opened an issue about it. If you a are certain this is a new, unreported bug, you can submit a [bug report](https://facebook.github.io/react-native/docs/contributing.html#reporting-new-issues). If you have questions about using React Native, the [Community page](https://facebook.github.io/react-native/help.html) list various resources that should help you get started. We also have a [place where you can request features or enhancements](https://react-native.canny.io/feature-requests). If you see anything you'd like to be implemented, vote it up and explain your use case. ## Reporting new issues When [opening a new issue](https://github.com/facebook/react-native/issues/new), always make sure to fill out the [issue template](https://raw.githubusercontent.com/facebook/react-native/master/.github/ISSUE_TEMPLATE.md). **This step is very important!** Not doing so may result in your issue getting closed. Don't take this personally if this happens, and feel free to open a new issue once you've gathered all the information required by the template. * **One issue, one bug:** Please report a single bug per issue. * **Provide a Snack:** The best way to get attention on your issue is to provide a reduced test case. You can use [Snack](https://snack.expo.io/) to demonstrate the issue. * **Provide reproduction steps:** List all the steps necessary to reproduce the issue. Provide a Snack or upload a sample project to GitHub. The person reading your bug report should be able to follow these steps to reproduce your issue with minimal effort. * **Try out the latest version:** Verify that the issue can be reproduced locally by updating your project to use [React Native from `master`](https://facebook.github.io/react-native/versions.html). The bug may have already been fixed! We're not able to provide support through GitHub Issues. If you're looking for help with your code, consider asking on [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native) or reaching out to the community through [other channels](https://facebook.github.io/react-native/support.html). ### Security bugs Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. With that in mind, please do not file public issues; go through the process outlined on that page. ## Pull requests ### Your first pull request So you have decided to contribute code back to upstream by opening a pull request. You've invested a good chunk of time, and we appreciate it. We will do our best to work with you and get the PR looked at. Working on your first Pull Request? You can learn how from this free video series: [**How to Contribute to an Open Source Project on GitHub**](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) We have a list of [beginner friendly issues](https://github.com/facebook/react-native/labels/Good%20First%20Task) to help you get your feet wet in the React Native codebase and familiar with our contribution process. This is a great place to get started. ### Proposing a change If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, we have a [place to track feature requests](https://react-native.canny.io/feature-requests). If you intend to change the public API, or make any non-trivial changes to the implementation, we recommend [filing an issue](https://github.com/facebook/react-native/issues/new?title=%5BProposal%5D) that includes `[Proposal]` in the title. This lets us reach an agreement on your proposal before you put significant effort into it. These types of issues should be rare. If you have been contributing to the project long enough, you will probably already have access to the [React Native Core Contributors](https://www.facebook.com/groups/reactnativeoss/) Facebook Group, where this sort of discussion is usually held. If you're only fixing a bug, it's fine to submit a pull request right away but we still recommend to file an issue detailing what you're fixing. This is helpful in case we don't accept that specific fix but want to keep track of the issue. ### Sending a pull request Small pull requests are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. Please make sure the following is done when submitting a pull request: 1. Fork [the repository](https://github.com/facebook/react-native) and create your branch from `master`. 2. Add the copyright notice to the top of any new files you've added. 3. Describe your [**test plan**](https://facebook.github.io/react-native/docs/contributing.html#test-plan) in your pull request description. Make sure to [test your changes](https://facebook.github.io/react-native/docs/testing.html)! 4. Make sure your code lints (`npm run lint`). 5. If you haven't already, [sign the CLA](https://code.facebook.com/cla). All pull requests should be opened against the `master` branch. After opening your pull request, ensure [**all tests pass**](https://facebook.github.io/react-native/docs/contributing.html#contrinuous-integration-tests) on Circle CI. If a test fails and you believe it is unrelated to your change, leave a comment on the pull request explaining why. > **Note:** It is not necessary to keep clicking `Merge master to your branch` on the PR page. You would want to merge master if there are conflicts or tests are failing. The Facebook-GitHub-Bot ultimately squashes all commits to a single one before merging your PR. #### Test plan A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI. * If you've added code that should be tested, add tests! * If you've changed APIs, update the documentation. See [What is a Test Plan?](https://medium.com/@martinkonicek/what-is-a-test-plan-8bfc840ec171#.y9lcuqqi9) to learn more. #### Continuous integration tests Make sure all **tests pass** on [Circle CI][circle]. PRs that break tests are unlikely to be merged. Learn more about [testing your changes here](https://facebook.github.io/react-native/docs/testing.html). [circle]: https://circleci.com/gh/facebook/react-native #### Breaking changes When adding a new breaking change, follow this template in your pull request: ``` ### New breaking change here * **Who does this affect**: * **How to migrate**: * **Why make this breaking change**: * **Severity (number of people affected x effort)**: ``` If your pull request is merged, a core contributor will update the [list of breaking changes](https://github.com/facebook/react-native/wiki/Breaking-Changes) which is then used to populate the release notes. #### Copyright Notice for files Copy and paste this to the top of your new file(s): ```JS /** * 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. */ ``` If you've added a new module, add a `@providesModule ` at the end of the comment. This will allow the haste package manager to find it. #### Contributor License Agreement (CLA) In order to accept your pull request, we need you to submit a CLA. You only need to do this once, so if you've done this for another Facebook open source project, you're good to go. If you are submitting a pull request for the first time, the Facebook GitHub Bot will reply with a link to the CLA form. You may also [complete your CLA here](https://code.facebook.com/cla). ### What happens next? The core team will be monitoring for pull requests. Read [what to expect from maintainers](https://facebook.github.io/react-native/docs/maintainers.html#handling-pull-requests) to understand what may happen after you open a pull request. ## Style Guide Our linter will catch most styling issues that may exist in your code. You can check the status of your code styling by simply running `npm run lint`. However, there are still some styles that the linter cannot pick up. ### Code Conventions #### General * **Most important: Look around.** Match the style you see used in the rest of the project. This includes formatting, naming things in code, naming things in documentation. * Add trailing commas, * 2 spaces for indentation (no tabs) * "Attractive" #### JavaScript * Use semicolons; * ES6 standards * Prefer `'` over `"` * Do not use the optional parameters of `setTimeout` and `setInterval` * 80 character line length #### JSX * Prefer `"` over `'` for string literal props * When wrapping opening tags over multiple lines, place one prop per line * `{}` of props should hug their values (no spaces) * Place the closing `>` of opening tags on the same line as the last prop * Place the closing `/>` of self-closing tags on their own line and left-align them with the opening `<` #### Objective-C * Space after `@property` declarations * Brackets on *every* `if`, on the *same* line * `- method`, `@interface`, and `@implementation` brackets on the following line * *Try* to keep it around 80 characters line length (sometimes it's just not possible...) * `*` operator goes with the variable name (e.g. `NSObject *variableName;`) #### Java * If a method call spans multiple lines closing bracket is on the same line as the last argument. * If a method header doesn't fit on one line each argument goes on a separate line. * 100 character line length ### Documentation * Do not wrap lines at 80 characters - configure your editor to soft-wrap when editing documentation. ## License By contributing to React Native, you agree that your contributions will be licensed under its BSD license. ================================================ FILE: ContainerShip/Dockerfile.android ================================================ FROM hramos/android-base:latest # set default environment variables ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\"" ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8" # add ReactAndroid directory ADD .buckconfig /app/.buckconfig ADD .buckjavaargs /app/.buckjavaargs ADD ReactAndroid /app/ReactAndroid ADD ReactCommon /app/ReactCommon ADD keystores /app/keystores # set workdir WORKDIR /app # run buck fetches RUN buck fetch ReactAndroid/src/test/java/com/facebook/react/modules RUN buck fetch ReactAndroid/src/main/java/com/facebook/react RUN buck fetch ReactAndroid/src/main/java/com/facebook/react/shell RUN buck fetch ReactAndroid/src/test/... RUN buck fetch ReactAndroid/src/androidTest/... # build app RUN buck build ReactAndroid/src/main/java/com/facebook/react RUN buck build ReactAndroid/src/main/java/com/facebook/react/shell ADD gradle /app/gradle ADD gradlew /app/gradlew ADD settings.gradle /app/settings.gradle ADD build.gradle /app/build.gradle ADD react.gradle /app/react.gradle # run gradle downloads RUN ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders # compile native libs with Gradle script, we need bridge for unit and integration tests RUN ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1 -Pcom.android.build.threadPoolSize=1 # add all react-native code ADD . /app WORKDIR /app # https://github.com/npm/npm/issues/13306 RUN cd $(npm root -g)/npm && npm install fs-extra && sed -i -e s/graceful-fs/fs-extra/ -e s/fs.rename/fs.move/ ./lib/utils/rename.js # build node dependencies RUN npm install WORKDIR /app ================================================ FILE: ContainerShip/Dockerfile.android-base ================================================ FROM library/ubuntu:16.04 # set default build arguments ARG ANDROID_TOOLS_VERSION=25.2.5 ARG BUCK_VERSION=v2017.11.16.01 ARG NDK_VERSION=10e ARG NODE_VERSION=6.2.0 ARG WATCHMAN_VERSION=4.7.0 # set default environment variables ENV ADB_INSTALL_TIMEOUT=10 ENV PATH=${PATH}:/opt/buck/bin/ ENV ANDROID_HOME=/opt/android ENV ANDROID_SDK_HOME=${ANDROID_HOME} ENV PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools ENV ANDROID_NDK=/opt/ndk/android-ndk-r$NDK_VERSION ENV PATH=${PATH}:${ANDROID_NDK} # install system dependencies RUN apt-get update && apt-get install ant autoconf automake curl g++ gcc git libqt5widgets5 lib32z1 lib32stdc++6 make maven npm openjdk-8* python-dev python3-dev qml-module-qtquick-controls qtdeclarative5-dev unzip -y # configure npm RUN npm config set spin=false RUN npm config set progress=false # install node RUN npm install n -g RUN n $NODE_VERSION # download buck RUN git clone https://github.com/facebook/buck.git /opt/buck --branch $BUCK_VERSION --depth=1 WORKDIR /opt/buck # build buck RUN ant # download watchman RUN git clone https://github.com/facebook/watchman.git /opt/watchman WORKDIR /opt/watchman RUN git checkout v$WATCHMAN_VERSION # build watchman RUN ./autogen.sh RUN ./configure RUN make RUN make install # Full reference at https://dl.google.com/android/repository/repository2-1.xml # download and unpack android RUN mkdir /opt/android WORKDIR /opt/android RUN curl --silent https://dl.google.com/android/repository/tools_r$ANDROID_TOOLS_VERSION-linux.zip > android.zip RUN unzip android.zip RUN rm android.zip # download and unpack NDK RUN mkdir /opt/ndk WORKDIR /opt/ndk RUN curl --silent https://dl.google.com/android/repository/android-ndk-r$NDK_VERSION-linux-x86_64.zip > ndk.zip RUN unzip ndk.zip # cleanup NDK RUN rm ndk.zip # Add android SDK tools RUN echo "y" | sdkmanager "system-images;android-19;google_apis;armeabi-v7a" RUN echo "y" | sdkmanager "platforms;android-23" RUN echo "y" | sdkmanager "platforms;android-19" RUN echo "y" | sdkmanager "build-tools;23.0.1" RUN echo "y" | sdkmanager "add-ons;addon-google_apis-google-23" RUN echo "y" | sdkmanager "extras;android;m2repository" # Link adb executable RUN ln -s /opt/android/platform-tools/adb /usr/bin/adb # clean up unnecessary directories RUN rm -rf /opt/android/system-images/android-19/default/x86 ================================================ FILE: ContainerShip/Dockerfile.javascript ================================================ FROM library/node:6.9.2 ENV YARN_VERSION=0.27.5 # install dependencies RUN apt-get update && apt-get install ocaml libelf-dev -y RUN npm install yarn@$YARN_VERSION -g # add code RUN mkdir /app ADD . /app WORKDIR /app RUN yarn install --ignore-engines WORKDIR website RUN yarn install --ignore-engines --ignore-platform WORKDIR /app ================================================ FILE: ContainerShip/scripts/run-android-ci-instrumentation-tests.js ================================================ /** * 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'; /** * This script runs instrumentation tests one by one with retries * Instrumentation tests tend to be flaky, so rerunning them individually increases * chances for success and reduces total average execution time. * * We assume that all instrumentation tests are flat in one folder * Available arguments: * --path - path to all .java files with tests * --package - com.facebook.react.tests * --retries [num] - how many times to retry possible flaky commands: npm install and running tests, default 1 */ const argv = require('yargs').argv; const async = require('async'); const child_process = require('child_process'); const fs = require('fs'); const path = require('path'); const colors = { GREEN: '\x1b[32m', RED: '\x1b[31m', RESET: '\x1b[0m' }; const test_opts = { FILTER: new RegExp(argv.filter || '.*', 'i'), IGNORE: argv.ignore || null, PACKAGE: argv.package || 'com.facebook.react.tests', PATH: argv.path || './ReactAndroid/src/androidTest/java/com/facebook/react/tests', RETRIES: parseInt(argv.retries || 2, 10), TEST_TIMEOUT: parseInt(argv['test-timeout'] || 1000 * 60 * 10), OFFSET: argv.offset, COUNT: argv.count }; let max_test_class_length = Number.NEGATIVE_INFINITY; let testClasses = fs.readdirSync(path.resolve(process.cwd(), test_opts.PATH)) .filter((file) => { return file.endsWith('.java'); }).map((clazz) => { return path.basename(clazz, '.java'); }); if (test_opts.IGNORE) { test_opts.IGNORE = new RegExp(test_opts.IGNORE, 'i'); testClasses = testClasses.filter(className => { return !test_opts.IGNORE.test(className); }); } testClasses = testClasses.map((clazz) => { return test_opts.PACKAGE + '.' + clazz; }).filter((clazz) => { return test_opts.FILTER.test(clazz); }); // only process subset of the tests at corresponding offset and count if args provided if (test_opts.COUNT != null && test_opts.OFFSET != null) { const testCount = testClasses.length; const start = test_opts.COUNT * test_opts.OFFSET; const end = start + test_opts.COUNT; if (start >= testClasses.length) { testClasses = []; } else if (end >= testClasses.length) { testClasses = testClasses.slice(start); } else { testClasses = testClasses.slice(start, end); } } return async.mapSeries(testClasses, (clazz, callback) => { if (clazz.length > max_test_class_length) { max_test_class_length = clazz.length; } return async.retry(test_opts.RETRIES, (retryCb) => { const test_process = child_process.spawn('./ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh', [test_opts.PACKAGE, clazz], { stdio: 'inherit' }); const timeout = setTimeout(() => { test_process.kill(); }, test_opts.TEST_TIMEOUT); test_process.on('error', (err) => { clearTimeout(timeout); retryCb(err); }); test_process.on('exit', (code) => { clearTimeout(timeout); if (code !== 0) { return retryCb(new Error(`Process exited with code: ${code}`)); } return retryCb(); }); }, (err) => { return callback(null, { name: clazz, status: err ? 'failure' : 'success' }); }); }, (err, results) => { print_test_suite_results(results); const failures = results.filter((test) => { return test.status === 'failure'; }); return failures.length === 0 ? process.exit(0) : process.exit(1); }); function print_test_suite_results(results) { console.log('\n\nTest Suite Results:\n'); let color; let failing_suites = 0; let passing_suites = 0; function pad_output(num_chars) { let i = 0; while (i < num_chars) { process.stdout.write(' '); i++; } } results.forEach((test) => { if (test.status === 'success') { color = colors.GREEN; passing_suites++; } else if (test.status === 'failure') { color = colors.RED; failing_suites++; } process.stdout.write(color); process.stdout.write(test.name); pad_output((max_test_class_length - test.name.length) + 8); process.stdout.write(test.status); process.stdout.write(`${colors.RESET}\n`); }); console.log(`\n${passing_suites} passing, ${failing_suites} failing!`); } ================================================ FILE: ContainerShip/scripts/run-android-docker-instrumentation-tests.sh ================================================ #!/bin/bash # for buck gen mount -o remount,exec /dev/shm AVD_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1) # create virtual device echo no | android create avd -n $AVD_UUID -f -t android-19 --abi default/armeabi-v7a # emulator setup emulator64-arm -avd $AVD_UUID -no-skin -no-audio -no-window -no-boot-anim & bootanim="" until [[ "$bootanim" =~ "stopped" ]]; do sleep 5 bootanim=$(adb -e shell getprop init.svc.bootanim 2>&1) echo "boot animation status=$bootanim" done set -x # solve issue with max user watches limit echo 65536 | tee -a /proc/sys/fs/inotify/max_user_watches watchman shutdown-server # integration tests # build JS bundle for instrumentation tests node local-cli/cli.js bundle --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js # build test APK source ./scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1 # run installed apk with tests node ./ContainerShip/scripts/run-android-ci-instrumentation-tests.js $* exit $? ================================================ FILE: ContainerShip/scripts/run-android-docker-unit-tests.sh ================================================ #!/bin/bash # set default environment variables UNIT_TESTS_BUILD_THREADS="${UNIT_TESTS_BUILD_THREADS:-1}" # for buck gen mount -o remount,exec /dev/shm set -x # run unit tests buck test ReactAndroid/src/test/... --config build.threads=$UNIT_TESTS_BUILD_THREADS ================================================ FILE: ContainerShip/scripts/run-ci-e2e-tests.sh ================================================ #!/bin/bash set -ex # set default environment variables ROOT=$(pwd) SCRIPTS=$(pwd)/scripts RUN_ANDROID=0 RUN_CLI_INSTALL=1 RUN_IOS=0 RUN_JS=0 RETRY_COUNT=${RETRY_COUNT:-2} AVD_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1) ANDROID_NPM_DEPS="appium@1.5.1 mocha@2.4.5 wd@0.3.11 colors@1.0.3 pretty-data2@0.40.1" CLI_PACKAGE=$ROOT/react-native-cli/react-native-cli-*.tgz PACKAGE=$ROOT/react-native-*.tgz # solve issue with max user watches limit echo 65536 | tee -a /proc/sys/fs/inotify/max_user_watches watchman shutdown-server # retries command on failure # $1 -- max attempts # $2 -- command to run function retry() { local -r -i max_attempts="$1"; shift local -r cmd="$@" local -i attempt_num=1 until $cmd; do if (( attempt_num == max_attempts )); then echo "Execution of '$cmd' failed; no more attempts left" return 1 else (( attempt_num++ )) echo "Execution of '$cmd' failed; retrying for attempt number $attempt_num..." fi done } # parse command line args & flags while :; do case "$1" in --android) RUN_ANDROID=1 shift ;; --ios) RUN_IOS=1 shift ;; --js) RUN_JS=1 shift ;; --skip-cli-install) RUN_CLI_INSTALL=0 shift ;; --tvos) RUN_IOS=1 shift ;; *) break esac done function e2e_suite() { cd $ROOT if [ $RUN_ANDROID -eq 0 ] && [ $RUN_IOS -eq 0 ] && [ $RUN_JS -eq 0 ]; then echo "No e2e tests specified!" return 0 fi # create temp dir TEMP_DIR=$(mktemp -d /tmp/react-native-XXXXXXXX) # To make sure we actually installed the local version # of react-native, we will create a temp file inside the template # and check that it exists after `react-native init IOS_MARKER=$(mktemp $ROOT/local-cli/templates/HelloWorld/ios/HelloWorld/XXXXXXXX) ANDROID_MARKER=$(mktemp ${ROOT}/local-cli/templates/HelloWorld/android/XXXXXXXX) # install CLI cd react-native-cli npm pack cd .. # can skip cli install for non sudo mode if [ $RUN_CLI_INSTALL -ne 0 ]; then npm install -g $CLI_PACKAGE if [ $? -ne 0 ]; then echo "Could not install react-native-cli globally, please run in su mode" echo "Or with --skip-cli-install to skip this step" return 1 fi fi if [ $RUN_ANDROID -ne 0 ]; then set +ex # create virtual device if ! android list avd | grep "$AVD_UUID" > /dev/null; then echo no | android create avd -n $AVD_UUID -f -t android-19 --abi default/armeabi-v7a fi # newline at end of adb devices call and first line is headers DEVICE_COUNT=$(adb devices | wc -l) ((DEVICE_COUNT -= 2)) # will always kill an existing emulator if one exists for fresh setup if [[ $DEVICE_COUNT -ge 1 ]]; then adb emu kill fi # emulator setup emulator64-arm -avd $AVD_UUID -no-skin -no-audio -no-window -no-boot-anim & bootanim="" until [[ "$bootanim" =~ "stopped" ]]; do sleep 5 bootanim=$(adb -e shell getprop init.svc.bootanim 2>&1) echo "boot animation status=$bootanim" done set -ex ./gradlew :ReactAndroid:installArchives -Pjobs=1 -Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError" if [ $? -ne 0 ]; then echo "Failed to compile Android binaries" return 1 fi fi npm pack if [ $? -ne 0 ]; then echo "Failed to pack react-native" return 1 fi cd $TEMP_DIR retry $RETRY_COUNT react-native init EndToEndTest --version $PACKAGE --npm if [ $? -ne 0 ]; then echo "Failed to execute react-native init" echo "Most common reason is npm registry connectivity, try again" return 1 fi cd EndToEndTest # android tests if [ $RUN_ANDROID -ne 0 ]; then echo "Running an Android e2e test" echo "Installing e2e framework" retry $RETRY_COUNT npm install --save-dev $ANDROID_NPM_DEPS --silent >> /dev/null if [ $? -ne 0 ]; then echo "Failed to install appium" echo "Most common reason is npm registry connectivity, try again" return 1 fi cp $SCRIPTS/android-e2e-test.js android-e2e-test.js cd android echo "Downloading Maven deps" ./gradlew :app:copyDownloadableDepsToLibs cd .. keytool -genkey -v -keystore android/keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US" node ./node_modules/.bin/appium >> /dev/null & APPIUM_PID=$! echo "Starting appium server $APPIUM_PID" echo "Building app" buck build android/app # hack to get node unhung (kill buckd) kill -9 $(pgrep java) if [ $? -ne 0 ]; then echo "could not execute Buck build, is it installed and in PATH?" return 1 fi echo "Starting packager server" npm start >> /dev/null & SERVER_PID=$! sleep 15 echo "Executing android e2e test" retry $RETRY_COUNT node node_modules/.bin/_mocha android-e2e-test.js if [ $? -ne 0 ]; then echo "Failed to run Android e2e tests" echo "Most likely the code is broken" return 1 fi # kill packager process if kill -0 $SERVER_PID; then echo "Killing packager $SERVER_PID" kill -9 $SERVER_PID fi # kill appium process if kill -0 $APPIUM_PID; then echo "Killing appium $APPIUM_PID" kill -9 $APPIUM_PID fi fi # ios tests if [ $RUN_IOS -ne 0 ]; then echo "Running ios e2e tests not yet implemented for docker!" fi # js tests if [ $RUN_JS -ne 0 ]; then # Check the packager produces a bundle (doesn't throw an error) react-native bundle --max-workers 1 --platform android --dev true --entry-file index.js --bundle-output android-bundle.js if [ $? -ne 0 ]; then echo "Could not build android bundle" return 1 fi react-native bundle --max-workers 1 --platform ios --dev true --entry-file index.js --bundle-output ios-bundle.js if [ $? -ne 0 ]; then echo "Could not build iOS bundle" return 1 fi fi # directory cleanup rm $IOS_MARKER rm $ANDROID_MARKER return 0 } retry $RETRY_COUNT e2e_suite ================================================ FILE: ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh ================================================ #!/bin/bash # Python script to run instrumentation tests, copied from https://github.com/circleci/circle-dummy-android # Example: ./scripts/run-android-instrumentation-tests.sh com.facebook.react.tests com.facebook.react.tests.ReactPickerTestCase # export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH" # clear the logs adb logcat -c # run tests and check output python - $1 $2 << END import re import subprocess as sp import sys import threading import time done = False test_app = sys.argv[1] test_class = None if len(sys.argv) > 2: test_class = sys.argv[2] def update(): # prevent CircleCI from killing the process for inactivity while not done: time.sleep(5) print "Running in background. Waiting for 'adb' command response..." t = threading.Thread(target=update) t.dameon = True t.start() def run(): sp.Popen(['adb', 'wait-for-device']).communicate() if (test_class != None): p = sp.Popen('adb shell am instrument -w -e class %s %s/android.support.test.runner.AndroidJUnitRunner' % (test_class, test_app), shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE) else : p = sp.Popen('adb shell am instrument -w %s/android.support.test.runner.AndroidJUnitRunner' % (test_app), shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE) return p.communicate() success = re.compile(r'OK \(\d+ test(s)?\)') stdout, stderr = run() done = True print stderr print stdout if success.search(stderr + stdout): sys.exit(0) else: # dump the logs sp.Popen(['adb', 'logcat', '-d']).communicate() sys.exit(1) # make sure we fail if the test failed END RETVAL=$? exit $RETVAL ================================================ FILE: DockerTests.md ================================================ # Dockerfile Tests This is a high level overview of the test configuration using docker. It explains how to run the tests locally and how they integrate with the Jenkins Pipeline script to run the automated tests on ContainerShip . ## Docker Installation It is required to have Docker running on your machine in order to build and run the tests in the Dockerfiles. See for more information on how to install. ## Convenience NPM Run Scripts We have added a number of default run scripts to the `package.json` file to simplify building and running your tests. `npm run test-android-setup` - Pulls down the base android docker image used for running the tests `npm run test-android-build` - Builds the docker image used to run the tests `npm run test-android-run-unit` - Runs all the unit tests that have been built in the latest react/android docker image (note: you need to run test-android-build before executing this, if the image does not exist it will fail) `npm run test-android-run-instrumentation` - Runs all the instrumentation tests that have been built in the latest react/android docker image (note: you need to run test-android-build before executing this, if the image does not exist it will fail). You can also pass additional flags to filter which tests instrumentation tests are run. Ex: `npm run test-android-run-instrumentation -- --filter=TestIdTestCase` to only run the TestIdTestCase instrumentation test. See below for more information on the instrumentation test flags. `npm run test-android-run-e2e` - Runs all the end to end tests that have been built in the latest react/android docker image (note: you need to run test-android-build before executing this, if the image does not exist it will fail) `npm run test-android-unit` - Builds and runs the android unit tests. `npm run test-android-instrumentation` - Builds and runs the android instrumentation tests. `npm run test-android-e2e` - Builds and runs the android end to end tests. ## Detailed Android Setup There are two Dockerfiles for use with the Android codebase. The `Dockerfile.android-base` contains all the necessary prerequisites required to run the React Android tests. It is separated out into a separate Dockerfile because these are dependencies that rarely change and also because it is quite a beastly image since it contains all the Android depedencies for running android and the emulators (~9GB). The good news is you should rarely have to build or pull down the base image! All iterative code updates happen as part of the `Dockerfile.android` image build. So step one... `docker pull containership/android-base:latest` This will take quite some time depending on your connection and you need to ensure you have ~10GB of free disk space. Once this is done, you can run tests locally by executing two simple commands: 1. `docker build -t react/android -f ./ContainerShip/Dockerfile.android .` 2. `docker run --cap-add=SYS_ADMIN -it react/android bash ContainerShip/scripts/run-android-docker-unit-tests.sh` > Note: `--cap-add=SYS_ADMIN` flag is required for the `ContainerShip/scripts/run-android-docker-unit-tests.sh` and `ContainerShip/scripts/run-android-docker-instrumentation-tests.sh` in order to allow the remounting of `/dev/shm` as writeable so the `buck` build system may write temporary output to that location Every time you make any modifications to the codebase, you should re-run the `docker build ...` command in order for your updates to be included in your local docker image. The following shell scripts have been provided for android testing: `ContainerShip/scripts/run-android-docker-unit-tests.sh` - Runs the standard android unit tests `ContainerShip/scripts/run-android-docker-instrumentation-tests.sh` - Runs the android instrumentation tests on the emulator. *Note* that these tests take quite some time to run so there are various flags you can pass in order to filter which tests are run (see below) `ContainerShip/scripts/run-ci-e2e-tests.sh` - Runs the android end to end tests #### ContainerShip/scripts/run-android-docker-instrumentation-tests.sh The instrumentation test script accepts the following flags in order to customize the execution of the tests: `--filter` - A regex that filters which instrumentation tests will be run. (Defaults to .*) `--package` - Name of the java package containing the instrumentation tests (Defaults to com.facebook.react.tests) `--path` - Path to the directory containing the instrumentation tests. (Defaults to ./ReactAndroid/src/androidTest/java/com/facebook/react/tests) `--retries` - Number of times to retry a failed test before declaring a failure (Defaults to 2) For example, if locally you only wanted to run the InitialPropsTestCase, you could do the following: `docker run --cap-add=SYS_ADMIN -it react/android bash ContainerShip/scripts/run-android-docker-instrumentation-tests.sh --filter="InitialPropsTestCase"` # Javascript Setup There is a single Dockerfile for use with the javascript codebase. The `Dockerfile.javascript` base requires all the necessary dependencies for running Javascript tests. Any time you make an update to the codebase, you can build and run the javascript tests with the following three commands: 1. `docker build -t react/js -f ./ContainerShip/Dockerfile.javascript .` 2. `docker run -it react/js yarn test --maxWorkers=4` 3. `docker run -it react/js yarn run flow -- check` ================================================ FILE: IntegrationTests/AccessibilityManagerTest.js ================================================ /** * 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. * * @flow * @providesModule AccessibilityManagerTest */ 'use strict'; const React = require('react'); const ReactNative = require('react-native'); const { View } = ReactNative; const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); const { TestModule, AccessibilityManager, } = ReactNative.NativeModules; class AccessibilityManagerTest extends React.Component<{}> { componentDidMount() { AccessibilityManager.setAccessibilityContentSizeMultipliers({ 'extraSmall': 1.0, 'small': 2.0, 'medium': 3.0, 'large': 4.0, 'extraLarge': 5.0, 'extraExtraLarge': 6.0, 'extraExtraExtraLarge': 7.0, 'accessibilityMedium': 8.0, 'accessibilityLarge': 9.0, 'accessibilityExtraLarge': 10.0, 'accessibilityExtraExtraLarge': 11.0, 'accessibilityExtraExtraExtraLarge': 12.0, }); RCTDeviceEventEmitter.addListener('didUpdateDimensions', update => { TestModule.markTestPassed(update.window.fontScale === 4.0); }); } render(): React.Node { return ; } } AccessibilityManagerTest.displayName = 'AccessibilityManagerTest'; module.exports = AccessibilityManagerTest; ================================================ FILE: IntegrationTests/AppEventsTest.js ================================================ /** * 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 AppEventsTest * @flow */ 'use strict'; var React = require('React'); var ReactNative = require('react-native'); var { NativeAppEventEmitter, StyleSheet, Text, View, } = ReactNative; var { TestModule } = ReactNative.NativeModules; var deepDiffer = require('deepDiffer'); var TEST_PAYLOAD = {foo: 'bar'}; type AppEvent = { data: Object, ts: number, }; type State = { sent: 'none' | AppEvent, received: 'none' | AppEvent, elapsed?: string, }; class AppEventsTest extends React.Component<{}, State> { state: State = {sent: 'none', received: 'none'}; componentDidMount() { NativeAppEventEmitter.addListener('testEvent', this.receiveEvent); var event = {data: TEST_PAYLOAD, ts: Date.now()}; TestModule.sendAppEvent('testEvent', event); this.setState({sent: event}); } receiveEvent = (event: any) => { if (deepDiffer(event.data, TEST_PAYLOAD)) { throw new Error('Received wrong event: ' + JSON.stringify(event)); } var elapsed = (Date.now() - event.ts) + 'ms'; this.setState({received: event, elapsed}, () => { TestModule.markTestCompleted(); }); }; render() { return ( {JSON.stringify(this.state, null, ' ')} ); } } AppEventsTest.displayName = 'AppEventsTest'; var styles = StyleSheet.create({ container: { margin: 40, }, }); module.exports = AppEventsTest; ================================================ FILE: IntegrationTests/AsyncStorageTest.js ================================================ /** * 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. * * @flow * @providesModule AsyncStorageTest */ 'use strict'; var React = require('React'); var ReactNative = require('react-native'); var { AsyncStorage, Text, View, } = ReactNative; var { TestModule } = ReactNative.NativeModules; var deepDiffer = require('deepDiffer'); var DEBUG = false; var KEY_1 = 'key_1'; var VAL_1 = 'val_1'; var KEY_2 = 'key_2'; var VAL_2 = 'val_2'; var KEY_MERGE = 'key_merge'; var VAL_MERGE_1 = {'foo': 1, 'bar': {'hoo': 1, 'boo': 1}, 'moo': {'a': 3}}; var VAL_MERGE_2 = {'bar': {'hoo': 2}, 'baz': 2, 'moo': {'a': 3}}; var VAL_MERGE_EXPECT = {'foo': 1, 'bar': {'hoo': 2, 'boo': 1}, 'baz': 2, 'moo': {'a': 3}}; // setup in componentDidMount var done = (result : ?boolean) => {}; var updateMessage = (message : string ) => {}; function runTestCase(description : string, fn) { updateMessage(description); fn(); } function expectTrue(condition : boolean, message : string) { if (!condition) { throw new Error(message); } } function expectEqual(lhs, rhs, testname : string) { expectTrue( !deepDiffer(lhs, rhs), 'Error in test ' + testname + ': expected\n' + JSON.stringify(rhs) + '\ngot\n' + JSON.stringify(lhs) ); } function expectAsyncNoError(place, err) { if (err instanceof Error) { err = err.message; } expectTrue(err === null, 'Unexpected error in ' + place + ': ' + JSON.stringify(err)); } function testSetAndGet() { AsyncStorage.setItem(KEY_1, VAL_1, (err1) => { expectAsyncNoError('testSetAndGet/setItem', err1); AsyncStorage.getItem(KEY_1, (err2, result) => { expectAsyncNoError('testSetAndGet/getItem', err2); expectEqual(result, VAL_1, 'testSetAndGet setItem'); updateMessage('get(key_1) correctly returned ' + result); runTestCase('should get null for missing key', testMissingGet); }); }); } function testMissingGet() { AsyncStorage.getItem(KEY_2, (err, result) => { expectAsyncNoError('testMissingGet/setItem', err); expectEqual(result, null, 'testMissingGet'); updateMessage('missing get(key_2) correctly returned ' + result); runTestCase('check set twice results in a single key', testSetTwice); }); } function testSetTwice() { AsyncStorage.setItem(KEY_1, VAL_1, ()=>{ AsyncStorage.setItem(KEY_1, VAL_1, ()=>{ AsyncStorage.getItem(KEY_1, (err, result) => { expectAsyncNoError('testSetTwice/setItem', err); expectEqual(result, VAL_1, 'testSetTwice'); updateMessage('setTwice worked as expected'); runTestCase('test removeItem', testRemoveItem); }); }); }); } function testRemoveItem() { AsyncStorage.setItem(KEY_1, VAL_1, ()=>{ AsyncStorage.setItem(KEY_2, VAL_2, ()=>{ AsyncStorage.getAllKeys((err, result) => { expectAsyncNoError('testRemoveItem/getAllKeys', err); expectTrue( result.indexOf(KEY_1) >= 0 && result.indexOf(KEY_2) >= 0, 'Missing KEY_1 or KEY_2 in ' + '(' + result + ')' ); updateMessage('testRemoveItem - add two items'); AsyncStorage.removeItem(KEY_1, (err2) => { expectAsyncNoError('testRemoveItem/removeItem', err2); updateMessage('delete successful '); AsyncStorage.getItem(KEY_1, (err3, result2) => { expectAsyncNoError('testRemoveItem/getItem', err3); expectEqual( result2, null, 'testRemoveItem: key_1 present after delete' ); updateMessage('key properly removed '); AsyncStorage.getAllKeys((err4, result3) => { expectAsyncNoError('testRemoveItem/getAllKeys', err4); expectTrue( result3.indexOf(KEY_1) === -1, 'Unexpected: KEY_1 present in ' + result3 ); updateMessage('proper length returned.'); runTestCase('should merge values', testMerge); }); }); }); }); }); }); } function testMerge() { AsyncStorage.setItem(KEY_MERGE, JSON.stringify(VAL_MERGE_1), (err1) => { expectAsyncNoError('testMerge/setItem', err1); AsyncStorage.mergeItem(KEY_MERGE, JSON.stringify(VAL_MERGE_2), (err2) => { expectAsyncNoError('testMerge/mergeItem', err2); AsyncStorage.getItem(KEY_MERGE, (err3, result) => { expectAsyncNoError('testMerge/setItem', err3); expectEqual(JSON.parse(result), VAL_MERGE_EXPECT, 'testMerge'); updateMessage('objects deeply merged\nDone!'); runTestCase('multi set and get', testOptimizedMultiGet); }); }); }); } function testOptimizedMultiGet() { let batch = [[KEY_1, VAL_1], [KEY_2, VAL_2]]; let keys = batch.map(([key, value]) => key); AsyncStorage.multiSet(batch, (err1) => { // yes, twice on purpose [1, 2].forEach((i) => { expectAsyncNoError(`${i} testOptimizedMultiGet/multiSet`, err1); AsyncStorage.multiGet(keys, (err2, result) => { expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2); expectEqual(result, batch, `${i} testOptimizedMultiGet multiGet`); updateMessage('multiGet([key_1, key_2]) correctly returned ' + JSON.stringify(result)); done(); }); }); }); } class AsyncStorageTest extends React.Component<{}, $FlowFixMeState> { state = { messages: 'Initializing...', done: false, }; componentDidMount() { done = () => this.setState({done: true}, () => { TestModule.markTestCompleted(); }); updateMessage = (msg) => { this.setState({messages: this.state.messages.concat('\n' + msg)}); DEBUG && console.log(msg); }; AsyncStorage.clear(testSetAndGet); } render() { return ( { /* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This * comment suppresses an error found when Flow v0.54 was deployed. * To see the error delete this comment and run Flow. */ this.constructor.displayName + ': '} {this.state.done ? 'Done' : 'Testing...'} {'\n\n' + this.state.messages} ); } } AsyncStorageTest.displayName = 'AsyncStorageTest'; module.exports = AsyncStorageTest; ================================================ FILE: IntegrationTests/ImageCachePolicyTest.js ================================================ /** * 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. * * @flow * @providesModule ImageCachePolicyTest */ 'use strict'; var React = require('react'); var ReactNative = require('react-native'); var { Image, View, Text, StyleSheet, } = ReactNative; var { TestModule } = ReactNative.NativeModules; /* * The reload and force-cache tests don't actually verify that the complete functionality. * * reload: Should have the server set a long cache header, then swap the image on next load * with the test comparing the old image to the new image and making sure they are different. * * force-cache: Should do the above but set a no-cache header. The test should compare the first * image with the new one and make sure they are the same. */ const TESTS = ['only-if-cached', 'default', 'reload', 'force-cache']; type Props = {} type State = { 'only-if-cached'?: boolean, 'default'?: boolean, 'reload'?: boolean, 'force-cache'?: boolean, } class ImageCachePolicyTest extends React.Component { state = {} shouldComponentUpdate(nextProps: Props, nextState: State) { const results: Array = TESTS.map(x => nextState[x]); if (!results.includes(undefined)) { const result: boolean = results.reduce((x,y) => x === y === true, true); TestModule.markTestPassed(result); } return false; } testComplete(name: string, pass: boolean) { this.setState({[name]: pass}); } render() { return ( Hello this.testComplete('only-if-cached', false)} onError={() => this.testComplete('only-if-cached', true)} style={styles.base} /> this.testComplete('default', true)} onError={() => this.testComplete('default', false)} style={styles.base} /> this.testComplete('reload', true)} onError={() => this.testComplete('reload', false)} style={styles.base} /> this.testComplete('force-cache', true)} onError={() => this.testComplete('force-cache', false)} style={styles.base} /> ); } } const styles = StyleSheet.create({ base: { width: 100, height: 100, }, }); ImageCachePolicyTest.displayName = 'ImageCachePolicyTest'; module.exports = ImageCachePolicyTest; ================================================ FILE: IntegrationTests/ImageSnapshotTest.js ================================================ /** * 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. * * @flow * @providesModule ImageSnapshotTest */ 'use strict'; var React = require('React'); var ReactNative = require('react-native'); var { Image, View, } = ReactNative; var { TestModule } = ReactNative.NativeModules; class ImageSnapshotTest extends React.Component<{}> { componentDidMount() { if (!TestModule.verifySnapshot) { throw new Error('TestModule.verifySnapshot not defined.'); } } done = (success : boolean) => { TestModule.markTestPassed(success); }; render() { return ( TestModule.verifySnapshot(this.done)} /> ); } } ImageSnapshotTest.displayName = 'ImageSnapshotTest'; module.exports = ImageSnapshotTest; ================================================ FILE: IntegrationTests/IntegrationTestHarnessTest.js ================================================ /** * 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. * * @flow * @providesModule IntegrationTestHarnessTest */ 'use strict'; /* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error * found when Flow v0.54 was deployed. To see the error delete this comment and * run Flow. */ var requestAnimationFrame = require('fbjs/lib/requestAnimationFrame'); var React = require('react'); var PropTypes = require('prop-types'); var ReactNative = require('react-native'); var { Text, View } = ReactNative; var { TestModule } = ReactNative.NativeModules; class IntegrationTestHarnessTest extends React.Component<{ shouldThrow?: boolean, waitOneFrame?: boolean, }, $FlowFixMeState> { static propTypes = { shouldThrow: PropTypes.bool, waitOneFrame: PropTypes.bool, }; state = { done: false, }; componentDidMount() { if (this.props.waitOneFrame) { requestAnimationFrame(this.runTest); } else { this.runTest(); } } runTest = () => { if (this.props.shouldThrow) { throw new Error('Throwing error because shouldThrow'); } if (!TestModule) { throw new Error('RCTTestModule is not registered.'); } else if (!TestModule.markTestCompleted) { throw new Error('RCTTestModule.markTestCompleted not defined.'); } this.setState({ done: true }, () => { TestModule.markTestCompleted(); }); }; render() { return ( { /* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This * comment suppresses an error found when Flow v0.54 was deployed. * To see the error delete this comment and run Flow. */ this.constructor.displayName + ': '} {this.state.done ? 'Done' : 'Testing...'} ); } } IntegrationTestHarnessTest.displayName = 'IntegrationTestHarnessTest'; module.exports = IntegrationTestHarnessTest; ================================================ FILE: IntegrationTests/IntegrationTestsApp.js ================================================ /** * 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. * * @flow * @providesModule IntegrationTestsApp */ 'use strict'; var React = require('React'); var ReactNative = require('react-native'); var { AppRegistry, ScrollView, StyleSheet, Text, TouchableOpacity, View, } = ReactNative; // Keep this list in sync with RNTesterIntegrationTests.m var TESTS = [ require('./IntegrationTestHarnessTest'), require('./TimersTest'), require('./AsyncStorageTest'), require('./LayoutEventsTest'), require('./AppEventsTest'), require('./SimpleSnapshotTest'), require('./ImageCachePolicyTest'), require('./ImageSnapshotTest'), require('./PromiseTest'), require('./WebViewTest'), require('./SyncMethodTest'), require('./WebSocketTest'), require('./AccessibilityManagerTest'), ]; TESTS.forEach( /* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This comment * suppresses an error found when Flow v0.54 was deployed. To see the error * delete this comment and run Flow. */ (test) => AppRegistry.registerComponent(test.displayName, () => test) ); // Modules required for integration tests require('LoggingTestModule'); type Test = any; class IntegrationTestsApp extends React.Component<{}, $FlowFixMeState> { state = { test: (null: ?Test), }; render() { if (this.state.test) { return ( {/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This * comment suppresses an error when upgrading Flow's support for * React. To see the error delete this comment and run Flow. */} ); } return ( Click on a test to run it in this shell for easier debugging and development. Run all tests in the testing environment with cmd+U in Xcode. {TESTS.map((test) => [ this.setState({test})} style={styles.row}> {test.displayName} , ])} ); } } var styles = StyleSheet.create({ container: { backgroundColor: 'white', marginTop: 40, margin: 15, }, row: { padding: 10, }, testName: { fontWeight: '500', }, separator: { height: 1, backgroundColor: '#bbbbbb', }, }); AppRegistry.registerComponent('IntegrationTestsApp', () => IntegrationTestsApp); ================================================ FILE: IntegrationTests/LayoutEventsTest.js ================================================ /** * 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 LayoutEventsTest * @flow */ 'use strict'; var React = require('react'); var createReactClass = require('create-react-class'); var ReactNative = require('react-native'); var { Image, LayoutAnimation, StyleSheet, Text, View, } = ReactNative; var { TestModule } = ReactNative.NativeModules; var deepDiffer = require('deepDiffer'); function debug(...args) { // console.log.apply(null, arguments); } import type {Layout, LayoutEvent} from 'CoreEventTypes'; type Style = { margin?: number, padding?: number, borderColor?: string, borderWidth?: number, backgroundColor?: string, width?: number, }; type State = { didAnimation: boolean, extraText?: string, imageLayout?: Layout, textLayout?: Layout, viewLayout?: Layout, viewStyle?: Style, containerStyle?: Style, }; var LayoutEventsTest = createReactClass({ displayName: 'LayoutEventsTest', getInitialState(): State { return { didAnimation: false, }; }, animateViewLayout: function() { debug('animateViewLayout invoked'); LayoutAnimation.configureNext( LayoutAnimation.Presets.spring, () => { debug('animateViewLayout done'); this.checkLayout(this.addWrapText); } ); this.setState({viewStyle: {margin: 60}}); }, addWrapText: function() { debug('addWrapText invoked'); this.setState( {extraText: ' And a bunch more text to wrap around a few lines.'}, () => this.checkLayout(this.changeContainer) ); }, changeContainer: function() { debug('changeContainer invoked'); this.setState( {containerStyle: {width: 280}}, () => this.checkLayout(TestModule.markTestCompleted) ); }, checkLayout: function(next?: ?Function) { if (!this.isMounted()) { return; } this.refs.view.measure((x, y, width, height) => { this.compare('view', {x, y, width, height}, this.state.viewLayout); if (typeof next === 'function') { next(); } else if (!this.state.didAnimation) { // Trigger first state change after onLayout fires this.animateViewLayout(); this.state.didAnimation = true; } }); this.refs.txt.measure((x, y, width, height) => { this.compare('txt', {x, y, width, height}, this.state.textLayout); }); this.refs.img.measure((x, y, width, height) => { this.compare('img', {x, y, width, height}, this.state.imageLayout); }); }, compare: function(node: string, measured: any, onLayout: any): void { if (deepDiffer(measured, onLayout)) { var data = {measured, onLayout}; throw new Error( node + ' onLayout mismatch with measure ' + JSON.stringify(data, null, ' ') ); } }, onViewLayout: function(e: LayoutEvent) { debug('received view layout event\n', e.nativeEvent); this.setState({viewLayout: e.nativeEvent.layout}, this.checkLayout); }, onTextLayout: function(e: LayoutEvent) { debug('received text layout event\n', e.nativeEvent); this.setState({textLayout: e.nativeEvent.layout}, this.checkLayout); }, onImageLayout: function(e: LayoutEvent) { debug('received image layout event\n', e.nativeEvent); this.setState({imageLayout: e.nativeEvent.layout}, this.checkLayout); }, render: function() { var viewStyle = [styles.view, this.state.viewStyle]; var textLayout = this.state.textLayout || {width: '?', height: '?'}; var imageLayout = this.state.imageLayout || {x: '?', y: '?'}; debug('viewLayout', this.state.viewLayout); return ( A simple piece of text.{this.state.extraText} {'\n'} Text w/h: {textLayout.width}/{textLayout.height + '\n'} Image x/y: {imageLayout.x}/{imageLayout.y} ); } }); var styles = StyleSheet.create({ container: { margin: 40, }, view: { margin: 20, padding: 12, borderColor: 'black', borderWidth: 0.5, backgroundColor: 'transparent', }, text: { alignSelf: 'flex-start', borderColor: 'rgba(0, 0, 255, 0.2)', borderWidth: 0.5, }, image: { width: 50, height: 50, marginBottom: 10, alignSelf: 'center', }, }); LayoutEventsTest.displayName = 'LayoutEventsTest'; module.exports = LayoutEventsTest; ================================================ FILE: IntegrationTests/LoggingTestModule.js ================================================ /** * 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 LoggingTestModule */ 'use strict'; var BatchedBridge = require('BatchedBridge'); var warning = require('fbjs/lib/warning'); var invariant = require('fbjs/lib/invariant'); var LoggingTestModule = { logToConsole: function(str) { console.log(str); }, logToConsoleAfterWait: function(str,timeout_ms) { setTimeout(function() { console.log(str); }, timeout_ms); }, warning: function(str) { warning(false, str); }, invariant: function(str) { invariant(false, str); }, logErrorToConsole: function(str) { console.error(str); }, throwError: function(str) { throw new Error(str); } }; BatchedBridge.registerCallableModule( 'LoggingTestModule', LoggingTestModule ); module.exports = LoggingTestModule; ================================================ FILE: IntegrationTests/PromiseTest.js ================================================ /** * 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. * * @flow * @providesModule PromiseTest */ 'use strict'; var React = require('React'); var ReactNative = require('react-native'); var { View } = ReactNative; var { TestModule } = ReactNative.NativeModules; class PromiseTest extends React.Component<{}> { shouldResolve = false; shouldReject = false; shouldSucceedAsync = false; shouldThrowAsync = false; componentDidMount() { Promise.all([ this.testShouldResolve(), this.testShouldReject(), this.testShouldSucceedAsync(), this.testShouldThrowAsync(), ]).then(() => TestModule.markTestPassed( this.shouldResolve && this.shouldReject && this.shouldSucceedAsync && this.shouldThrowAsync )); } testShouldResolve = () => { return TestModule .shouldResolve() .then(() => this.shouldResolve = true) .catch(() => this.shouldResolve = false); }; testShouldReject = () => { return TestModule .shouldReject() .then(() => this.shouldReject = false) .catch(() => this.shouldReject = true); }; testShouldSucceedAsync = async (): Promise => { try { await TestModule.shouldResolve(); this.shouldSucceedAsync = true; } catch (e) { this.shouldSucceedAsync = false; } }; testShouldThrowAsync = async (): Promise => { try { await TestModule.shouldReject(); this.shouldThrowAsync = false; } catch (e) { this.shouldThrowAsync = true; } }; render(): React.Node { return ; } } PromiseTest.displayName = 'PromiseTest'; module.exports = PromiseTest; ================================================ FILE: IntegrationTests/PropertiesUpdateTest.js ================================================ /** * 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 PropertiesUpdateTest */ 'use strict'; var React = require('React'); var ReactNative = require('react-native'); var { View, } = ReactNative; var { TestModule } = ReactNative.NativeModules; class PropertiesUpdateTest extends React.Component { render() { if (this.props.markTestPassed) { TestModule.markTestPassed(true); } return ( ); } } PropertiesUpdateTest.displayName = 'PropertiesUpdateTest'; module.exports = PropertiesUpdateTest; ================================================ FILE: IntegrationTests/RCTRootViewIntegrationTestApp.js ================================================ /** * 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 RCTRootViewIntegrationTestApp */ 'use strict'; require('regenerator-runtime/runtime'); var React = require('React'); var ReactNative = require('react-native'); var { AppRegistry, ScrollView, StyleSheet, Text, TouchableOpacity, View, } = ReactNative; /* Keep this list in sync with RCTRootViewIntegrationTests.m */ var TESTS = [ require('./PropertiesUpdateTest'), require('./ReactContentSizeUpdateTest'), require('./SizeFlexibilityUpdateTest'), ]; TESTS.forEach( (test) => AppRegistry.registerComponent(test.displayName, () => test) ); class RCTRootViewIntegrationTestApp extends React.Component { state = { test: null, }; render() { if (this.state.test) { return ( ); } return ( Click on a test to run it in this shell for easier debugging and development. Run all tests in the testing environment with cmd+U in Xcode. {TESTS.map((test) => [ this.setState({test})} style={styles.row}> {test.displayName} , ])} ); } } var styles = StyleSheet.create({ container: { backgroundColor: 'white', marginTop: 40, margin: 15, }, row: { padding: 10, }, testName: { fontWeight: '500', }, separator: { height: 1, backgroundColor: '#bbbbbb', }, }); AppRegistry.registerComponent('RCTRootViewIntegrationTestApp', () => RCTRootViewIntegrationTestApp); ================================================ FILE: IntegrationTests/ReactContentSizeUpdateTest.js ================================================ /** * 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 ReactContentSizeUpdateTest */ 'use strict'; var React = require('react'); var createReactClass = require('create-react-class'); var ReactNative = require('react-native'); var RCTNativeAppEventEmitter = require('RCTNativeAppEventEmitter'); var Subscribable = require('Subscribable'); var TimerMixin = require('react-timer-mixin'); var { View } = ReactNative; var { TestModule } = ReactNative.NativeModules; var reactViewWidth = 101; var reactViewHeight = 102; var newReactViewWidth = 201; var newReactViewHeight = 202; var ReactContentSizeUpdateTest = createReactClass({ displayName: 'ReactContentSizeUpdateTest', mixins: [Subscribable.Mixin, TimerMixin], componentWillMount: function() { this.addListenerOn( RCTNativeAppEventEmitter, 'rootViewDidChangeIntrinsicSize', this.rootViewDidChangeIntrinsicSize ); }, getInitialState: function() { return { height: reactViewHeight, width: reactViewWidth, }; }, updateViewSize: function() { this.setState({ height: newReactViewHeight, width: newReactViewWidth, }); }, componentDidMount: function() { this.setTimeout( () => { this.updateViewSize(); }, 1000 ); }, rootViewDidChangeIntrinsicSize: function(intrinsicSize) { if (intrinsicSize.height === newReactViewHeight && intrinsicSize.width === newReactViewWidth) { TestModule.markTestPassed(true); } }, render() { return ( ); } }); ReactContentSizeUpdateTest.displayName = 'ReactContentSizeUpdateTest'; module.exports = ReactContentSizeUpdateTest; ================================================ FILE: IntegrationTests/SimpleSnapshotTest.js ================================================ /** * 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. * * @flow * @providesModule SimpleSnapshotTest */ 'use strict'; var React = require('React'); var ReactNative = require('react-native'); /* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error * found when Flow v0.54 was deployed. To see the error delete this comment and * run Flow. */ var requestAnimationFrame = require('fbjs/lib/requestAnimationFrame'); var { StyleSheet, View, } = ReactNative; var { TestModule } = ReactNative.NativeModules; class SimpleSnapshotTest extends React.Component<{}> { componentDidMount() { if (!TestModule.verifySnapshot) { throw new Error('TestModule.verifySnapshot not defined.'); } requestAnimationFrame(() => TestModule.verifySnapshot(this.done)); } done = (success : boolean) => { TestModule.markTestPassed(success); }; render() { return ( ); } } var styles = StyleSheet.create({ box1: { width: 80, height: 50, backgroundColor: 'red', }, box2: { top: -10, left: 20, width: 70, height: 90, backgroundColor: 'blue', }, }); SimpleSnapshotTest.displayName = 'SimpleSnapshotTest'; module.exports = SimpleSnapshotTest; ================================================ FILE: IntegrationTests/SizeFlexibilityUpdateTest.js ================================================ /** * 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 SizeFlexibilityUpdateTest */ 'use strict'; var React = require('react'); var createReactClass = require('create-react-class'); var ReactNative = require('react-native'); var RCTNativeAppEventEmitter = require('RCTNativeAppEventEmitter'); var Subscribable = require('Subscribable'); var { View } = ReactNative; var { TestModule } = ReactNative.NativeModules; var reactViewWidth = 111; var reactViewHeight = 222; var finalState = false; var SizeFlexibilityUpdateTest = createReactClass({ displayName: 'SizeFlexibilityUpdateTest', mixins: [Subscribable.Mixin], componentWillMount: function() { this.addListenerOn( RCTNativeAppEventEmitter, 'rootViewDidChangeIntrinsicSize', this.rootViewDidChangeIntrinsicSize ); }, markPassed: function() { TestModule.markTestPassed(true); finalState = true; }, rootViewDidChangeIntrinsicSize: function(intrinsicSize) { if (finalState) { // If a test reaches its final state, it is not expected to do anything more TestModule.markTestPassed(false); return; } if (this.props.both) { if (intrinsicSize.width === reactViewWidth && intrinsicSize.height === reactViewHeight) { this.markPassed(); return; } } if (this.props.height) { if (intrinsicSize.width !== reactViewWidth && intrinsicSize.height === reactViewHeight) { this.markPassed(); return; } } if (this.props.width) { if (intrinsicSize.width === reactViewWidth && intrinsicSize.height !== reactViewHeight) { this.markPassed(); return; } } if (this.props.none) { if (intrinsicSize.width !== reactViewWidth && intrinsicSize.height !== reactViewHeight) { this.markPassed(); return; } } }, render() { return ( ); } }); SizeFlexibilityUpdateTest.displayName = 'SizeFlexibilityUpdateTest'; module.exports = SizeFlexibilityUpdateTest; ================================================ FILE: IntegrationTests/SyncMethodTest.js ================================================ /** * 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. * * @flow * @providesModule SyncMethodTest */ 'use strict'; var React = require('react'); var ReactNative = require('react-native'); var { View } = ReactNative; const { TestModule, RNTesterTestModule, } = ReactNative.NativeModules; class SyncMethodTest extends React.Component<{}> { componentDidMount() { if (RNTesterTestModule.echoString('test string value') !== 'test string value') { throw new Error('Something wrong with sync method export'); } if (RNTesterTestModule.methodThatReturnsNil() != null) { throw new Error('Something wrong with sync method export'); } TestModule.markTestCompleted(); } render(): React.Node { return ; } } SyncMethodTest.displayName = 'SyncMethodTest'; module.exports = SyncMethodTest; ================================================ FILE: IntegrationTests/TimersTest.js ================================================ /** * 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. * * @flow * @providesModule TimersTest */ 'use strict'; var React = require('react'); var createReactClass = require('create-react-class'); var ReactNative = require('react-native'); /* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error * found when Flow v0.54 was deployed. To see the error delete this comment and * run Flow. */ var TimerMixin = require('react-timer-mixin'); var { StyleSheet, Text, View, } = ReactNative; var { TestModule } = ReactNative.NativeModules; var TimersTest = createReactClass({ displayName: 'TimersTest', mixins: [TimerMixin], _nextTest: () => {}, _interval: -1, getInitialState() { return { count: 0, done: false, }; }, componentDidMount() { this.setTimeout(this.testSetTimeout0, 1000); }, testSetTimeout0() { this.setTimeout(this.testSetTimeout1, 0); }, testSetTimeout1() { this.setTimeout(this.testSetTimeout50, 1); }, testSetTimeout50() { this.setTimeout(this.testRequestAnimationFrame, 50); }, testRequestAnimationFrame() { this.requestAnimationFrame(this.testSetInterval0); }, testSetInterval0() { this._nextTest = this.testSetInterval20; this._interval = this.setInterval(this._incrementInterval, 0); }, testSetInterval20() { this._nextTest = this.testSetImmediate; this._interval = this.setInterval(this._incrementInterval, 20); }, testSetImmediate() { this.setImmediate(this.testClearTimeout0); }, testClearTimeout0() { var timeout = this.setTimeout(() => this._fail('testClearTimeout0'), 0); this.clearTimeout(timeout); this.testClearTimeout30(); }, testClearTimeout30() { var timeout = this.setTimeout(() => this._fail('testClearTimeout30'), 30); this.clearTimeout(timeout); this.setTimeout(this.testClearMulti, 50); }, testClearMulti() { var fails = []; fails.push(this.setTimeout(() => this._fail('testClearMulti-1'), 20)); fails.push(this.setTimeout(() => this._fail('testClearMulti-2'), 50)); var delayClear = this.setTimeout(() => this._fail('testClearMulti-3'), 50); fails.push(this.setTimeout(() => this._fail('testClearMulti-4'), 0)); fails.push(this.setTimeout(() => this._fail('testClearMulti-5'), 10)); fails.forEach((timeout) => this.clearTimeout(timeout)); this.setTimeout(() => this.clearTimeout(delayClear), 20); this.setTimeout(this.testOrdering, 50); }, testOrdering() { // Clear timers are set first because it's more likely to uncover bugs. var fail0; this.setImmediate(() => this.clearTimeout(fail0)); fail0 = this.setTimeout( () => this._fail('testOrdering-t0, setImmediate should happen before ' + 'setTimeout 0'), 0 ); var failAnim; // This should fail without the t=0 fastpath feature. this.setTimeout(() => this.cancelAnimationFrame(failAnim), 0); failAnim = this.requestAnimationFrame( () => this._fail('testOrdering-Anim, setTimeout 0 should happen before ' + 'requestAnimationFrame') ); var fail25; this.setTimeout(() => { this.clearTimeout(fail25); }, 20); fail25 = this.setTimeout( () => this._fail('testOrdering-t25, setTimeout 20 should happen before ' + 'setTimeout 25'), 25 ); this.setTimeout(this.done, 50); }, done() { this.setState({done: true}, () => { TestModule.markTestCompleted(); }); }, render() { return ( {this.constructor.displayName + ': \n'} Intervals: {this.state.count + '\n'} {this.state.done ? 'Done' : 'Testing...'} ); }, _incrementInterval() { if (this.state.count > 3) { throw new Error('interval incremented past end.'); } if (this.state.count === 3) { this.clearInterval(this._interval); this.setState({count: 0}, this._nextTest); return; } this.setState({count: this.state.count + 1}); }, _fail(caller : string) : void { throw new Error('_fail called by ' + caller); }, }); var styles = StyleSheet.create({ container: { backgroundColor: 'white', padding: 40, }, }); TimersTest.displayName = 'TimersTest'; module.exports = TimersTest; ================================================ FILE: IntegrationTests/WebSocketTest.js ================================================ /** * 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. * * @flow * @providesModule WebSocketTest */ 'use strict'; var React = require('react'); var ReactNative = require('react-native'); var { View } = ReactNative; var { TestModule } = ReactNative.NativeModules; const DEFAULT_WS_URL = 'ws://localhost:5555/'; const WS_EVENTS = [ 'close', 'error', 'message', 'open', ]; const WS_STATES = [ /* 0 */ 'CONNECTING', /* 1 */ 'OPEN', /* 2 */ 'CLOSING', /* 3 */ 'CLOSED', ]; type State = { url: string; fetchStatus: ?string; socket: ?WebSocket; socketState: ?number; lastSocketEvent: ?string; lastMessage: ?string | ?ArrayBuffer; testMessage: string; testExpectedResponse: string; }; class WebSocketTest extends React.Component<{}, State> { state: State = { url: DEFAULT_WS_URL, fetchStatus: null, socket: null, socketState: null, lastSocketEvent: null, lastMessage: null, testMessage: 'testMessage', testExpectedResponse: 'testMessage_response' }; _waitFor = (condition: any, timeout: any, callback: any) => { var remaining = timeout; var t; var timeoutFunction = function() { if (condition()) { callback(true); return; } remaining--; if (remaining === 0) { callback(false); } else { t = setTimeout(timeoutFunction,1000); } }; t = setTimeout(timeoutFunction,1000); } _connect = () => { const socket = new WebSocket(this.state.url); WS_EVENTS.forEach(ev => socket.addEventListener(ev, this._onSocketEvent)); this.setState({ socket, socketState: socket.readyState, }); }; _socketIsConnected = () => { return this.state.socketState === 1; //'OPEN' } _socketIsDisconnected = () => { return this.state.socketState === 3; //'CLOSED' } _disconnect = () => { if (!this.state.socket) { return; } this.state.socket.close(); }; _onSocketEvent = (event: any) => { const state: any = { socketState: event.target.readyState, lastSocketEvent: event.type, }; if (event.type === 'message') { state.lastMessage = event.data; } this.setState(state); }; _sendText = (text: string) => { if (!this.state.socket) { return; } this.state.socket.send(text); }; _sendTestMessage = () => { this._sendText(this.state.testMessage); }; _receivedTestExpectedResponse = () => { return (this.state.lastMessage === this.state.testExpectedResponse); }; componentDidMount() { this.testConnect(); } testConnect = () => { var component = this; component._connect(); component._waitFor(component._socketIsConnected, 5, function(connectSucceeded) { if (!connectSucceeded) { TestModule.markTestPassed(false); return; } component.testSendAndReceive(); }); } testSendAndReceive = () => { var component = this; component._sendTestMessage(); component._waitFor(component._receivedTestExpectedResponse, 5, function(messageReceived) { if (!messageReceived) { TestModule.markTestPassed(false); return; } component.testDisconnect(); }); } testDisconnect = () => { var component = this; component._disconnect(); component._waitFor(component._socketIsDisconnected, 5, function(disconnectSucceeded) { TestModule.markTestPassed(disconnectSucceeded); }); } render(): React.Node { return ; } } WebSocketTest.displayName = 'WebSocketTest'; module.exports = WebSocketTest; ================================================ FILE: IntegrationTests/WebViewTest.js ================================================ /** * 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 WebViewTest */ 'use strict'; var React = require('react'); var ReactNative = require('react-native'); var { WebView, } = ReactNative; var { TestModule } = ReactNative.NativeModules; class WebViewTest extends React.Component { render() { var firstMessageReceived = false; var secondMessageReceived = false; function processMessage(e) { var message = e.nativeEvent.data; if (message === 'First') {firstMessageReceived = true;} if (message === 'Second') {secondMessageReceived = true;} // got both messages if (firstMessageReceived && secondMessageReceived) {TestModule.markTestPassed(true);} // wait for next message else if (firstMessageReceived && !secondMessageReceived) {return;} // first message got lost else if (!firstMessageReceived && secondMessageReceived) {throw new Error('First message got lost');} } var html = 'Hello world' + ''; // fail if messages didn't get through; window.setTimeout(function() { throw new Error(firstMessageReceived ? 'Both messages got lost' : 'Second message got lost');}, 10000); var source = { html: html, }; return ( ); } } WebViewTest.displayName = 'WebViewTest'; module.exports = WebViewTest; ================================================ FILE: IntegrationTests/launchWebSocketServer.command ================================================ #!/usr/bin/env bash # 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. # Set terminal title echo -en "\033]0;Web Socket Test Server\a" clear THIS_DIR=$(dirname "$0") pushd "$THIS_DIR" ./websocket_integration_test_server.js popd echo "Process terminated. Press to close the window" read ================================================ FILE: IntegrationTests/websocket_integration_test_server.js ================================================ #!/usr/bin/env node /** * 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. * * @flow * @providesModule websocket_integration_test_server */ 'use strict'; /* eslint-env node */ /* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error * found when Flow v0.54 was deployed. To see the error delete this comment and * run Flow. */ const WebSocket = require('ws'); console.log(`\ WebSocket integration test server This will send each incoming message back, with the string '_response' appended. An incoming message of 'exit' will shut down the server. `); const server = new WebSocket.Server({port: 5555}); server.on('connection', (ws) => { ws.on('message', (message) => { console.log('Received message:', message); if (message === 'exit') { console.log('WebSocket integration test server exit'); process.exit(0); } console.log('Cookie:', ws.upgradeReq.headers.cookie); ws.send(message + '_response'); }); ws.send('hello'); }); ================================================ FILE: Jenkinsfile ================================================ import groovy.json.JsonSlurperClassic def runPipeline() { try { ansiColor('xterm') { runStages(); } } catch(err) { echo "Error: ${err}" currentBuild.result = "FAILED" } } def pullDockerImage(imageName) { def result = sh(script: "docker pull ${imageName}", returnStatus: true) if (result != 0) { throw new Exception("Failed to pull image[${imageName}]") } } def buildDockerfile(dockerfilePath = "Dockerfile", imageName) { def buildCmd = "docker build -f ${dockerfilePath} -t ${imageName} ." echo "${buildCmd}" def result = sh(script: buildCmd, returnStatus: true) if (result != 0) { throw new Exception("Failed to build image[${imageName}] from '${dockerfilePath}'") } } def runCmdOnDockerImage(imageName, cmd, run_opts = '') { def result = sh(script: "docker run ${run_opts} -i ${imageName} sh -c '${cmd}'", returnStatus: true) if(result != 0) { throw new Exception("Failed to run cmd[${cmd}] on image[${imageName}]") } } def calculateGithubInfo() { return [ branch: env.BRANCH_NAME, sha: sh(returnStdout: true, script: 'git rev-parse HEAD').trim(), tag: null, isPR: "${env.CHANGE_URL}".contains('/pull/') ] } def getParallelInstrumentationTests(testDir, parallelCount, imageName) { def integrationTests = [:] def testCount = sh(script: "ls ${testDir} | wc -l", returnStdout: true).trim().toInteger() def testPerParallel = testCount.intdiv(parallelCount) + 1 def ignoredTests = 'CatalystNativeJavaToJSReturnValuesTestCase|CatalystUIManagerTestCase|CatalystMeasureLayoutTest|CatalystNativeJavaToJSArgumentsTestCase|CatalystNativeJSToJavaParametersTestCase|ReactScrollViewTestCase|ReactHorizontalScrollViewTestCase|ViewRenderingTestCase'; for (def x = 0; (x*testPerParallel) < testCount; x++) { def offset = x integrationTests["android integration tests: ${offset}"] = { run: { runCmdOnDockerImage(imageName, "bash /app/ContainerShip/scripts/run-android-docker-instrumentation-tests.sh --offset=${offset} --count=${testPerParallel} --ignore=\"${ignoredTests}\"", '--privileged --rm') } } } return integrationTests } def runStages() { def buildInfo = [ image: [ name: "facebook/react-native", tag: null ], scm: [ branch: null, sha: null, tag: null, isPR: false ] ] node { def jsDockerBuild, androidDockerBuild def jsTag, androidTag, jsImageName, androidImageName, parallelInstrumentationTests try { stage('Setup') { parallel( 'pull images': { pullDockerImage('containership/android-base:latest') } ) } stage('Build') { checkout scm def githubInfo = calculateGithubInfo() buildInfo.scm.branch = githubInfo.branch buildInfo.scm.sha = githubInfo.sha buildInfo.scm.tag = githubInfo.tag buildInfo.scm.isPR = githubInfo.isPR buildInfo.image.tag = "${buildInfo.scm.sha}-${env.BUILD_TAG.replace(" ", "-").replace("/", "-").replace("%2F", "-")}" jsTag = "${buildInfo.image.tag}" androidTag = "${buildInfo.image.tag}" jsImageName = "${buildInfo.image.name}-js:${jsTag}" androidImageName = "${buildInfo.image.name}-android:${androidTag}" parallelInstrumentationTests = getParallelInstrumentationTests('./ReactAndroid/src/androidTest/java/com/facebook/react/tests', 3, androidImageName) parallel( 'javascript build': { jsDockerBuild = docker.build("${jsImageName}", "-f ContainerShip/Dockerfile.javascript .") }, 'android build': { androidDockerBuild = docker.build("${androidImageName}", "-f ContainerShip/Dockerfile.android .") } ) } stage('Tests JS') { try { parallel( 'javascript flow': { runCmdOnDockerImage(jsImageName, 'yarn run flow -- check', '--rm') }, 'javascript tests': { runCmdOnDockerImage(jsImageName, 'yarn test --maxWorkers=4', '--rm') }, 'documentation tests': { runCmdOnDockerImage(jsImageName, 'cd website && yarn test', '--rm') }, 'documentation generation': { runCmdOnDockerImage(jsImageName, 'cd website && node ./server/generate.js', '--rm') } ) } catch(e) { currentBuild.result = "FAILED" echo "Test JS Stage Error: ${e}" } } stage('Tests Android') { try { parallel( 'android unit tests': { runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-android-docker-unit-tests.sh', '--privileged --rm') }, 'android e2e tests': { runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-ci-e2e-tests.sh --android --js', '--privileged --rm') } ) } catch(e) { currentBuild.result = "FAILED" echo "Tests Android Stage Error: ${e}" } } stage('Tests Android Instrumentation') { // run all tests in parallel try { parallel(parallelInstrumentationTests) } catch(e) { currentBuild.result = "FAILED" echo "Tests Android Instrumentation Stage Error: ${e}" } } stage('Cleanup') { cleanupImage(jsDockerBuild) cleanupImage(androidDockerBuild) } } catch(err) { cleanupImage(jsDockerBuild) cleanupImage(androidDockerBuild) throw err } } } def isMasterBranch() { return env.GIT_BRANCH == 'master' } def gitCommit() { return sh(returnStdout: true, script: 'git rev-parse HEAD').trim() } def cleanupImage(image) { if (image) { try { sh "docker ps -a | awk '{ print \$1,\$2 }' | grep ${image.id} | awk '{print \$1 }' | xargs -I {} docker rm {}" sh "docker rmi -f ${image.id}" } catch(e) { echo "Error cleaning up ${image.id}" echo "${e}" } } } runPipeline() ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2015-present, Facebook, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. ================================================ FILE: LICENSE-docs ================================================ Attribution 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the "Licensor." Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. ================================================ FILE: Libraries/.eslintrc ================================================ { "rules": { // This folder currently runs through babel and doesn't need to be // compatible with node 4 "comma-dangle": 0 } } ================================================ FILE: Libraries/ART/ART.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 0CF68B051AF0549300FF9E5C /* ARTGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */; }; 0CF68B061AF0549300FF9E5C /* ARTNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE01AF0549300FF9E5C /* ARTNode.m */; }; 0CF68B071AF0549300FF9E5C /* ARTRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */; }; 0CF68B081AF0549300FF9E5C /* ARTShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE41AF0549300FF9E5C /* ARTShape.m */; }; 0CF68B091AF0549300FF9E5C /* ARTSurfaceView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */; }; 0CF68B0A1AF0549300FF9E5C /* ARTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE81AF0549300FF9E5C /* ARTText.m */; }; 0CF68B0B1AF0549300FF9E5C /* ARTBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */; }; 0CF68B0C1AF0549300FF9E5C /* ARTLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */; }; 0CF68B0D1AF0549300FF9E5C /* ARTPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */; }; 0CF68B0E1AF0549300FF9E5C /* ARTRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */; }; 0CF68B0F1AF0549300FF9E5C /* ARTSolidColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */; }; 0CF68B101AF0549300FF9E5C /* RCTConvert+ART.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */; }; 0CF68B111AF0549300FF9E5C /* ARTGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */; }; 0CF68B121AF0549300FF9E5C /* ARTNodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */; }; 0CF68B131AF0549300FF9E5C /* ARTRenderableManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */; }; 0CF68B141AF0549300FF9E5C /* ARTShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */; }; 0CF68B151AF0549300FF9E5C /* ARTSurfaceViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */; }; 0CF68B161AF0549300FF9E5C /* ARTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */; }; 325CF7AD1E5F2ABA00AC9606 /* ARTBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */; }; 325CF7AE1E5F2ABA00AC9606 /* ARTLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */; }; 325CF7AF1E5F2ABA00AC9606 /* ARTPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */; }; 325CF7B01E5F2ABA00AC9606 /* ARTRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */; }; 325CF7B11E5F2ABA00AC9606 /* ARTSolidColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */; }; 325CF7B21E5F2ABA00AC9606 /* ARTGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */; }; 325CF7B31E5F2ABA00AC9606 /* ARTNodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */; }; 325CF7B41E5F2ABA00AC9606 /* ARTRenderableManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */; }; 325CF7B51E5F2ABA00AC9606 /* ARTShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */; }; 325CF7B61E5F2ABA00AC9606 /* ARTSurfaceViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */; }; 325CF7B71E5F2ABA00AC9606 /* ARTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */; }; 325CF7B81E5F2ABA00AC9606 /* ARTGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */; }; 325CF7B91E5F2ABA00AC9606 /* ARTNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE01AF0549300FF9E5C /* ARTNode.m */; }; 325CF7BA1E5F2ABA00AC9606 /* ARTRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */; }; 325CF7BB1E5F2ABA00AC9606 /* ARTShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE41AF0549300FF9E5C /* ARTShape.m */; }; 325CF7BC1E5F2ABA00AC9606 /* ARTSurfaceView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */; }; 325CF7BD1E5F2ABA00AC9606 /* ARTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE81AF0549300FF9E5C /* ARTText.m */; }; 325CF7BE1E5F2ABA00AC9606 /* RCTConvert+ART.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 0CF68ABF1AF0540F00FF9E5C /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 323A12851E5F266B004975B8 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 0CF68AC11AF0540F00FF9E5C /* libART.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libART.a; sourceTree = BUILT_PRODUCTS_DIR; }; 0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTCGFloatArray.h; sourceTree = ""; }; 0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTContainer.h; sourceTree = ""; }; 0CF68ADD1AF0549300FF9E5C /* ARTGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTGroup.h; sourceTree = ""; }; 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTGroup.m; sourceTree = ""; }; 0CF68ADF1AF0549300FF9E5C /* ARTNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTNode.h; sourceTree = ""; }; 0CF68AE01AF0549300FF9E5C /* ARTNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTNode.m; sourceTree = ""; }; 0CF68AE11AF0549300FF9E5C /* ARTRenderable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRenderable.h; sourceTree = ""; }; 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRenderable.m; sourceTree = ""; }; 0CF68AE31AF0549300FF9E5C /* ARTShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTShape.h; sourceTree = ""; }; 0CF68AE41AF0549300FF9E5C /* ARTShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTShape.m; sourceTree = ""; }; 0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSurfaceView.h; sourceTree = ""; }; 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSurfaceView.m; sourceTree = ""; }; 0CF68AE71AF0549300FF9E5C /* ARTText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTText.h; sourceTree = ""; }; 0CF68AE81AF0549300FF9E5C /* ARTText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTText.m; sourceTree = ""; }; 0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTTextFrame.h; sourceTree = ""; }; 0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTBrush.h; sourceTree = ""; }; 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTBrush.m; sourceTree = ""; }; 0CF68AED1AF0549300FF9E5C /* ARTLinearGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTLinearGradient.h; sourceTree = ""; }; 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTLinearGradient.m; sourceTree = ""; }; 0CF68AEF1AF0549300FF9E5C /* ARTPattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPattern.h; sourceTree = ""; }; 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPattern.m; sourceTree = ""; }; 0CF68AF11AF0549300FF9E5C /* ARTRadialGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRadialGradient.h; sourceTree = ""; }; 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRadialGradient.m; sourceTree = ""; }; 0CF68AF31AF0549300FF9E5C /* ARTSolidColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSolidColor.h; sourceTree = ""; }; 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSolidColor.m; sourceTree = ""; }; 0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+ART.h"; sourceTree = ""; }; 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+ART.m"; sourceTree = ""; }; 0CF68AF91AF0549300FF9E5C /* ARTGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTGroupManager.h; sourceTree = ""; }; 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTGroupManager.m; sourceTree = ""; }; 0CF68AFB1AF0549300FF9E5C /* ARTNodeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTNodeManager.h; sourceTree = ""; }; 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTNodeManager.m; sourceTree = ""; }; 0CF68AFD1AF0549300FF9E5C /* ARTRenderableManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRenderableManager.h; sourceTree = ""; }; 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRenderableManager.m; sourceTree = ""; }; 0CF68AFF1AF0549300FF9E5C /* ARTShapeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTShapeManager.h; sourceTree = ""; }; 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTShapeManager.m; sourceTree = ""; }; 0CF68B011AF0549300FF9E5C /* ARTSurfaceViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSurfaceViewManager.h; sourceTree = ""; }; 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSurfaceViewManager.m; sourceTree = ""; }; 0CF68B031AF0549300FF9E5C /* ARTTextManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTTextManager.h; sourceTree = ""; }; 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTTextManager.m; sourceTree = ""; }; 323A12871E5F266B004975B8 /* libART-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libART-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 0CF68ABE1AF0540F00FF9E5C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 323A12841E5F266B004975B8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0CF68AB81AF0540F00FF9E5C = { isa = PBXGroup; children = ( 0CF68AEA1AF0549300FF9E5C /* Brushes */, 0CF68AF81AF0549300FF9E5C /* ViewManagers */, 0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */, 0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */, 0CF68ADD1AF0549300FF9E5C /* ARTGroup.h */, 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */, 0CF68ADF1AF0549300FF9E5C /* ARTNode.h */, 0CF68AE01AF0549300FF9E5C /* ARTNode.m */, 0CF68AE11AF0549300FF9E5C /* ARTRenderable.h */, 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */, 0CF68AE31AF0549300FF9E5C /* ARTShape.h */, 0CF68AE41AF0549300FF9E5C /* ARTShape.m */, 0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */, 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */, 0CF68AE71AF0549300FF9E5C /* ARTText.h */, 0CF68AE81AF0549300FF9E5C /* ARTText.m */, 0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */, 0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */, 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */, 0CF68AC21AF0540F00FF9E5C /* Products */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 0CF68AC21AF0540F00FF9E5C /* Products */ = { isa = PBXGroup; children = ( 0CF68AC11AF0540F00FF9E5C /* libART.a */, 323A12871E5F266B004975B8 /* libART-tvOS.a */, ); name = Products; sourceTree = ""; }; 0CF68AEA1AF0549300FF9E5C /* Brushes */ = { isa = PBXGroup; children = ( 0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */, 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */, 0CF68AED1AF0549300FF9E5C /* ARTLinearGradient.h */, 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */, 0CF68AEF1AF0549300FF9E5C /* ARTPattern.h */, 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */, 0CF68AF11AF0549300FF9E5C /* ARTRadialGradient.h */, 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */, 0CF68AF31AF0549300FF9E5C /* ARTSolidColor.h */, 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */, ); path = Brushes; sourceTree = ""; }; 0CF68AF81AF0549300FF9E5C /* ViewManagers */ = { isa = PBXGroup; children = ( 0CF68AF91AF0549300FF9E5C /* ARTGroupManager.h */, 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */, 0CF68AFB1AF0549300FF9E5C /* ARTNodeManager.h */, 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */, 0CF68AFD1AF0549300FF9E5C /* ARTRenderableManager.h */, 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */, 0CF68AFF1AF0549300FF9E5C /* ARTShapeManager.h */, 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */, 0CF68B011AF0549300FF9E5C /* ARTSurfaceViewManager.h */, 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */, 0CF68B031AF0549300FF9E5C /* ARTTextManager.h */, 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */, ); path = ViewManagers; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 0CF68AC01AF0540F00FF9E5C /* ART */ = { isa = PBXNativeTarget; buildConfigurationList = 0CF68AD51AF0540F00FF9E5C /* Build configuration list for PBXNativeTarget "ART" */; buildPhases = ( 0CF68ABD1AF0540F00FF9E5C /* Sources */, 0CF68ABE1AF0540F00FF9E5C /* Frameworks */, 0CF68ABF1AF0540F00FF9E5C /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = ART; productName = ART; productReference = 0CF68AC11AF0540F00FF9E5C /* libART.a */; productType = "com.apple.product-type.library.static"; }; 323A12861E5F266B004975B8 /* ART-tvOS */ = { isa = PBXNativeTarget; buildConfigurationList = 323A128D1E5F266B004975B8 /* Build configuration list for PBXNativeTarget "ART-tvOS" */; buildPhases = ( 323A12831E5F266B004975B8 /* Sources */, 323A12841E5F266B004975B8 /* Frameworks */, 323A12851E5F266B004975B8 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = "ART-tvOS"; productName = "ART-tvOS"; productReference = 323A12871E5F266B004975B8 /* libART-tvOS.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 0CF68AB91AF0540F00FF9E5C /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0620; TargetAttributes = { 0CF68AC01AF0540F00FF9E5C = { CreatedOnToolsVersion = 6.2; }; 323A12861E5F266B004975B8 = { CreatedOnToolsVersion = 6.2; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 0CF68ABC1AF0540F00FF9E5C /* Build configuration list for PBXProject "ART" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 0CF68AB81AF0540F00FF9E5C; productRefGroup = 0CF68AC21AF0540F00FF9E5C /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 0CF68AC01AF0540F00FF9E5C /* ART */, 323A12861E5F266B004975B8 /* ART-tvOS */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 0CF68ABD1AF0540F00FF9E5C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 0CF68B161AF0549300FF9E5C /* ARTTextManager.m in Sources */, 0CF68B111AF0549300FF9E5C /* ARTGroupManager.m in Sources */, 0CF68B0D1AF0549300FF9E5C /* ARTPattern.m in Sources */, 0CF68B0A1AF0549300FF9E5C /* ARTText.m in Sources */, 0CF68B121AF0549300FF9E5C /* ARTNodeManager.m in Sources */, 0CF68B051AF0549300FF9E5C /* ARTGroup.m in Sources */, 0CF68B131AF0549300FF9E5C /* ARTRenderableManager.m in Sources */, 0CF68B091AF0549300FF9E5C /* ARTSurfaceView.m in Sources */, 0CF68B0E1AF0549300FF9E5C /* ARTRadialGradient.m in Sources */, 0CF68B151AF0549300FF9E5C /* ARTSurfaceViewManager.m in Sources */, 0CF68B081AF0549300FF9E5C /* ARTShape.m in Sources */, 0CF68B071AF0549300FF9E5C /* ARTRenderable.m in Sources */, 0CF68B101AF0549300FF9E5C /* RCTConvert+ART.m in Sources */, 0CF68B061AF0549300FF9E5C /* ARTNode.m in Sources */, 0CF68B0F1AF0549300FF9E5C /* ARTSolidColor.m in Sources */, 0CF68B0C1AF0549300FF9E5C /* ARTLinearGradient.m in Sources */, 0CF68B0B1AF0549300FF9E5C /* ARTBrush.m in Sources */, 0CF68B141AF0549300FF9E5C /* ARTShapeManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 323A12831E5F266B004975B8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 325CF7B71E5F2ABA00AC9606 /* ARTTextManager.m in Sources */, 325CF7B21E5F2ABA00AC9606 /* ARTGroupManager.m in Sources */, 325CF7AF1E5F2ABA00AC9606 /* ARTPattern.m in Sources */, 325CF7BD1E5F2ABA00AC9606 /* ARTText.m in Sources */, 325CF7B31E5F2ABA00AC9606 /* ARTNodeManager.m in Sources */, 325CF7B81E5F2ABA00AC9606 /* ARTGroup.m in Sources */, 325CF7B41E5F2ABA00AC9606 /* ARTRenderableManager.m in Sources */, 325CF7BC1E5F2ABA00AC9606 /* ARTSurfaceView.m in Sources */, 325CF7B01E5F2ABA00AC9606 /* ARTRadialGradient.m in Sources */, 325CF7B61E5F2ABA00AC9606 /* ARTSurfaceViewManager.m in Sources */, 325CF7BB1E5F2ABA00AC9606 /* ARTShape.m in Sources */, 325CF7BA1E5F2ABA00AC9606 /* ARTRenderable.m in Sources */, 325CF7BE1E5F2ABA00AC9606 /* RCTConvert+ART.m in Sources */, 325CF7B91E5F2ABA00AC9606 /* ARTNode.m in Sources */, 325CF7B11E5F2ABA00AC9606 /* ARTSolidColor.m in Sources */, 325CF7AE1E5F2ABA00AC9606 /* ARTLinearGradient.m in Sources */, 325CF7AD1E5F2ABA00AC9606 /* ARTBrush.m in Sources */, 325CF7B51E5F2ABA00AC9606 /* ARTShapeManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 0CF68AD31AF0540F00FF9E5C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SKIP_INSTALL = YES; }; name = Debug; }; 0CF68AD41AF0540F00FF9E5C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; VALIDATE_PRODUCT = YES; }; name = Release; }; 0CF68AD61AF0540F00FF9E5C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.11; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; name = Debug; }; 0CF68AD71AF0540F00FF9E5C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.11; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; name = Release; }; 323A128E1E5F266B004975B8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; TVOS_DEPLOYMENT_TARGET = 9.2; }; name = Debug; }; 323A128F1E5F266B004975B8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_NO_COMMON_BLOCKS = YES; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; TVOS_DEPLOYMENT_TARGET = 9.2; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 0CF68ABC1AF0540F00FF9E5C /* Build configuration list for PBXProject "ART" */ = { isa = XCConfigurationList; buildConfigurations = ( 0CF68AD31AF0540F00FF9E5C /* Debug */, 0CF68AD41AF0540F00FF9E5C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 0CF68AD51AF0540F00FF9E5C /* Build configuration list for PBXNativeTarget "ART" */ = { isa = XCConfigurationList; buildConfigurations = ( 0CF68AD61AF0540F00FF9E5C /* Debug */, 0CF68AD71AF0540F00FF9E5C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 323A128D1E5F266B004975B8 /* Build configuration list for PBXNativeTarget "ART-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 323A128E1E5F266B004975B8 /* Debug */, 323A128F1E5F266B004975B8 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 0CF68AB91AF0540F00FF9E5C /* Project object */; } ================================================ FILE: Libraries/ART/ARTCGFloatArray.h ================================================ /** * 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. */ // A little helper to make sure we have the right memory allocation ready for use. // We assume that we will only this in one place so no reference counting is necessary. // Needs to be freed when dealloced. // This is fragile since this relies on these values not getting reused. Consider // wrapping these in an Obj-C class or some ARC hackery to get refcounting. typedef struct { size_t count; CGFloat *array; } ARTCGFloatArray; ================================================ FILE: Libraries/ART/ARTContainer.h ================================================ /** * 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. */ #import @protocol ARTContainer // This is used as a hook for child to mark it's parent as dirty. // This bubbles up to the root which gets marked as dirty. - (void)invalidate; @end ================================================ FILE: Libraries/ART/ARTGroup.h ================================================ /** * 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. */ #import #import "ARTContainer.h" #import "ARTNode.h" @interface ARTGroup : ARTNode @property (nonatomic, assign) CGRect clipping; @end ================================================ FILE: Libraries/ART/ARTGroup.m ================================================ /** * 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. */ #import "ARTGroup.h" @implementation ARTGroup - (void)renderLayerTo:(CGContextRef)context { if (!CGRectIsEmpty(self.clipping)) { CGContextClipToRect(context, self.clipping); } for (ARTNode *node in self.subviews) { [node renderTo:context]; } } @end ================================================ FILE: Libraries/ART/ARTNode.h ================================================ /** * 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. */ #import #import #import "React/NSView+React.h" /** * ART nodes are implemented as empty UIViews but this is just an implementation detail to fit * into the existing view management. They should also be shadow views and painted on a background * thread. */ @interface ARTNode : NSView @property (nonatomic, assign) CGFloat opacity; - (void)invalidate; - (void)renderTo:(CGContextRef)context; /** * renderTo will take opacity into account and draw renderLayerTo off-screen if there is opacity * specified, then composite that onto the context. renderLayerTo always draws at opacity=1. * @abstract */ - (void)renderLayerTo:(CGContextRef)context; @end ================================================ FILE: Libraries/ART/ARTNode.m ================================================ /** * 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. */ #import "ARTNode.h" #import "ARTContainer.h" @implementation ARTNode { CGAffineTransform _transform; //BOOL *_shouldBeTransformed; } - (BOOL)isFlipped { return YES; } - (void)insertReactSubview:(NSView *)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; [self addSubview:subview]; [self invalidate]; } - (void)removeReactSubview:(NSView *)subview { [super removeReactSubview:subview]; [self invalidate]; } - (void)didUpdateReactSubviews { // Do nothing, as subviews are inserted by insertReactSubview: } - (void)setOpacity:(CGFloat)opacity { [self invalidate]; _opacity = opacity; } - (void)setTransform:(CGAffineTransform)transform { [self invalidate]; self.layer.affineTransform = _transform; _transform = transform; } - (void)invalidate { id container = (id)self.superview; [container invalidate]; } - (void)renderTo:(CGContextRef)context { if (self.opacity <= 0) { // Nothing to paint return; } if (self.opacity >= 1) { // Just paint at full opacity CGContextSaveGState(context); CGContextConcatCTM(context, self.layer.affineTransform); CGContextSetAlpha(context, 1); [self renderLayerTo:context]; CGContextRestoreGState(context); return; } // This needs to be painted on a layer before being composited. CGContextSaveGState(context); CGContextConcatCTM(context, self.layer.affineTransform); CGContextSetAlpha(context, self.opacity); CGContextBeginTransparencyLayer(context, NULL); [self renderLayerTo:context]; CGContextEndTransparencyLayer(context); CGContextRestoreGState(context); } - (void)renderLayerTo:(CGContextRef)context { // abstract } - (void)layout { [super layout]; self.layer.affineTransform = _transform; } @end ================================================ FILE: Libraries/ART/ARTRenderable.h ================================================ /** * 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. */ #import #import "ARTBrush.h" #import "ARTCGFloatArray.h" #import "ARTNode.h" @interface ARTRenderable : ARTNode @property (nonatomic, strong) ARTBrush *fill; @property (nonatomic, assign) CGColorRef stroke; @property (nonatomic, assign) CGFloat strokeWidth; @property (nonatomic, assign) CGLineCap strokeCap; @property (nonatomic, assign) CGLineJoin strokeJoin; @property (nonatomic, assign) ARTCGFloatArray strokeDash; @end ================================================ FILE: Libraries/ART/ARTRenderable.m ================================================ /** * 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. */ #import "ARTRenderable.h" @implementation ARTRenderable - (void)setFill:(ARTBrush *)fill { [self invalidate]; _fill = fill; } - (void)setStroke:(CGColorRef)stroke { if (stroke == _stroke) { return; } [self invalidate]; CGColorRelease(_stroke); _stroke = CGColorRetain(stroke); } - (void)setStrokeWidth:(CGFloat)strokeWidth { [self invalidate]; _strokeWidth = strokeWidth; } - (void)setStrokeCap:(CGLineCap)strokeCap { [self invalidate]; _strokeCap = strokeCap; } - (void)setStrokeJoin:(CGLineJoin)strokeJoin { [self invalidate]; _strokeJoin = strokeJoin; } - (void)setStrokeDash:(ARTCGFloatArray)strokeDash { if (strokeDash.array == _strokeDash.array) { return; } if (_strokeDash.array) { free(_strokeDash.array); } [self invalidate]; _strokeDash = strokeDash; } - (void)dealloc { CGColorRelease(_stroke); if (_strokeDash.array) { free(_strokeDash.array); } } - (void)renderTo:(CGContextRef)context { if (self.opacity <= 0 || self.opacity >= 1 || (self.fill && self.stroke)) { // If we have both fill and stroke, we will need to paint this using normal compositing [super renderTo: context]; return; } // This is a terminal with only one painting. Therefore we don't need to paint this // off-screen. We can just composite it straight onto the buffer. CGContextSaveGState(context); CGContextConcatCTM(context, self.layer.affineTransform); CGContextSetAlpha(context, self.opacity); [self renderLayerTo:context]; CGContextRestoreGState(context); } - (void)renderLayerTo:(CGContextRef)context { // abstract } @end ================================================ FILE: Libraries/ART/ARTSerializablePath.js ================================================ /** * 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 ARTSerializablePath */ 'use strict'; // TODO: Move this into an ART mode called "serialized" or something var Class = require('art/core/class.js'); var Path = require('art/core/path.js'); var MOVE_TO = 0; var CLOSE = 1; var LINE_TO = 2; var CURVE_TO = 3; var ARC = 4; var SerializablePath = Class(Path, { initialize: function(path) { this.reset(); if (path instanceof SerializablePath) { this.path = path.path.slice(0); } else if (path) { if (path.applyToPath) { path.applyToPath(this); } else { this.push(path); } } }, onReset: function() { this.path = []; }, onMove: function(sx, sy, x, y) { this.path.push(MOVE_TO, x, y); }, onLine: function(sx, sy, x, y) { this.path.push(LINE_TO, x, y); }, onBezierCurve: function(sx, sy, p1x, p1y, p2x, p2y, x, y) { this.path.push(CURVE_TO, p1x, p1y, p2x, p2y, x, y); }, _arcToBezier: Path.prototype.onArc, onArc: function(sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) { if (rx !== ry || rotation) { return this._arcToBezier( sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation ); } this.path.push(ARC, cx, cy, rx, sa, ea, ccw ? 0 : 1); }, onClose: function() { this.path.push(CLOSE); }, toJSON: function() { return this.path; } }); module.exports = SerializablePath; ================================================ FILE: Libraries/ART/ARTShape.h ================================================ /** * 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. */ #import #import "ARTRenderable.h" @interface ARTShape : ARTRenderable @property (nonatomic, assign) CGPathRef d; @end ================================================ FILE: Libraries/ART/ARTShape.m ================================================ /** * 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. */ #import "ARTShape.h" @implementation ARTShape - (void)setD:(CGPathRef)d { if (d == _d) { return; } [self invalidate]; CGPathRelease(_d); _d = CGPathRetain(d); } - (void)dealloc { CGPathRelease(_d); } - (void)renderLayerTo:(CGContextRef)context { if ((!self.fill && !self.stroke) || !self.d) { return; } CGPathDrawingMode mode = kCGPathStroke; if (self.fill) { if ([self.fill applyFillColor:context]) { mode = kCGPathFill; } else { CGContextSaveGState(context); CGContextAddPath(context, self.d); CGContextClip(context); [self.fill paint:context]; CGContextRestoreGState(context); if (!self.stroke) { return; } } } if (self.stroke) { CGContextSetStrokeColorWithColor(context, self.stroke); CGContextSetLineWidth(context, self.strokeWidth); CGContextSetLineCap(context, self.strokeCap); CGContextSetLineJoin(context, self.strokeJoin); ARTCGFloatArray dash = self.strokeDash; if (dash.count) { CGContextSetLineDash(context, 0, dash.array, dash.count); } if (mode == kCGPathFill) { mode = kCGPathFillStroke; } } CGContextAddPath(context, self.d); CGContextDrawPath(context, mode); } @end ================================================ FILE: Libraries/ART/ARTSurfaceView.h ================================================ /** * 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. */ #import #import "ARTContainer.h" @interface ARTSurfaceView : NSView @end ================================================ FILE: Libraries/ART/ARTSurfaceView.m ================================================ /** * 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. */ #import "ARTSurfaceView.h" #import #import "ARTNode.h" @implementation ARTSurfaceView - (BOOL)isFlipped { return YES; } - (void)insertReactSubview:(NSView *)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; [self addSubview:subview]; [self invalidate]; } - (void)removeReactSubview:(NSView *)subview { [super removeReactSubview:subview]; [self invalidate]; } - (void)didUpdateReactSubviews { // Do nothing, as subviews are inserted by insertReactSubview: } - (void)invalidate { [self setNeedsDisplay:YES]; } - (void)drawRect:(CGRect)rect { CGContextRef context = [NSGraphicsContext currentContext].CGContext; for (ARTNode *node in self.subviews) { [node renderTo:context]; } } - (void)reactSetInheritedBackgroundColor:(NSColor *)inheritedBackgroundColor { [self setWantsLayer:YES]; self.layer.backgroundColor = [inheritedBackgroundColor CGColor]; } @end ================================================ FILE: Libraries/ART/ARTText.h ================================================ /** * 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. */ #import #import "ARTRenderable.h" #import "ARTTextFrame.h" @interface ARTText : ARTRenderable @property (nonatomic, assign) CTTextAlignment alignment; @property (nonatomic, assign) ARTTextFrame textFrame; @end ================================================ FILE: Libraries/ART/ARTText.m ================================================ /** * 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. */ #import "ARTText.h" #import @implementation ARTText - (void)setAlignment:(CTTextAlignment)alignment { [self invalidate]; _alignment = alignment; } static void ARTFreeTextFrame(ARTTextFrame frame) { if (frame.count) { // We must release each line before freeing up this struct for (int i = 0; i < frame.count; i++) { CFRelease(frame.lines[i]); } free(frame.lines); free(frame.widths); } } - (void)setTextFrame:(ARTTextFrame)frame { if (frame.lines != _textFrame.lines) { ARTFreeTextFrame(_textFrame); } [self invalidate]; _textFrame = frame; } - (void)dealloc { ARTFreeTextFrame(_textFrame); } - (void)renderLayerTo:(CGContextRef)context { ARTTextFrame frame = self.textFrame; if ((!self.fill && !self.stroke) || !frame.count) { return; } // to-do: draw along a path CGTextDrawingMode mode = kCGTextStroke; if (self.fill) { if ([self.fill applyFillColor:context]) { mode = kCGTextFill; } else { for (int i = 0; i < frame.count; i++) { CGContextSaveGState(context); // Inverse the coordinate space since CoreText assumes a bottom-up coordinate space CGContextScaleCTM(context, 1.0, -1.0); CGContextSetTextDrawingMode(context, kCGTextClip); [self renderLineTo:context atIndex:i]; // Inverse the coordinate space back to the original before filling CGContextScaleCTM(context, 1.0, -1.0); [self.fill paint:context]; // Restore the state so that the next line can be clipped separately CGContextRestoreGState(context); } if (!self.stroke) { return; } } } if (self.stroke) { CGContextSetStrokeColorWithColor(context, self.stroke); CGContextSetLineWidth(context, self.strokeWidth); CGContextSetLineCap(context, self.strokeCap); CGContextSetLineJoin(context, self.strokeJoin); ARTCGFloatArray dash = self.strokeDash; if (dash.count) { CGContextSetLineDash(context, 0, dash.array, dash.count); } if (mode == kCGTextFill) { mode = kCGTextFillStroke; } } CGContextSetTextDrawingMode(context, mode); // Inverse the coordinate space since CoreText assumes a bottom-up coordinate space CGContextScaleCTM(context, 1.0, -1.0); for (int i = 0; i < frame.count; i++) { [self renderLineTo:context atIndex:i]; } } - (void)renderLineTo:(CGContextRef)context atIndex:(int)index { ARTTextFrame frame = self.textFrame; CGFloat shift; switch (self.alignment) { case kCTTextAlignmentRight: shift = frame.widths[index]; break; case kCTTextAlignmentCenter: shift = (frame.widths[index] / 2); break; default: shift = 0; break; } // We should consider snapping this shift to device pixels to improve rendering quality // when a line has subpixel width. CGContextSetTextPosition(context, -shift, -frame.baseLine - frame.lineHeight * index); CTLineRef line = frame.lines[index]; CTLineDraw(line, context); } @end ================================================ FILE: Libraries/ART/ARTTextFrame.h ================================================ /** * 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. */ #import // A little helper to make sure we have a set of lines including width ready for use. // We assume that we will only this in one place so no reference counting is necessary. // Needs to be freed when dealloced. // This is fragile since this relies on these values not getting reused. Consider // wrapping these in an Obj-C class or some ARC hackery to get refcounting. typedef struct { size_t count; CGFloat baseLine; // Distance from the origin to the base line of the first line CGFloat lineHeight; // Distance between lines CTLineRef *lines; CGFloat *widths; // Width of each line } ARTTextFrame; ================================================ FILE: Libraries/ART/Brushes/ARTBrush.h ================================================ /** * 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. */ #import #import @interface ARTBrush : NSObject /* @abstract */ - (instancetype)initWithArray:(NSArray *)data NS_DESIGNATED_INITIALIZER; /** * For certain brushes we can fast path a combined fill and stroke. * For those brushes we override applyFillColor which sets the fill * color to be used by those batch paints. Those return YES. * We can't batch gradient painting in CoreGraphics, so those will * return NO and paint gets called instead. * @abstract */ - (BOOL)applyFillColor:(CGContextRef)context; /** * paint fills the context with a brush. The context is assumed to * be clipped. * @abstract */ - (void)paint:(CGContextRef)context; @end ================================================ FILE: Libraries/ART/Brushes/ARTBrush.m ================================================ /** * 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. */ #import "ARTBrush.h" #import @implementation ARTBrush - (instancetype)initWithArray:(NSArray *)data { return [super init]; } RCT_NOT_IMPLEMENTED(- (instancetype)init) - (BOOL)applyFillColor:(CGContextRef)context { return NO; } - (void)paint:(CGContextRef)context { // abstract } @end ================================================ FILE: Libraries/ART/Brushes/ARTLinearGradient.h ================================================ /** * 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. */ #import "ARTBrush.h" @interface ARTLinearGradient : ARTBrush @end ================================================ FILE: Libraries/ART/Brushes/ARTLinearGradient.m ================================================ /** * 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. */ #import "ARTLinearGradient.h" #import #import "RCTConvert+ART.h" @implementation ARTLinearGradient { CGGradientRef _gradient; CGPoint _startPoint; CGPoint _endPoint; } - (instancetype)initWithArray:(NSArray *)array { if ((self = [super initWithArray:array])) { if (array.count < 5) { RCTLogError(@"-[%@ %@] expects 5 elements, received %@", self.class, NSStringFromSelector(_cmd), array); return nil; } _startPoint = [RCTConvert CGPoint:array offset:1]; _endPoint = [RCTConvert CGPoint:array offset:3]; _gradient = CGGradientRetain([RCTConvert CGGradient:array offset:5]); } return self; } - (void)dealloc { CGGradientRelease(_gradient); } - (void)paint:(CGContextRef)context { CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; CGContextDrawLinearGradient(context, _gradient, _startPoint, _endPoint, extendOptions); } @end ================================================ FILE: Libraries/ART/Brushes/ARTPattern.h ================================================ /** * 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. */ #import "ARTBrush.h" @interface ARTPattern : ARTBrush @end ================================================ FILE: Libraries/ART/Brushes/ARTPattern.m ================================================ /** * 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. */ #import "ARTPattern.h" #import #import "RCTConvert+ART.h" @implementation ARTPattern { CGImageRef _image; CGRect _rect; } - (instancetype)initWithArray:(NSArray *)array { if ((self = [super initWithArray:array])) { if (array.count < 6) { RCTLogError(@"-[%@ %@] expects 6 elements, received %@", self.class, NSStringFromSelector(_cmd), array); return nil; } _image = CGImageRetain([RCTConvert CGImage:array[1]]); _rect = [RCTConvert CGRect:array offset:2]; } return self; } - (void)dealloc { CGImageRelease(_image); } // Note: This could use applyFillColor with a pattern. This could be more efficient but // to do that, we need to calculate our own user space CTM. - (void)paint:(CGContextRef)context { CGContextDrawTiledImage(context, _rect, _image); } @end ================================================ FILE: Libraries/ART/Brushes/ARTRadialGradient.h ================================================ /** * 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. */ #import "ARTBrush.h" @interface ARTRadialGradient : ARTBrush @end ================================================ FILE: Libraries/ART/Brushes/ARTRadialGradient.m ================================================ /** * 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. */ #import "ARTRadialGradient.h" #import #import "RCTConvert+ART.h" @implementation ARTRadialGradient { CGGradientRef _gradient; CGPoint _focusPoint; CGPoint _centerPoint; CGFloat _radius; CGFloat _radiusRatio; } - (instancetype)initWithArray:(NSArray *)array { if ((self = [super initWithArray:array])) { if (array.count < 7) { RCTLogError(@"-[%@ %@] expects 7 elements, received %@", self.class, NSStringFromSelector(_cmd), array); return nil; } _radius = [RCTConvert CGFloat:array[3]]; _radiusRatio = [RCTConvert CGFloat:array[4]] / _radius; _focusPoint.x = [RCTConvert CGFloat:array[1]]; _focusPoint.y = [RCTConvert CGFloat:array[2]] / _radiusRatio; _centerPoint.x = [RCTConvert CGFloat:array[5]]; _centerPoint.y = [RCTConvert CGFloat:array[6]] / _radiusRatio; _gradient = CGGradientRetain([RCTConvert CGGradient:array offset:7]); } return self; } - (void)dealloc { CGGradientRelease(_gradient); } - (void)paint:(CGContextRef)context { CGAffineTransform transform = CGAffineTransformMakeScale(1, _radiusRatio); CGContextConcatCTM(context, transform); CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; CGContextDrawRadialGradient(context, _gradient, _focusPoint, 0, _centerPoint, _radius, extendOptions); } @end ================================================ FILE: Libraries/ART/Brushes/ARTSolidColor.h ================================================ /** * 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. */ #import "ARTBrush.h" @interface ARTSolidColor : ARTBrush @end ================================================ FILE: Libraries/ART/Brushes/ARTSolidColor.m ================================================ /** * 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. */ #import "ARTSolidColor.h" #import #import "RCTConvert+ART.h" @implementation ARTSolidColor { CGColorRef _color; } - (instancetype)initWithArray:(NSArray *)array { if ((self = [super initWithArray:array])) { _color = CGColorRetain([RCTConvert CGColor:array offset:1]); } return self; } - (void)dealloc { CGColorRelease(_color); } - (BOOL)applyFillColor:(CGContextRef)context { CGContextSetFillColorWithColor(context, _color); return YES; } @end ================================================ FILE: Libraries/ART/RCTConvert+ART.h ================================================ /** * 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. */ #import #import #import "ARTBrush.h" #import "ARTCGFloatArray.h" #import "ARTTextFrame.h" @interface RCTConvert (ART) + (CGPathRef)CGPath:(id)json; + (CTTextAlignment)CTTextAlignment:(id)json; + (ARTTextFrame)ARTTextFrame:(id)json; + (ARTCGFloatArray)ARTCGFloatArray:(id)json; + (ARTBrush *)ARTBrush:(id)json; + (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset; + (CGRect)CGRect:(id)json offset:(NSUInteger)offset; + (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset; + (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset; @end ================================================ FILE: Libraries/ART/RCTConvert+ART.m ================================================ /** * 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. */ #import "RCTConvert+ART.h" #import #import #import "ARTLinearGradient.h" #import "ARTPattern.h" #import "ARTRadialGradient.h" #import "ARTSolidColor.h" @implementation RCTConvert (ART) + (CGPathRef)CGPath:(id)json { NSArray *arr = [self NSNumberArray:json]; NSUInteger count = [arr count]; #define NEXT_VALUE [self double:arr[i++]] CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, 0, 0); @try { NSUInteger i = 0; while (i < count) { NSUInteger type = [arr[i++] unsignedIntegerValue]; switch (type) { case 0: CGPathMoveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE); break; case 1: CGPathCloseSubpath(path); break; case 2: CGPathAddLineToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE); break; case 3: CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE); break; case 4: CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0); break; default: RCTLogError(@"Invalid CGPath type %llu at element %llu of %@", (unsigned long long)type, (unsigned long long)i, arr); CGPathRelease(path); return NULL; } } } @catch (NSException *exception) { RCTLogError(@"Invalid CGPath format: %@", arr); CGPathRelease(path); return NULL; } return (CGPathRef)CFAutorelease(path); } RCT_ENUM_CONVERTER(CTTextAlignment, (@{ @"auto": @(kCTTextAlignmentNatural), @"left": @(kCTTextAlignmentLeft), @"center": @(kCTTextAlignmentCenter), @"right": @(kCTTextAlignmentRight), @"justify": @(kCTTextAlignmentJustified), }), kCTTextAlignmentNatural, integerValue) // This takes a tuple of text lines and a font to generate a CTLine for each text line. // This prepares everything for rendering a frame of text in ARTText. + (ARTTextFrame)ARTTextFrame:(id)json { NSDictionary *dict = [self NSDictionary:json]; ARTTextFrame frame; frame.count = 0; NSArray *lines = [self NSArray:dict[@"lines"]]; NSUInteger lineCount = [lines count]; if (lineCount == 0) { return frame; } NSDictionary *fontDict = dict[@"font"]; CTFontRef font = (__bridge CTFontRef)[self NSFont:dict[@"font"]]; if (!font) { return frame; } // Create a dictionary for this font CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (NSString *)kCTFontAttributeName:(__bridge id)font, (NSString *)kCTForegroundColorFromContextAttributeName: @YES }; // Set up text frame with font metrics CGFloat size = CTFontGetSize(font); frame.count = lineCount; frame.baseLine = size; // estimate base line frame.lineHeight = size * 1.1; // Base on ART canvas line height estimate frame.lines = malloc(sizeof(CTLineRef) * lineCount); frame.widths = malloc(sizeof(CGFloat) * lineCount); [lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) { CFStringRef string = (__bridge CFStringRef)text; CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); CTLineRef line = CTLineCreateWithAttributedString(attrString); CFRelease(attrString); frame.lines[i] = line; frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL); }]; return frame; } + (ARTCGFloatArray)ARTCGFloatArray:(id)json { NSArray *arr = [self NSNumberArray:json]; NSUInteger count = arr.count; ARTCGFloatArray array; array.count = count; array.array = NULL; if (count) { // Ideally, these arrays should already use the same memory layout. // In that case we shouldn't need this new malloc. array.array = malloc(sizeof(CGFloat) * count); for (NSUInteger i = 0; i < count; i++) { array.array[i] = [arr[i] doubleValue]; } } return array; } + (ARTBrush *)ARTBrush:(id)json { NSArray *arr = [self NSArray:json]; NSUInteger type = [self NSUInteger:arr.firstObject]; switch (type) { case 0: // solid color // These are probably expensive allocations since it's often the same value. // We should memoize colors but look ups may be just as expensive. return [[ARTSolidColor alloc] initWithArray:arr]; case 1: // linear gradient return [[ARTLinearGradient alloc] initWithArray:arr]; case 2: // radial gradient return [[ARTRadialGradient alloc] initWithArray:arr]; case 3: // pattern return [[ARTPattern alloc] initWithArray:arr]; default: RCTLogError(@"Unknown brush type: %llu", (unsigned long long)type); return nil; } } + (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset { NSArray *arr = [self NSArray:json]; if (arr.count < offset + 2) { RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(2 + offset), arr); return CGPointZero; } return (CGPoint){ [self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]], }; } + (CGRect)CGRect:(id)json offset:(NSUInteger)offset { NSArray *arr = [self NSArray:json]; if (arr.count < offset + 4) { RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(4 + offset), arr); return CGRectZero; } return (CGRect){ {[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]}, {[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]}, }; } + (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset { NSArray *arr = [self NSArray:json]; if (arr.count < offset + 4) { RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(4 + offset), arr); return NULL; } return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]]; } + (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset { NSArray *arr = [self NSArray:json]; if (arr.count < offset) { RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)offset, arr); return NULL; } arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}]; ARTCGFloatArray colorsAndOffsets = [self ARTCGFloatArray:arr]; size_t stops = colorsAndOffsets.count / 5; CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); CGGradientRef gradient = CGGradientCreateWithColorComponents( rgb, colorsAndOffsets.array, colorsAndOffsets.array + stops * 4, stops ); CGColorSpaceRelease(rgb); free(colorsAndOffsets.array); return (CGGradientRef)CFAutorelease(gradient); } @end ================================================ FILE: Libraries/ART/ReactNativeART.js ================================================ /** * 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 ReactNativeART */ 'use strict'; var Color = require('art/core/color'); var Path = require('ARTSerializablePath'); var Transform = require('art/core/transform'); var React = require('React'); var PropTypes = require('prop-types'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var createReactNativeComponentClass = require('createReactNativeComponentClass'); var merge = require('merge'); var invariant = require('fbjs/lib/invariant'); // Diff Helpers function arrayDiffer(a, b) { if (a == null || b == null) { return true; } if (a.length !== b.length) { return true; } for (var i = 0; i < a.length; i++) { if (a[i] !== b[i]) { return true; } } return false; } function fontAndLinesDiffer(a, b) { if (a === b) { return false; } if (a.font !== b.font) { if (a.font === null) { return true; } if (b.font === null) { return true; } if ( a.font.fontFamily !== b.font.fontFamily || a.font.fontSize !== b.font.fontSize || a.font.fontWeight !== b.font.fontWeight || a.font.fontStyle !== b.font.fontStyle ) { return true; } } return arrayDiffer(a.lines, b.lines); } // Native Attributes var SurfaceViewAttributes = merge(ReactNativeViewAttributes.UIView, { // This should contain pixel information such as width, height and // resolution to know what kind of buffer needs to be allocated. // Currently we rely on UIViews and style to figure that out. }); var NodeAttributes = { transform: { diff: arrayDiffer }, opacity: true, }; var GroupAttributes = merge(NodeAttributes, { clipping: { diff: arrayDiffer } }); var RenderableAttributes = merge(NodeAttributes, { fill: { diff: arrayDiffer }, stroke: { diff: arrayDiffer }, strokeWidth: true, strokeCap: true, strokeJoin: true, strokeDash: { diff: arrayDiffer }, }); var ShapeAttributes = merge(RenderableAttributes, { d: { diff: arrayDiffer }, }); var TextAttributes = merge(RenderableAttributes, { alignment: true, frame: { diff: fontAndLinesDiffer }, path: { diff: arrayDiffer } }); // Native Components var NativeSurfaceView = createReactNativeComponentClass('ARTSurfaceView', () => ({ validAttributes: SurfaceViewAttributes, uiViewClassName: 'ARTSurfaceView', })); var NativeGroup = createReactNativeComponentClass('ARTGroup', () => ({ validAttributes: GroupAttributes, uiViewClassName: 'ARTGroup', })); var NativeShape = createReactNativeComponentClass('ARTShape', () => ({ validAttributes: ShapeAttributes, uiViewClassName: 'ARTShape', })); var NativeText = createReactNativeComponentClass('ARTText', () => ({ validAttributes: TextAttributes, uiViewClassName: 'ARTText', })); // Utilities function childrenAsString(children) { if (!children) { return ''; } if (typeof children === 'string') { return children; } if (children.length) { return children.join('\n'); } return ''; } // Surface - Root node of all ART class Surface extends React.Component { static childContextTypes = { isInSurface: PropTypes.bool, }; getChildContext() { return { isInSurface: true }; } render() { var props = this.props; var w = extractNumber(props.width, 0); var h = extractNumber(props.height, 0); return ( {this.props.children} ); } } // Node Props // TODO: The desktop version of ART has title and cursor. We should have // accessibility support here too even though hovering doesn't work. function extractNumber(value, defaultValue) { if (value == null) { return defaultValue; } return +value; } var pooledTransform = new Transform(); function extractTransform(props) { var scaleX = props.scaleX != null ? props.scaleX : props.scale != null ? props.scale : 1; var scaleY = props.scaleY != null ? props.scaleY : props.scale != null ? props.scale : 1; pooledTransform .transformTo(1, 0, 0, 1, 0, 0) .move(props.x || 0, props.y || 0) .rotate(props.rotation || 0, props.originX, props.originY) .scale(scaleX, scaleY, props.originX, props.originY); if (props.transform != null) { pooledTransform.transform(props.transform); } return [ pooledTransform.xx, pooledTransform.yx, pooledTransform.xy, pooledTransform.yy, pooledTransform.x, pooledTransform.y, ]; } function extractOpacity(props) { // TODO: visible === false should also have no hit detection if (props.visible === false) { return 0; } if (props.opacity == null) { return 1; } return +props.opacity; } // Groups // Note: ART has a notion of width and height on Group but AFAIK it's a noop in // ReactART. class Group extends React.Component { static contextTypes = { isInSurface: PropTypes.bool.isRequired, }; render() { var props = this.props; invariant( this.context.isInSurface, 'ART: must be a child of a ' ); return ( {this.props.children} ); } } class ClippingRectangle extends React.Component { render() { var props = this.props; var x = extractNumber(props.x, 0); var y = extractNumber(props.y, 0); var w = extractNumber(props.width, 0); var h = extractNumber(props.height, 0); var clipping = [x, y, w, h]; // The current clipping API requires x and y to be ignored in the transform var propsExcludingXAndY = merge(props); delete propsExcludingXAndY.x; delete propsExcludingXAndY.y; return ( {this.props.children} ); } } // Renderables var SOLID_COLOR = 0; var LINEAR_GRADIENT = 1; var RADIAL_GRADIENT = 2; var PATTERN = 3; function insertColorIntoArray(color, targetArray, atIndex) { var c = new Color(color); targetArray[atIndex + 0] = c.red / 255; targetArray[atIndex + 1] = c.green / 255; targetArray[atIndex + 2] = c.blue / 255; targetArray[atIndex + 3] = c.alpha; } function insertColorsIntoArray(stops, targetArray, atIndex) { var i = 0; if ('length' in stops) { while (i < stops.length) { insertColorIntoArray(stops[i], targetArray, atIndex + i * 4); i++; } } else { for (var offset in stops) { insertColorIntoArray(stops[offset], targetArray, atIndex + i * 4); i++; } } return atIndex + i * 4; } function insertOffsetsIntoArray(stops, targetArray, atIndex, multi, reverse) { var offsetNumber; var i = 0; if ('length' in stops) { while (i < stops.length) { offsetNumber = i / (stops.length - 1) * multi; targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber; i++; } } else { for (var offsetString in stops) { offsetNumber = (+offsetString) * multi; targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber; i++; } } return atIndex + i; } function insertColorStopsIntoArray(stops, targetArray, atIndex) { var lastIndex = insertColorsIntoArray(stops, targetArray, atIndex); insertOffsetsIntoArray(stops, targetArray, lastIndex, 1, false); } function insertDoubleColorStopsIntoArray(stops, targetArray, atIndex) { var lastIndex = insertColorsIntoArray(stops, targetArray, atIndex); lastIndex = insertColorsIntoArray(stops, targetArray, lastIndex); lastIndex = insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, false); insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, true); } function applyBoundingBoxToBrushData(brushData, props) { var type = brushData[0]; var width = +props.width; var height = +props.height; if (type === LINEAR_GRADIENT) { brushData[1] *= width; brushData[2] *= height; brushData[3] *= width; brushData[4] *= height; } else if (type === RADIAL_GRADIENT) { brushData[1] *= width; brushData[2] *= height; brushData[3] *= width; brushData[4] *= height; brushData[5] *= width; brushData[6] *= height; } else if (type === PATTERN) { // todo } } function extractBrush(colorOrBrush, props) { if (colorOrBrush == null) { return null; } if (colorOrBrush._brush) { if (colorOrBrush._bb) { // The legacy API for Gradients allow for the bounding box to be used // as a convenience for specifying gradient positions. This should be // deprecated. It's not properly implemented in canvas mode. ReactART // doesn't handle update to the bounding box correctly. That's why we // mutate this so that if it's reused, we reuse the same resolved box. applyBoundingBoxToBrushData(colorOrBrush._brush, props); colorOrBrush._bb = false; } return colorOrBrush._brush; } var c = new Color(colorOrBrush); return [SOLID_COLOR, c.red / 255, c.green / 255, c.blue / 255, c.alpha]; } function extractColor(color) { if (color == null) { return null; } var c = new Color(color); return [c.red / 255, c.green / 255, c.blue / 255, c.alpha]; } function extractStrokeCap(strokeCap) { switch (strokeCap) { case 'butt': return 0; case 'square': return 2; default: return 1; // round } } function extractStrokeJoin(strokeJoin) { switch (strokeJoin) { case 'miter': return 0; case 'bevel': return 2; default: return 1; // round } } // Shape // Note: ART has a notion of width and height on Shape but AFAIK it's a noop in // ReactART. class Shape extends React.Component { render() { var props = this.props; var path = props.d || childrenAsString(props.children); var d = (path instanceof Path ? path : new Path(path)).toJSON(); return ( ); } } // Text var cachedFontObjectsFromString = {}; var fontFamilyPrefix = /^[\s"']*/; var fontFamilySuffix = /[\s"']*$/; function extractSingleFontFamily(fontFamilyString) { // ART on the web allows for multiple font-families to be specified. // For compatibility, we extract the first font-family, hoping // we'll get a match. return fontFamilyString.split(',')[0] .replace(fontFamilyPrefix, '') .replace(fontFamilySuffix, ''); } function parseFontString(font) { if (cachedFontObjectsFromString.hasOwnProperty(font)) { return cachedFontObjectsFromString[font]; } var regexp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm\%]*(?:\s*\/.*?)?\s+)?\s*\"?([^\"]*)/i; var match = regexp.exec(font); if (!match) { return null; } var fontFamily = extractSingleFontFamily(match[3]); var fontSize = +match[2] || 12; var isBold = /bold/.exec(match[1]); var isItalic = /italic/.exec(match[1]); cachedFontObjectsFromString[font] = { fontFamily: fontFamily, fontSize: fontSize, fontWeight: isBold ? 'bold' : 'normal', fontStyle: isItalic ? 'italic' : 'normal', }; return cachedFontObjectsFromString[font]; } function extractFont(font) { if (font == null) { return null; } if (typeof font === 'string') { return parseFontString(font); } var fontFamily = extractSingleFontFamily(font.fontFamily); var fontSize = +font.fontSize || 12; var fontWeight = font.fontWeight != null ? font.fontWeight.toString() : '400'; return { // Normalize fontFamily: fontFamily, fontSize: fontSize, fontWeight: fontWeight, fontStyle: font.fontStyle, }; } var newLine = /\n/g; function extractFontAndLines(font, text) { return { font: extractFont(font), lines: text.split(newLine) }; } function extractAlignment(alignment) { switch (alignment) { case 'right': return 1; case 'center': return 2; default: return 0; } } class Text extends React.Component { render() { var props = this.props; var path = props.path; var textPath = path ? (path instanceof Path ? path : new Path(path)).toJSON() : null; var textFrame = extractFontAndLines( props.font, childrenAsString(props.children) ); return ( ); } } // Declarative fill type objects - API design not finalized function LinearGradient(stops, x1, y1, x2, y2) { var type = LINEAR_GRADIENT; if (arguments.length < 5) { var angle = ((x1 == null) ? 270 : x1) * Math.PI / 180; var x = Math.cos(angle); var y = -Math.sin(angle); var l = (Math.abs(x) + Math.abs(y)) / 2; x *= l; y *= l; x1 = 0.5 - x; x2 = 0.5 + x; y1 = 0.5 - y; y2 = 0.5 + y; this._bb = true; } else { this._bb = false; } var brushData = [type, +x1, +y1, +x2, +y2]; insertColorStopsIntoArray(stops, brushData, 5); this._brush = brushData; } function RadialGradient(stops, fx, fy, rx, ry, cx, cy) { if (ry == null) { ry = rx; } if (cx == null) { cx = fx; } if (cy == null) { cy = fy; } if (fx == null) { // As a convenience we allow the whole radial gradient to cover the // bounding box. We should consider dropping this API. fx = fy = rx = ry = cx = cy = 0.5; this._bb = true; } else { this._bb = false; } // The ART API expects the radial gradient to be repeated at the edges. // To simulate this we render the gradient twice as large and add double // color stops. Ideally this API would become more restrictive so that this // extra work isn't needed. var brushData = [RADIAL_GRADIENT, +fx, +fy, +rx * 2, +ry * 2, +cx, +cy]; insertDoubleColorStopsIntoArray(stops, brushData, 7); this._brush = brushData; } function Pattern(url, width, height, left, top) { this._brush = [PATTERN, url, +left || 0, +top || 0, +width, +height]; } var ReactART = { LinearGradient: LinearGradient, RadialGradient: RadialGradient, Pattern: Pattern, Transform: Transform, Path: Path, Surface: Surface, Group: Group, ClippingRectangle: ClippingRectangle, Shape: Shape, Text: Text, }; module.exports = ReactART; ================================================ FILE: Libraries/ART/ViewManagers/ARTGroupManager.h ================================================ /** * 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. */ #import "ARTNodeManager.h" @interface ARTGroupManager : ARTNodeManager @end ================================================ FILE: Libraries/ART/ViewManagers/ARTGroupManager.m ================================================ /** * 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. */ #import "ARTGroupManager.h" #import "ARTGroup.h" #import "RCTConvert+ART.h" @implementation ARTGroupManager RCT_EXPORT_MODULE() - (ARTNode *)node { return [ARTGroup new]; } RCT_EXPORT_VIEW_PROPERTY(clipping, CGRect) @end ================================================ FILE: Libraries/ART/ViewManagers/ARTNodeManager.h ================================================ /** * 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. */ #import @class ARTNode; @interface ARTNodeManager : RCTViewManager - (ARTNode *)node; @end ================================================ FILE: Libraries/ART/ViewManagers/ARTNodeManager.m ================================================ /** * 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. */ #import "ARTNodeManager.h" #import "ARTNode.h" @implementation ARTNodeManager RCT_EXPORT_MODULE() - (ARTNode *)node { return [ARTNode new]; } - (NSView *)view { return [self node]; } - (RCTShadowView *)shadowView { return nil; } RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform) @end ================================================ FILE: Libraries/ART/ViewManagers/ARTRenderableManager.h ================================================ /** * 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. */ #import "ARTNodeManager.h" #import "ARTRenderable.h" @interface ARTRenderableManager : ARTNodeManager - (ARTRenderable *)node; @end ================================================ FILE: Libraries/ART/ViewManagers/ARTRenderableManager.m ================================================ /** * 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. */ #import "ARTRenderableManager.h" #import "RCTConvert+ART.h" @implementation ARTRenderableManager RCT_EXPORT_MODULE() - (ARTRenderable *)node { return [ARTRenderable new]; } RCT_EXPORT_VIEW_PROPERTY(strokeWidth, CGFloat) RCT_EXPORT_VIEW_PROPERTY(strokeCap, CGLineCap) RCT_EXPORT_VIEW_PROPERTY(strokeJoin, CGLineJoin) RCT_EXPORT_VIEW_PROPERTY(fill, ARTBrush) RCT_EXPORT_VIEW_PROPERTY(stroke, CGColor) RCT_EXPORT_VIEW_PROPERTY(strokeDash, ARTCGFloatArray) @end ================================================ FILE: Libraries/ART/ViewManagers/ARTShapeManager.h ================================================ /** * 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. */ #import "ARTRenderableManager.h" @interface ARTShapeManager : ARTRenderableManager @end ================================================ FILE: Libraries/ART/ViewManagers/ARTShapeManager.m ================================================ /** * 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. */ #import "ARTShapeManager.h" #import "ARTShape.h" #import "RCTConvert+ART.h" @implementation ARTShapeManager RCT_EXPORT_MODULE() - (ARTRenderable *)node { return [ARTShape new]; } RCT_EXPORT_VIEW_PROPERTY(d, CGPath) @end ================================================ FILE: Libraries/ART/ViewManagers/ARTSurfaceViewManager.h ================================================ /** * 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. */ #import @interface ARTSurfaceViewManager : RCTViewManager @end ================================================ FILE: Libraries/ART/ViewManagers/ARTSurfaceViewManager.m ================================================ /** * 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. */ #import "ARTSurfaceViewManager.h" #import "ARTSurfaceView.h" @implementation ARTSurfaceViewManager RCT_EXPORT_MODULE() - (NSView *)view { return [ARTSurfaceView new]; } @end ================================================ FILE: Libraries/ART/ViewManagers/ARTTextManager.h ================================================ /** * 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. */ #import "ARTRenderableManager.h" @interface ARTTextManager : ARTRenderableManager @end ================================================ FILE: Libraries/ART/ViewManagers/ARTTextManager.m ================================================ /** * 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. */ #import "ARTTextManager.h" #import "ARTText.h" #import "RCTConvert+ART.h" @implementation ARTTextManager RCT_EXPORT_MODULE() - (ARTRenderable *)node { return [ARTText new]; } RCT_EXPORT_VIEW_PROPERTY(alignment, CTTextAlignment) RCT_REMAP_VIEW_PROPERTY(frame, textFrame, ARTTextFrame) @end ================================================ FILE: Libraries/ActionSheetIOS/ActionSheetIOS.js ================================================ /** * 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 ActionSheetIOS * @flow * @format */ 'use strict'; const RCTActionSheetManager = require('NativeModules').ActionSheetManager; const invariant = require('fbjs/lib/invariant'); const processColor = require('processColor'); /** * Display action sheets and share sheets on iOS. * * See http://facebook.github.io/react-native/docs/actionsheetios.html */ const ActionSheetIOS = { /** * Display an iOS action sheet. * * The `options` object must contain one or more of: * * - `options` (array of strings) - a list of button titles (required) * - `cancelButtonIndex` (int) - index of cancel button in `options` * - `destructiveButtonIndex` (int) - index of destructive button in `options` * - `title` (string) - a title to show above the action sheet * - `message` (string) - a message to show below the title * * The 'callback' function takes one parameter, the zero-based index * of the selected item. * * See http://facebook.github.io/react-native/docs/actionsheetios.html#showactionsheetwithoptions */ showActionSheetWithOptions( options: {| +title?: ?string, +message?: ?string, +options: Array, +destructiveButtonIndex?: ?number, +cancelButtonIndex?: ?number, +anchor?: ?number, +tintColor?: number | string, |}, callback: (buttonIndex: number) => void, ) { invariant( typeof options === 'object' && options !== null, 'Options must be a valid object', ); invariant(typeof callback === 'function', 'Must provide a valid callback'); RCTActionSheetManager.showActionSheetWithOptions( {...options, tintColor: processColor(options.tintColor)}, callback, ); }, /** * Display the iOS share sheet. The `options` object should contain * one or both of `message` and `url` and can additionally have * a `subject` or `excludedActivityTypes`: * * - `url` (string) - a URL to share * - `message` (string) - a message to share * - `subject` (string) - a subject for the message * - `excludedActivityTypes` (array) - the activities to exclude from * the ActionSheet * - `tintColor` (color) - tint color of the buttons * * The 'failureCallback' function takes one parameter, an error object. * The only property defined on this object is an optional `stack` property * of type `string`. * * The 'successCallback' function takes two parameters: * * - a boolean value signifying success or failure * - a string that, in the case of success, indicates the method of sharing * * See http://facebook.github.io/react-native/docs/actionsheetios.html#showshareactionsheetwithoptions */ showShareActionSheetWithOptions( options: Object, failureCallback: Function, successCallback: Function, ) { invariant( typeof options === 'object' && options !== null, 'Options must be a valid object', ); invariant( typeof failureCallback === 'function', 'Must provide a valid failureCallback', ); invariant( typeof successCallback === 'function', 'Must provide a valid successCallback', ); RCTActionSheetManager.showShareActionSheetWithOptions( {...options, tintColor: processColor(options.tintColor)}, failureCallback, successCallback, ); }, }; module.exports = ActionSheetIOS; ================================================ FILE: Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 14C644C41AB0DFC900DE3C65 /* RCTActionSheetManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C644C21AB0DFC900DE3C65 /* RCTActionSheetManager.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 134814201AA4EA6300B7C361 /* libRCTActionSheet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTActionSheet.a; sourceTree = BUILT_PRODUCTS_DIR; }; 14C644C11AB0DFC900DE3C65 /* RCTActionSheetManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTActionSheetManager.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 14C644C21AB0DFC900DE3C65 /* RCTActionSheetManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTActionSheetManager.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ 134814211AA4EA7D00B7C361 /* Products */ = { isa = PBXGroup; children = ( 134814201AA4EA6300B7C361 /* libRCTActionSheet.a */, ); name = Products; sourceTree = ""; }; 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( 14C644C11AB0DFC900DE3C65 /* RCTActionSheetManager.h */, 14C644C21AB0DFC900DE3C65 /* RCTActionSheetManager.m */, 134814211AA4EA7D00B7C361 /* Products */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 58B511DA1A9E6C8500147676 /* RCTActionSheet */ = { isa = PBXNativeTarget; buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTActionSheet" */; buildPhases = ( 58B511D71A9E6C8500147676 /* Sources */, ); buildRules = ( ); dependencies = ( ); name = RCTActionSheet; productName = RCTDataManager; productReference = 134814201AA4EA6300B7C361 /* libRCTActionSheet.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 58B511D31A9E6C8500147676 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0610; ORGANIZATIONNAME = Facebook; TargetAttributes = { 58B511DA1A9E6C8500147676 = { CreatedOnToolsVersion = 6.1.1; }; }; }; buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTActionSheet" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 58B511D21A9E6C8500147676; productRefGroup = 58B511D21A9E6C8500147676; projectDirPath = ""; projectRoot = ""; targets = ( 58B511DA1A9E6C8500147676 /* RCTActionSheet */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 58B511D71A9E6C8500147676 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14C644C41AB0DFC900DE3C65 /* RCTActionSheetManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 58B511ED1A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; GCC_WARN_SHADOW = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SKIP_INSTALL = YES; WARNING_CFLAGS = ( "-Werror", "-Wall", ); }; name = Debug; }; 58B511EE1A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; GCC_WARN_SHADOW = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; VALIDATE_PRODUCT = YES; WARNING_CFLAGS = ( "-Werror", "-Wall", ); }; name = Release; }; 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_STATIC_ANALYZER_MODE = deep; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTActionSheet; RUN_CLANG_STATIC_ANALYZER = YES; }; name = Debug; }; 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_STATIC_ANALYZER_MODE = deep; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTActionSheet; RUN_CLANG_STATIC_ANALYZER = NO; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTActionSheet" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511ED1A9E6C8500147676 /* Debug */, 58B511EE1A9E6C8500147676 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTActionSheet" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511F01A9E6C8500147676 /* Debug */, 58B511F11A9E6C8500147676 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 58B511D31A9E6C8500147676 /* Project object */; } ================================================ FILE: Libraries/ActionSheetIOS/RCTActionSheetManager.h ================================================ /** * 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. */ #import #import @interface RCTActionSheetManager : NSObject @end ================================================ FILE: Libraries/ActionSheetIOS/RCTActionSheetManager.m ================================================ /** * 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. */ #import "RCTActionSheetManager.h" #import #import #import #import #import @interface RCTActionSheetManager () @end @implementation RCTActionSheetManager { // Use NSMapTable, as UIAlertViews do not implement // which is required for NSDictionary keys NSMapTable *_callbacks; } RCT_EXPORT_MODULE() @synthesize bridge = _bridge; - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); } /* * The `anchor` option takes a view to set as the anchor for the share * popup to point to, on iPads running iOS 8. If it is not passed, it * defaults to centering the share popup on screen without any arrows. */ - (CGRect)sourceRectInView:(UIView *)sourceView anchorViewTag:(NSNumber *)anchorViewTag { if (anchorViewTag) { UIView *anchorView = [self.bridge.uiManager viewForReactTag:anchorViewTag]; return [anchorView convertRect:anchorView.bounds toView:sourceView]; } else { return (CGRect){sourceView.center, {1, 1}}; } } RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) { if (RCTRunningInAppExtension()) { RCTLogError(@"Unable to show action sheet from app extension"); return; } if (!_callbacks) { _callbacks = [NSMapTable strongToStrongObjectsMapTable]; } NSString *title = [RCTConvert NSString:options[@"title"]]; NSString *message = [RCTConvert NSString:options[@"message"]]; NSArray *buttons = [RCTConvert NSStringArray:options[@"options"]]; NSInteger destructiveButtonIndex = options[@"destructiveButtonIndex"] ? [RCTConvert NSInteger:options[@"destructiveButtonIndex"]] : -1; NSInteger cancelButtonIndex = options[@"cancelButtonIndex"] ? [RCTConvert NSInteger:options[@"cancelButtonIndex"]] : -1; UIViewController *controller = RCTPresentedViewController(); if (controller == nil) { RCTLogError(@"Tried to display action sheet but there is no application window. options: %@", options); return; } /* * The `anchor` option takes a view to set as the anchor for the share * popup to point to, on iPads running iOS 8. If it is not passed, it * defaults to centering the share popup on screen without any arrows. */ NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]]; UIView *sourceView = controller.view; CGRect sourceRect = [self sourceRectInView:sourceView anchorViewTag:anchorViewTag]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; NSInteger index = 0; for (NSString *option in buttons) { UIAlertActionStyle style = UIAlertActionStyleDefault; if (index == destructiveButtonIndex) { style = UIAlertActionStyleDestructive; } else if (index == cancelButtonIndex) { style = UIAlertActionStyleCancel; } NSInteger localIndex = index; [alertController addAction:[UIAlertAction actionWithTitle:option style:style handler:^(__unused UIAlertAction *action){ callback(@[@(localIndex)]); }]]; index++; } alertController.modalPresentationStyle = UIModalPresentationPopover; alertController.popoverPresentationController.sourceView = sourceView; alertController.popoverPresentationController.sourceRect = sourceRect; if (!anchorViewTag) { alertController.popoverPresentationController.permittedArrowDirections = 0; } [controller presentViewController:alertController animated:YES completion:nil]; alertController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]]; } RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options failureCallback:(RCTResponseErrorBlock)failureCallback successCallback:(RCTResponseSenderBlock)successCallback) { if (RCTRunningInAppExtension()) { RCTLogError(@"Unable to show action sheet from app extension"); return; } NSMutableArray *items = [NSMutableArray array]; NSString *message = [RCTConvert NSString:options[@"message"]]; if (message) { [items addObject:message]; } NSURL *URL = [RCTConvert NSURL:options[@"url"]]; if (URL) { if ([URL.scheme.lowercaseString isEqualToString:@"data"]) { NSError *error; NSData *data = [NSData dataWithContentsOfURL:URL options:(NSDataReadingOptions)0 error:&error]; if (!data) { failureCallback(error); return; } [items addObject:data]; } else { [items addObject:URL]; } } if (items.count == 0) { RCTLogError(@"No `url` or `message` to share"); return; } UIActivityViewController *shareController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil]; NSString *subject = [RCTConvert NSString:options[@"subject"]]; if (subject) { [shareController setValue:subject forKey:@"subject"]; } NSArray *excludedActivityTypes = [RCTConvert NSStringArray:options[@"excludedActivityTypes"]]; if (excludedActivityTypes) { shareController.excludedActivityTypes = excludedActivityTypes; } UIViewController *controller = RCTPresentedViewController(); shareController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, __unused NSArray *returnedItems, NSError *activityError) { if (activityError) { failureCallback(activityError); } else { successCallback(@[@(completed), RCTNullIfNil(activityType)]); } }; shareController.modalPresentationStyle = UIModalPresentationPopover; NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]]; if (!anchorViewTag) { shareController.popoverPresentationController.permittedArrowDirections = 0; } shareController.popoverPresentationController.sourceView = controller.view; shareController.popoverPresentationController.sourceRect = [self sourceRectInView:controller.view anchorViewTag:anchorViewTag]; [controller presentViewController:shareController animated:YES completion:nil]; shareController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]]; } #pragma mark UIActionSheetDelegate Methods - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { RCTResponseSenderBlock callback = [_callbacks objectForKey:actionSheet]; if (callback) { callback(@[@(buttonIndex)]); [_callbacks removeObjectForKey:actionSheet]; } else { RCTLogWarn(@"No callback registered for action sheet: %@", actionSheet.title); } } @end ================================================ FILE: Libraries/Alert/Alert.js ================================================ /** * 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 Alert * @flow */ 'use strict'; const AlertIOS = require('AlertIOS'); const NativeModules = require('NativeModules'); const Platform = require('Platform'); import type { AlertType, AlertButtonStyle } from 'AlertIOS'; export type Buttons = Array<{ text?: string, onPress?: ?Function, style?: AlertButtonStyle, }>; type Options = { cancelable?: ?boolean, onDismiss?: ?Function, }; /** * Launches an alert dialog with the specified title and message. * * See http://facebook.github.io/react-native/docs/alert.html */ class Alert { /** * Launches an alert dialog with the specified title and message. * * See http://facebook.github.io/react-native/docs/alert.html#alert */ static alert( title: ?string, message?: ?string, buttons?: Buttons, options?: Options, type?: AlertType, ): void { if (Platform.OS === 'ios' || Platform.OS === 'macos') { if (typeof type !== 'undefined') { console.warn('Alert.alert() with a 5th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.'); AlertIOS.alert(title, message, buttons, type); return; } AlertIOS.alert(title, message, buttons); } else if (Platform.OS === 'android') { AlertAndroid.alert(title, message, buttons, options); } } } /** * Wrapper around the Android native module. */ class AlertAndroid { static alert( title: ?string, message?: ?string, buttons?: Buttons, options?: Options, ): void { var config = { title: title || '', message: message || '', }; if (options) { config = {...config, cancelable: options.cancelable}; } // At most three buttons (neutral, negative, positive). Ignore rest. // The text 'OK' should be probably localized. iOS Alert does that in native. var validButtons: Buttons = buttons ? buttons.slice(0, 3) : [{text: 'OK'}]; var buttonPositive = validButtons.pop(); var buttonNegative = validButtons.pop(); var buttonNeutral = validButtons.pop(); if (buttonNeutral) { config = {...config, buttonNeutral: buttonNeutral.text || '' }; } if (buttonNegative) { config = {...config, buttonNegative: buttonNegative.text || '' }; } if (buttonPositive) { config = {...config, buttonPositive: buttonPositive.text || '' }; } NativeModules.DialogManagerAndroid.showAlert( config, (errorMessage) => console.warn(errorMessage), (action, buttonKey) => { if (action === NativeModules.DialogManagerAndroid.buttonClicked) { if (buttonKey === NativeModules.DialogManagerAndroid.buttonNeutral) { buttonNeutral.onPress && buttonNeutral.onPress(); } else if (buttonKey === NativeModules.DialogManagerAndroid.buttonNegative) { buttonNegative.onPress && buttonNegative.onPress(); } else if (buttonKey === NativeModules.DialogManagerAndroid.buttonPositive) { buttonPositive.onPress && buttonPositive.onPress(); } } else if (action === NativeModules.DialogManagerAndroid.dismissed) { options && options.onDismiss && options.onDismiss(); } } ); } } module.exports = Alert; ================================================ FILE: Libraries/Alert/AlertIOS.js ================================================ /** * 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 AlertIOS * @flow * @jsdoc */ 'use strict'; var RCTAlertManager = require('NativeModules').AlertManager; /** * An Alert button type */ export type AlertType = $Enum<{ /** * Default alert with no inputs */ 'default': string, /** * Plain text input alert */ 'plain-text': string, /** * Secure text input alert */ 'secure-text': string, /** * Login and password alert */ 'login-password': string, }>; /** * An Alert button style */ export type AlertButtonStyle = $Enum<{ /** * Default button style */ 'default': string, /** * Cancel button style */ 'cancel': string, /** * Destructive button style */ 'destructive': string, }>; /** * Array or buttons * @typedef {Array} ButtonsArray * @property {string=} text Button label * @property {Function=} onPress Callback function when button pressed * @property {AlertButtonStyle=} style Button style */ export type ButtonsArray = Array<{ /** * Button label */ text?: string, /** * Callback function when button pressed */ onPress?: ?Function, /** * Button style */ style?: AlertButtonStyle, }>; /** * Use `AlertIOS` to display an alert dialog with a message or to create a prompt for user input on iOS. If you don't need to prompt for user input, we recommend using `Alert.alert() for cross-platform support. * * See http://facebook.github.io/react-native/docs/alertios.html */ class AlertIOS { /** * Create and display a popup alert. * * See http://facebook.github.io/react-native/docs/alertios.html#alert */ static alert( title: ?string, message?: ?string, callbackOrButtons?: ?(() => void) | ButtonsArray, type?: AlertType, ): void { if (typeof type !== 'undefined') { console.warn('AlertIOS.alert() with a 4th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.'); this.prompt(title, message, callbackOrButtons, type); return; } if (!callbackOrButtons) { callbackOrButtons = [{text: 'OK'}] } this.prompt(title, message, callbackOrButtons, 'default'); } /** * Create and display a prompt to enter some text. * * See http://facebook.github.io/react-native/docs/alertios.html#prompt */ static prompt( title: ?string, message?: ?string, callbackOrButtons?: ?((text: string) => void) | ButtonsArray, type?: ?AlertType = 'plain-text', defaultValue?: string, keyboardType?: string ): void { if (typeof type === 'function') { console.warn( 'You passed a callback function as the "type" argument to AlertIOS.prompt(). React Native is ' + 'assuming you want to use the deprecated AlertIOS.prompt(title, defaultValue, buttons, callback) ' + 'signature. The current signature is AlertIOS.prompt(title, message, callbackOrButtons, type, defaultValue, ' + 'keyboardType) and the old syntax will be removed in a future version.'); var callback = type; var defaultValue = message; RCTAlertManager.alertWithArgs({ title: title || '', type: 'plain-text', defaultValue, }, (id, value) => { callback(value); }); return; } var callbacks = []; var buttons = []; var cancelButtonKey; var destructiveButtonKey; if (typeof callbackOrButtons === 'function') { callbacks = [callbackOrButtons]; } else if (callbackOrButtons instanceof Array) { callbackOrButtons.forEach((btn, index) => { callbacks[index] = btn.onPress; if (btn.style === 'cancel') { cancelButtonKey = String(index); } else if (btn.style === 'destructive') { destructiveButtonKey = String(index); } if (btn.text || index < (callbackOrButtons || []).length - 1) { var btnDef = {}; btnDef[index] = btn.text || ''; buttons.push(btnDef); } }); } RCTAlertManager.alertWithArgs({ title: title || '', message: message || undefined, buttons, type: type || undefined, defaultValue, cancelButtonKey, destructiveButtonKey, keyboardType, }, (id, value) => { var cb = callbacks[id]; cb && cb(value); }); } } module.exports = AlertIOS; ================================================ FILE: Libraries/Alert/RCTAlertManager.android.js ================================================ /** * Copyright (c) 2013-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 RCTAlertManager */ 'use strict'; var NativeModules = require('NativeModules'); function emptyCallback() {} module.exports = { alertWithArgs: function(args, callback) { // TODO(5998984): Polyfill it correctly with DialogManagerAndroid NativeModules.DialogManagerAndroid.showAlert( args, emptyCallback, callback || emptyCallback); }, }; ================================================ FILE: Libraries/Alert/RCTAlertManager.ios.js ================================================ /** * 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 RCTAlertManager * @flow */ 'use strict'; var RCTAlertManager = require('NativeModules').AlertManager; module.exports = RCTAlertManager; ================================================ FILE: Libraries/Animated/examples/demo.html ================================================ Animated

Animated

Animations have for a long time been a weak point of the React ecosystem. The Animated library aims at solving this problem. It embraces the declarative aspect of React and obtains performance by using raw DOM manipulation behind the scenes instead of the usual diff.

Animated.Value

The basic building block of this library is Animated.Value. This is a variable that's going to drive the animation. You use it like a normal value in style attribute. Only animated components such as Animated.div will understand it.

setValue

As you can see, the value is being used inside of render() as you would expect. However, you don't call setState() in order to update the value. Instead, you can call setValue() directly on the value itself. We are using a form of data binding.

The Animated.div component when rendered tracks which animated values it received. This way, whenever that value changes, we don't need to re-render the entire component, we can directly update the specific style attribute that changed.

Animated.timing

Now that we understand how the system works, let's play with some animations! The hello world of animations is to move the element somewhere else. To do that, we're going to animate the value from the current value 0 to the value 400.

On every frame (via requestAnimationFrame), the timing animation is going to figure out the new value based on the current time, update the animated value which in turn is going to update the corresponding DOM node.

Interrupt Animations

As a developer, the mental model is that most animations are fire and forget. When the user presses the button, you want it to shrink to 80% and when she releases, you want it to go back to 100%.

There are multiple challenges to implement this correctly. You need to stop the current animation, grab the current value and restart an animation from there. As this is pretty tedious to do manually, Animated will do that automatically for you.

Animated.spring

Unfortunately, the timing animation doesn't feel good. The main reason is that no matter how far you are in the animation, it will trigger a new one with always the same duration.

The commonly used solution for this problem is to use the equation of a real-world spring. Imagine that you attach a spring to the target value, stretch it to the current value and let it go. The spring movement is going to be the same as the update.

It turns out that this model is useful in a very wide range of animations. I highly recommend you to always start with a spring animation instead of a timing animation. It will make your interface feels much better.

interpolate

It is very common to animate multiple attributes during the same animation. The usual way to implement it is to start a separate animation for each of the attribute. The downside is that you now have to manage a different state per attribute which is not ideal.

With Animated, you can use a single state variable and render it in multiple attributes. When the value is updated, all the places will reflect the change.

In the following example, we're going to model the animation with a variable where 1 means fully visible and 0 means fully hidden. We can pass it directly to the scale attribute as the ranges match. But for the rotation, we need to convert [0 ; 1] range to [260deg ; 0deg]. This is where interpolate() comes handy.

stopAnimation

The reason why we can get away with not calling render() and instead modify the DOM directly on updates is because the animated values are opaque. In render, you cannot know the current value, which prevents you from being able to modify the structure of the DOM.

Animated can offload the animation to a different thread (CoreAnimation, CSS transitions, main thread...) and we don't have a good way to know the real value. If you try to query the value then modify it, you are going to be out of sync and the result will look terrible.

There's however one exception: when you want to stop the current animation. You need to know where it stopped in order to continue from there. We cannot know the value synchronously so we give it via a callback in stopAnimation. It will not suffer from being out of sync since the animation is no longer running.

Gesture-based Animations

Most animations libraries only deal with time-based animations. But, as we move to mobile, a lot of animations are also gesture driven. Even more problematic, they often switch between both modes: once the gesture is over, you start a time-based animation using the same interpolations.

Animated has been designed with this use case in mind. The key aspect is that there are three distinct and separate concepts: inputs, value, output. The same value can be updated either from a time-based animation or a gesture-based one. Because we use this intermediate representation for the animation, we can keep the same rendering as output.

HorizontalPan

The code needed to drag elements around is very messy with the DOM APIs. On mousedown, you need to register a mousemove listener on window otherwise you may drop touches if you move too fast. removeEventListener takes the same arguments as addEventListener instead of an id like clearTimeout. It's also really easy to forget to remove a listener and have a leak. And finally, you need to store the current position and value at the beginning and update only compared to it.

We introduce a little helper called HorizontalPan which handles all this annoying code for us. It takes an Animated.Value as first argument and returns the event handlers required for it to work. We just have to bind this value to the left attribute and we're good to go.

Animated.decay

One of the big breakthrough of the iPhone is the fact that when you release the finger while scrolling, it will not abruptly stop but instead keep going for some time.

In order to implement this effect, we are using a second real-world simulation: an object moving on an icy surface. All it needs is two values: the current velocity and a deceleration coefficient. It is implemented by Animated.decay.

Animation Chaining

The target for an animation is usually a number but sometimes it is convenient to use another value as a target. This way, the first value will track the second. Using a spring animation, we can get a nice trailing effect.

addListener

As I said earlier, if you track a spring

Animated.sequence

It is very common to animate

================================================ FILE: Libraries/Animated/examples/style.css ================================================ html, h1, h2 { font-family: 'Roboto', sans-serif; font-weight: 300; } .container { width: 800px; margin: 0 auto; } .circle { margin: 2px; width: 50px; height: 50px; position: absolute; display: inline-block; box-shadow: 0 1px 2px #999; text-shadow: 0 1px 2px #999; background-image: url(pic1.jpg); background-size: cover; line-height: 80px; vertical-align: bottom; text-align: center; color: white; font-size: 10px; } .circle:nth-child(2) { background-image: url(pic2.jpg); } .circle:nth-child(3) { background-image: url(pic3.jpg); } div.code { box-shadow: 0 1px 2px #999; width: 600px; padding: 5px; position: relative; margin: 0 auto; margin-bottom: 40px; } div.code .reset { float: right; } div.code pre { padding: 2px; } hr { border: none; border-bottom: 1px solid #D9D9D9; margin: 0; } button { vertical-align: top; } .example > span { color: #333; font-size: 13px; } .example { position: relative; height: 60px; } .code pre { margin: 0; font-size: 11px; line-height: 1; } .highlight { background: rgb(228, 254, 253); } ================================================ FILE: Libraries/Animated/release/gulpfile.js ================================================ /** * Copyright 2013-2015, 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 gulpfile */ 'use strict'; var babel = require('gulp-babel'); var babelPluginDEV = require('fbjs-scripts/babel/dev-expression'); var babelPluginModules = require('fbjs-scripts/babel/rewrite-modules'); var del = require('del'); var derequire = require('gulp-derequire'); var flatten = require('gulp-flatten'); var gulp = require('gulp'); var gulpUtil = require('gulp-util'); var header = require('gulp-header'); var objectAssign = require('object-assign'); var runSequence = require('run-sequence'); var webpackStream = require('webpack-stream'); var DEVELOPMENT_HEADER = [ '/**', ' * Animated v<%= version %>', ' */' ].join('\n') + '\n'; var PRODUCTION_HEADER = [ '/**', ' * Animated v<%= version %>', ' *', ' * Copyright 2013-2015, 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.', ' *', ' */' ].join('\n') + '\n'; var babelOpts = { nonStandard: true, loose: [ 'es6.classes' ], stage: 1, plugins: [babelPluginDEV, babelPluginModules], _moduleMap: objectAssign({}, require('fbjs/module-map'), { 'React': 'react', }) }; var buildDist = function(opts) { var webpackOpts = { debug: opts.debug, externals: { 'react': 'React', }, module: { loaders: [ {test: /\.js$/, loader: 'babel'} ], }, output: { filename: opts.output, library: 'Animated' }, plugins: [ new webpackStream.webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify( opts.debug ? 'development' : 'production' ), }), new webpackStream.webpack.optimize.OccurenceOrderPlugin(), new webpackStream.webpack.optimize.DedupePlugin() ] }; if (!opts.debug) { webpackOpts.plugins.push( new webpackStream.webpack.optimize.UglifyJsPlugin({ compress: { hoist_vars: true, screw_ie8: true, warnings: false } }) ); } return webpackStream(webpackOpts, null, function(err, stats) { if (err) { throw new gulpUtil.PluginError('webpack', err); } if (stats.compilation.errors.length) { throw new gulpUtil.PluginError('webpack', stats.toString()); } }); }; var paths = { dist: 'dist', entry: 'lib/AnimatedWeb.js', lib: 'lib', src: [ '*src/**/*.js', '!src/**/__tests__/**/*.js', '!src/**/__mocks__/**/*.js' ], }; gulp.task('clean', function(cb) { del([paths.dist, paths.lib], cb); }); gulp.task('modules', function() { return gulp .src(paths.src, {cwd: '../'}) .pipe(babel(babelOpts)) .pipe(flatten()) .pipe(gulp.dest(paths.lib)); }); gulp.task('dist', ['modules'], function () { var distOpts = { debug: true, output: 'animated.js' }; return gulp .src(paths.entry) .pipe(buildDist(distOpts)) .pipe(derequire()) .pipe(header(DEVELOPMENT_HEADER, { version: process.env.npm_package_version })) .pipe(gulp.dest(paths.dist)); }); gulp.task('dist:min', ['modules'], function () { var distOpts = { debug: false, output: 'animated.min.js' }; return gulp .src(paths.entry) .pipe(buildDist(distOpts)) .pipe(header(PRODUCTION_HEADER, { version: process.env.npm_package_version })) .pipe(gulp.dest(paths.dist)); }); gulp.task('watch', function() { gulp.watch(paths.src, ['modules']); }); gulp.task('default', function(cb) { runSequence('clean', 'modules', ['dist', 'dist:min'], cb); }); ================================================ FILE: Libraries/Animated/release/package.json ================================================ { "name": "react-animated", "description": "Animated provides powerful mechanisms for animating your React views", "version": "0.1.0", "keywords": [ "react", "animated", "animation" ], "license": "BSD-3-Clause", "main": "Animated.js", "dependencies": { "fbjs": "^0.2.1" }, "scripts": { "build": "gulp" }, "devDependencies": { "babel-core": "^5.8.25", "babel-loader": "^5.3.2", "del": "^1.2.0", "fbjs-scripts": "^0.2.0", "gulp": "^3.9.0", "gulp-babel": "^5.1.0", "gulp-derequire": "^2.1.0", "gulp-flatten": "^0.1.0", "gulp-header": "^1.2.2", "gulp-util": "^3.0.6", "object-assign": "^3.0.0", "run-sequence": "^1.1.2", "webpack": "1.11.0", "webpack-stream": "^2.1.0" } } ================================================ FILE: Libraries/Animated/src/Animated.js ================================================ /** * 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 Animated * @flow */ 'use strict'; var AnimatedImplementation = require('AnimatedImplementation'); var Image = require('Image'); var Text = require('Text'); var View = require('View'); let AnimatedScrollView; const Animated = { View: AnimatedImplementation.createAnimatedComponent(View), Text: AnimatedImplementation.createAnimatedComponent(Text), Image: AnimatedImplementation.createAnimatedComponent(Image), get ScrollView() { // Make this lazy to avoid circular reference. if (!AnimatedScrollView) { AnimatedScrollView = AnimatedImplementation.createAnimatedComponent(require('ScrollView')); } return AnimatedScrollView; }, }; Object.assign((Animated: Object), AnimatedImplementation); module.exports = ((Animated: any): (typeof AnimatedImplementation) & typeof Animated); ================================================ FILE: Libraries/Animated/src/AnimatedEvent.js ================================================ /** * 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 AnimatedEvent * @flow * @format */ 'use strict'; const AnimatedValue = require('./nodes/AnimatedValue'); const NativeAnimatedHelper = require('./NativeAnimatedHelper'); const ReactNative = require('ReactNative'); const invariant = require('fbjs/lib/invariant'); const {shouldUseNativeDriver} = require('./NativeAnimatedHelper'); export type Mapping = {[key: string]: Mapping} | AnimatedValue; export type EventConfig = { listener?: ?Function, useNativeDriver?: boolean, }; function attachNativeEvent( viewRef: any, eventName: string, argMapping: Array, ) { // Find animated values in `argMapping` and create an array representing their // key path inside the `nativeEvent` object. Ex.: ['contentOffset', 'x']. const eventMappings = []; const traverse = (value, path) => { if (value instanceof AnimatedValue) { value.__makeNative(); eventMappings.push({ nativeEventPath: path, animatedValueTag: value.__getNativeTag(), }); } else if (typeof value === 'object') { for (const key in value) { traverse(value[key], path.concat(key)); } } }; invariant( argMapping[0] && argMapping[0].nativeEvent, 'Native driven events only support animated values contained inside `nativeEvent`.', ); // Assume that the event containing `nativeEvent` is always the first argument. traverse(argMapping[0].nativeEvent, []); const viewTag = ReactNative.findNodeHandle(viewRef); eventMappings.forEach(mapping => { NativeAnimatedHelper.API.addAnimatedEventToView( viewTag, eventName, mapping, ); }); return { detach() { eventMappings.forEach(mapping => { NativeAnimatedHelper.API.removeAnimatedEventFromView( viewTag, eventName, mapping.animatedValueTag, ); }); }, }; } class AnimatedEvent { _argMapping: Array; _listeners: Array = []; _callListeners: Function; _attachedEvent: ?{ detach: () => void, }; __isNative: boolean; constructor(argMapping: Array, config?: EventConfig = {}) { this._argMapping = argMapping; if (config.listener) { this.__addListener(config.listener); } this._callListeners = this._callListeners.bind(this); this._attachedEvent = null; this.__isNative = shouldUseNativeDriver(config); if (__DEV__) { this._validateMapping(); } } __addListener(callback: Function): void { this._listeners.push(callback); } __removeListener(callback: Function): void { this._listeners = this._listeners.filter(listener => listener !== callback); } __attach(viewRef: any, eventName: string) { invariant( this.__isNative, 'Only native driven events need to be attached.', ); this._attachedEvent = attachNativeEvent( viewRef, eventName, this._argMapping, ); } __detach(viewTag: any, eventName: string) { invariant( this.__isNative, 'Only native driven events need to be detached.', ); this._attachedEvent && this._attachedEvent.detach(); } __getHandler() { if (this.__isNative) { return this._callListeners; } return (...args: any) => { const traverse = (recMapping, recEvt, key) => { if (typeof recEvt === 'number' && recMapping instanceof AnimatedValue) { recMapping.setValue(recEvt); } else if (typeof recMapping === 'object') { for (const mappingKey in recMapping) { /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This * comment suppresses an error when upgrading Flow's support for * React. To see the error delete this comment and run Flow. */ traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey); } } }; if (!this.__isNative) { this._argMapping.forEach((mapping, idx) => { traverse(mapping, args[idx], 'arg' + idx); }); } this._callListeners(...args); }; } _callListeners(...args) { this._listeners.forEach(listener => listener(...args)); } _validateMapping() { const traverse = (recMapping, recEvt, key) => { if (typeof recEvt === 'number') { invariant( recMapping instanceof AnimatedValue, 'Bad mapping of type ' + typeof recMapping + ' for key ' + key + ', event value must map to AnimatedValue', ); return; } invariant( typeof recMapping === 'object', 'Bad mapping of type ' + typeof recMapping + ' for key ' + key, ); invariant( typeof recEvt === 'object', 'Bad event of type ' + typeof recEvt + ' for key ' + key, ); for (const mappingKey in recMapping) { traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey); } }; } } module.exports = {AnimatedEvent, attachNativeEvent}; ================================================ FILE: Libraries/Animated/src/AnimatedImplementation.js ================================================ /** * 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 AnimatedImplementation * @flow * @format * @preventMunge */ 'use strict'; const {AnimatedEvent, attachNativeEvent} = require('./AnimatedEvent'); const AnimatedAddition = require('./nodes/AnimatedAddition'); const AnimatedDiffClamp = require('./nodes/AnimatedDiffClamp'); const AnimatedDivision = require('./nodes/AnimatedDivision'); const AnimatedInterpolation = require('./nodes/AnimatedInterpolation'); const AnimatedModulo = require('./nodes/AnimatedModulo'); const AnimatedMultiplication = require('./nodes/AnimatedMultiplication'); const AnimatedNode = require('./nodes/AnimatedNode'); const AnimatedProps = require('./nodes/AnimatedProps'); const AnimatedTracking = require('./nodes/AnimatedTracking'); const AnimatedValue = require('./nodes/AnimatedValue'); const AnimatedValueXY = require('./nodes/AnimatedValueXY'); const DecayAnimation = require('./animations/DecayAnimation'); const SpringAnimation = require('./animations/SpringAnimation'); const TimingAnimation = require('./animations/TimingAnimation'); const createAnimatedComponent = require('./createAnimatedComponent'); import type { AnimationConfig, EndCallback, EndResult, } from './animations/Animation'; import type {TimingAnimationConfig} from './animations/TimingAnimation'; import type {DecayAnimationConfig} from './animations/DecayAnimation'; import type {SpringAnimationConfig} from './animations/SpringAnimation'; import type {Mapping, EventConfig} from './AnimatedEvent'; type CompositeAnimation = { start: (callback?: ?EndCallback) => void, stop: () => void, reset: () => void, _startNativeLoop: (iterations?: number) => void, _isUsingNativeDriver: () => boolean, }; const add = function( a: AnimatedNode | number, b: AnimatedNode | number, ): AnimatedAddition { return new AnimatedAddition(a, b); }; const divide = function( a: AnimatedNode | number, b: AnimatedNode | number, ): AnimatedDivision { return new AnimatedDivision(a, b); }; const multiply = function( a: AnimatedNode | number, b: AnimatedNode | number, ): AnimatedMultiplication { return new AnimatedMultiplication(a, b); }; const modulo = function(a: AnimatedNode, modulus: number): AnimatedModulo { return new AnimatedModulo(a, modulus); }; const diffClamp = function( a: AnimatedNode, min: number, max: number, ): AnimatedDiffClamp { return new AnimatedDiffClamp(a, min, max); }; const _combineCallbacks = function( callback: ?EndCallback, config: AnimationConfig, ) { if (callback && config.onComplete) { return (...args) => { config.onComplete && config.onComplete(...args); callback && callback(...args); }; } else { return callback || config.onComplete; } }; const maybeVectorAnim = function( value: AnimatedValue | AnimatedValueXY, config: Object, anim: (value: AnimatedValue, config: Object) => CompositeAnimation, ): ?CompositeAnimation { if (value instanceof AnimatedValueXY) { const configX = {...config}; const configY = {...config}; for (const key in config) { const {x, y} = config[key]; if (x !== undefined && y !== undefined) { configX[key] = x; configY[key] = y; } } const aX = anim((value: AnimatedValueXY).x, configX); const aY = anim((value: AnimatedValueXY).y, configY); // We use `stopTogether: false` here because otherwise tracking will break // because the second animation will get stopped before it can update. return parallel([aX, aY], {stopTogether: false}); } return null; }; const spring = function( value: AnimatedValue | AnimatedValueXY, config: SpringAnimationConfig, ): CompositeAnimation { const start = function( animatedValue: AnimatedValue | AnimatedValueXY, configuration: SpringAnimationConfig, callback?: ?EndCallback, ): void { callback = _combineCallbacks(callback, configuration); const singleValue: any = animatedValue; const singleConfig: any = configuration; singleValue.stopTracking(); if (configuration.toValue instanceof AnimatedNode) { singleValue.track( new AnimatedTracking( singleValue, configuration.toValue, SpringAnimation, singleConfig, callback, ), ); } else { singleValue.animate(new SpringAnimation(singleConfig), callback); } }; return ( maybeVectorAnim(value, config, spring) || { start: function(callback?: ?EndCallback): void { start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, reset: function(): void { value.resetAnimation(); }, _startNativeLoop: function(iterations?: number): void { const singleConfig = {...config, iterations}; start(value, singleConfig); }, _isUsingNativeDriver: function(): boolean { return config.useNativeDriver || false; }, } ); }; const timing = function( value: AnimatedValue | AnimatedValueXY, config: TimingAnimationConfig, ): CompositeAnimation { const start = function( animatedValue: AnimatedValue | AnimatedValueXY, configuration: TimingAnimationConfig, callback?: ?EndCallback, ): void { callback = _combineCallbacks(callback, configuration); const singleValue: any = animatedValue; const singleConfig: any = configuration; singleValue.stopTracking(); if (configuration.toValue instanceof AnimatedNode) { singleValue.track( new AnimatedTracking( singleValue, configuration.toValue, TimingAnimation, singleConfig, callback, ), ); } else { singleValue.animate(new TimingAnimation(singleConfig), callback); } }; return ( maybeVectorAnim(value, config, timing) || { start: function(callback?: ?EndCallback): void { start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, reset: function(): void { value.resetAnimation(); }, _startNativeLoop: function(iterations?: number): void { const singleConfig = {...config, iterations}; start(value, singleConfig); }, _isUsingNativeDriver: function(): boolean { return config.useNativeDriver || false; }, } ); }; const decay = function( value: AnimatedValue | AnimatedValueXY, config: DecayAnimationConfig, ): CompositeAnimation { const start = function( animatedValue: AnimatedValue | AnimatedValueXY, configuration: DecayAnimationConfig, callback?: ?EndCallback, ): void { callback = _combineCallbacks(callback, configuration); const singleValue: any = animatedValue; const singleConfig: any = configuration; singleValue.stopTracking(); singleValue.animate(new DecayAnimation(singleConfig), callback); }; return ( maybeVectorAnim(value, config, decay) || { start: function(callback?: ?EndCallback): void { start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, reset: function(): void { value.resetAnimation(); }, _startNativeLoop: function(iterations?: number): void { const singleConfig = {...config, iterations}; start(value, singleConfig); }, _isUsingNativeDriver: function(): boolean { return config.useNativeDriver || false; }, } ); }; const sequence = function( animations: Array, ): CompositeAnimation { let current = 0; return { start: function(callback?: ?EndCallback) { const onComplete = function(result) { if (!result.finished) { callback && callback(result); return; } current++; if (current === animations.length) { callback && callback(result); return; } animations[current].start(onComplete); }; if (animations.length === 0) { callback && callback({finished: true}); } else { animations[current].start(onComplete); } }, stop: function() { if (current < animations.length) { animations[current].stop(); } }, reset: function() { animations.forEach((animation, idx) => { if (idx <= current) { animation.reset(); } }); current = 0; }, _startNativeLoop: function() { throw new Error( 'Loops run using the native driver cannot contain Animated.sequence animations', ); }, _isUsingNativeDriver: function(): boolean { return false; }, }; }; type ParallelConfig = { stopTogether?: boolean, // If one is stopped, stop all. default: true }; const parallel = function( animations: Array, config?: ?ParallelConfig, ): CompositeAnimation { let doneCount = 0; // Make sure we only call stop() at most once for each animation const hasEnded = {}; const stopTogether = !(config && config.stopTogether === false); const result = { start: function(callback?: ?EndCallback) { if (doneCount === animations.length) { callback && callback({finished: true}); return; } animations.forEach((animation, idx) => { const cb = function(endResult) { hasEnded[idx] = true; doneCount++; if (doneCount === animations.length) { doneCount = 0; callback && callback(endResult); return; } if (!endResult.finished && stopTogether) { result.stop(); } }; if (!animation) { cb({finished: true}); } else { animation.start(cb); } }); }, stop: function(): void { animations.forEach((animation, idx) => { !hasEnded[idx] && animation.stop(); hasEnded[idx] = true; }); }, reset: function(): void { animations.forEach((animation, idx) => { animation.reset(); hasEnded[idx] = false; doneCount = 0; }); }, _startNativeLoop: function() { throw new Error( 'Loops run using the native driver cannot contain Animated.parallel animations', ); }, _isUsingNativeDriver: function(): boolean { return false; }, }; return result; }; const delay = function(time: number): CompositeAnimation { // Would be nice to make a specialized implementation return timing(new AnimatedValue(0), {toValue: 0, delay: time, duration: 0}); }; const stagger = function( time: number, animations: Array, ): CompositeAnimation { return parallel( animations.map((animation, i) => { return sequence([delay(time * i), animation]); }), ); }; type LoopAnimationConfig = {iterations: number}; const loop = function( animation: CompositeAnimation, {iterations = -1}: LoopAnimationConfig = {}, ): CompositeAnimation { let isFinished = false; let iterationsSoFar = 0; return { start: function(callback?: ?EndCallback) { const restart = function(result: EndResult = {finished: true}): void { if ( isFinished || iterationsSoFar === iterations || result.finished === false ) { callback && callback(result); } else { iterationsSoFar++; animation.reset(); animation.start(restart); } }; if (!animation || iterations === 0) { callback && callback({finished: true}); } else { if (animation._isUsingNativeDriver()) { animation._startNativeLoop(iterations); } else { restart(); // Start looping recursively on the js thread } } }, stop: function(): void { isFinished = true; animation.stop(); }, reset: function(): void { iterationsSoFar = 0; isFinished = false; animation.reset(); }, _startNativeLoop: function() { throw new Error( 'Loops run using the native driver cannot contain Animated.loop animations', ); }, _isUsingNativeDriver: function(): boolean { return animation._isUsingNativeDriver(); }, }; }; function forkEvent( event: ?AnimatedEvent | ?Function, listener: Function, ): AnimatedEvent | Function { if (!event) { return listener; } else if (event instanceof AnimatedEvent) { event.__addListener(listener); return event; } else { return (...args) => { typeof event === 'function' && event(...args); listener(...args); }; } } function unforkEvent( event: ?AnimatedEvent | ?Function, listener: Function, ): void { if (event && event instanceof AnimatedEvent) { event.__removeListener(listener); } } const event = function(argMapping: Array, config?: EventConfig): any { const animatedEvent = new AnimatedEvent(argMapping, config); if (animatedEvent.__isNative) { return animatedEvent; } else { return animatedEvent.__getHandler(); } }; /** * The `Animated` library is designed to make animations fluid, powerful, and * easy to build and maintain. `Animated` focuses on declarative relationships * between inputs and outputs, with configurable transforms in between, and * simple `start`/`stop` methods to control time-based animation execution. * * See http://facebook.github.io/react-native/docs/animated.html */ module.exports = { /** * Standard value class for driving animations. Typically initialized with * `new Animated.Value(0);` * * See http://facebook.github.io/react-native/docs/animated.html#value */ Value: AnimatedValue, /** * 2D value class for driving 2D animations, such as pan gestures. * * See https://facebook.github.io/react-native/releases/next/docs/animatedvaluexy.html */ ValueXY: AnimatedValueXY, /** * Exported to use the Interpolation type in flow. * * See http://facebook.github.io/react-native/docs/animated.html#interpolation */ Interpolation: AnimatedInterpolation, /** * Exported for ease of type checking. All animated values derive from this * class. * * See http://facebook.github.io/react-native/docs/animated.html#node */ Node: AnimatedNode, /** * Animates a value from an initial velocity to zero based on a decay * coefficient. * * See http://facebook.github.io/react-native/docs/animated.html#decay */ decay, /** * Animates a value along a timed easing curve. The Easing module has tons of * predefined curves, or you can use your own function. * * See http://facebook.github.io/react-native/docs/animated.html#timing */ timing, /** * Animates a value according to an analytical spring model based on * damped harmonic oscillation. * * See http://facebook.github.io/react-native/docs/animated.html#spring */ spring, /** * Creates a new Animated value composed from two Animated values added * together. * * See http://facebook.github.io/react-native/docs/animated.html#add */ add, /** * Creates a new Animated value composed by dividing the first Animated value * by the second Animated value. * * See http://facebook.github.io/react-native/docs/animated.html#divide */ divide, /** * Creates a new Animated value composed from two Animated values multiplied * together. * * See http://facebook.github.io/react-native/docs/animated.html#multiply */ multiply, /** * Creates a new Animated value that is the (non-negative) modulo of the * provided Animated value. * * See http://facebook.github.io/react-native/docs/animated.html#modulo */ modulo, /** * Create a new Animated value that is limited between 2 values. It uses the * difference between the last value so even if the value is far from the * bounds it will start changing when the value starts getting closer again. * * See http://facebook.github.io/react-native/docs/animated.html#diffclamp */ diffClamp, /** * Starts an animation after the given delay. * * See http://facebook.github.io/react-native/docs/animated.html#delay */ delay, /** * Starts an array of animations in order, waiting for each to complete * before starting the next. If the current running animation is stopped, no * following animations will be started. * * See http://facebook.github.io/react-native/docs/animated.html#sequence */ sequence, /** * Starts an array of animations all at the same time. By default, if one * of the animations is stopped, they will all be stopped. You can override * this with the `stopTogether` flag. * * See http://facebook.github.io/react-native/docs/animated.html#parallel */ parallel, /** * Array of animations may run in parallel (overlap), but are started in * sequence with successive delays. Nice for doing trailing effects. * * See http://facebook.github.io/react-native/docs/animated.html#stagger */ stagger, /** * Loops a given animation continuously, so that each time it reaches the * end, it resets and begins again from the start. * * See http://facebook.github.io/react-native/docs/animated.html#loop */ loop, /** * Takes an array of mappings and extracts values from each arg accordingly, * then calls `setValue` on the mapped outputs. * * See http://facebook.github.io/react-native/docs/animated.html#event */ event, /** * Make any React component Animatable. Used to create `Animated.View`, etc. * * See http://facebook.github.io/react-native/docs/animated.html#createanimatedcomponent */ createAnimatedComponent, /** * Imperative API to attach an animated value to an event on a view. Prefer * using `Animated.event` with `useNativeDrive: true` if possible. * * See http://facebook.github.io/react-native/docs/animated.html#attachnativeevent */ attachNativeEvent, /** * Advanced imperative API for snooping on animated events that are passed in * through props. Use values directly where possible. * * See http://facebook.github.io/react-native/docs/animated.html#forkevent */ forkEvent, unforkEvent, __PropsOnlyForTests: AnimatedProps, }; ================================================ FILE: Libraries/Animated/src/AnimatedWeb.js ================================================ /** * 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. * * @flow * @providesModule AnimatedWeb */ 'use strict'; var AnimatedImplementation = require('AnimatedImplementation'); module.exports = { ...AnimatedImplementation, div: AnimatedImplementation.createAnimatedComponent('div'), span: AnimatedImplementation.createAnimatedComponent('span'), img: AnimatedImplementation.createAnimatedComponent('img'), }; ================================================ FILE: Libraries/Animated/src/Easing.js ================================================ /** * 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 Easing * @flow */ 'use strict'; let ease; /** * The `Easing` module implements common easing functions. This module is used * by [Animate.timing()](docs/animate.html#timing) to convey physically * believable motion in animations. * * You can find a visualization of some common easing functions at * http://easings.net/ * * ### Predefined animations * * The `Easing` module provides several predefined animations through the * following methods: * * - [`back`](docs/easing.html#back) provides a simple animation where the * object goes slightly back before moving forward * - [`bounce`](docs/easing.html#bounce) provides a bouncing animation * - [`ease`](docs/easing.html#ease) provides a simple inertial animation * - [`elastic`](docs/easing.html#elastic) provides a simple spring interaction * * ### Standard functions * * Three standard easing functions are provided: * * - [`linear`](docs/easing.html#linear) * - [`quad`](docs/easing.html#quad) * - [`cubic`](docs/easing.html#cubic) * * The [`poly`](docs/easing.html#poly) function can be used to implement * quartic, quintic, and other higher power functions. * * ### Additional functions * * Additional mathematical functions are provided by the following methods: * * - [`bezier`](docs/easing.html#bezier) provides a cubic bezier curve * - [`circle`](docs/easing.html#circle) provides a circular function * - [`sin`](docs/easing.html#sin) provides a sinusoidal function * - [`exp`](docs/easing.html#exp) provides an exponential function * * The following helpers are used to modify other easing functions. * * - [`in`](docs/easing.html#in) runs an easing function forwards * - [`inOut`](docs/easing.html#inout) makes any easing function symmetrical * - [`out`](docs/easing.html#out) runs an easing function backwards */ class Easing { /** * A stepping function, returns 1 for any positive value of `n`. */ static step0(n: number) { return n > 0 ? 1 : 0; } /** * A stepping function, returns 1 if `n` is greater than or equal to 1. */ static step1(n: number) { return n >= 1 ? 1 : 0; } /** * A linear function, `f(t) = t`. Position correlates to elapsed time one to * one. * * http://cubic-bezier.com/#0,0,1,1 */ static linear(t: number) { return t; } /** * A simple inertial interaction, similar to an object slowly accelerating to * speed. * * http://cubic-bezier.com/#.42,0,1,1 */ static ease(t: number): number { if (!ease) { ease = Easing.bezier(0.42, 0, 1, 1); } return ease(t); } /** * A quadratic function, `f(t) = t * t`. Position equals the square of elapsed * time. * * http://easings.net/#easeInQuad */ static quad(t: number) { return t * t; } /** * A cubic function, `f(t) = t * t * t`. Position equals the cube of elapsed * time. * * http://easings.net/#easeInCubic */ static cubic(t: number) { return t * t * t; } /** * A power function. Position is equal to the Nth power of elapsed time. * * n = 4: http://easings.net/#easeInQuart * n = 5: http://easings.net/#easeInQuint */ static poly(n: number) { return (t: number) => Math.pow(t, n); } /** * A sinusoidal function. * * http://easings.net/#easeInSine */ static sin(t: number) { return 1 - Math.cos(t * Math.PI / 2); } /** * A circular function. * * http://easings.net/#easeInCirc */ static circle(t: number) { return 1 - Math.sqrt(1 - t * t); } /** * An exponential function. * * http://easings.net/#easeInExpo */ static exp(t: number) { return Math.pow(2, 10 * (t - 1)); } /** * A simple elastic interaction, similar to a spring oscillating back and * forth. * * Default bounciness is 1, which overshoots a little bit once. 0 bounciness * doesn't overshoot at all, and bounciness of N > 1 will overshoot about N * times. * * http://easings.net/#easeInElastic */ static elastic(bounciness: number = 1): (t: number) => number { const p = bounciness * Math.PI; return (t) => 1 - Math.pow(Math.cos(t * Math.PI / 2), 3) * Math.cos(t * p); } /** * Use with `Animated.parallel()` to create a simple effect where the object * animates back slightly as the animation starts. * * Wolfram Plot: * * - http://tiny.cc/back_default (s = 1.70158, default) */ static back(s: number): (t: number) => number { if (s === undefined) { s = 1.70158; } return (t) => t * t * ((s + 1) * t - s); } /** * Provides a simple bouncing effect. * * http://easings.net/#easeInBounce */ static bounce(t: number): number { if (t < 1 / 2.75) { return 7.5625 * t * t; } if (t < 2 / 2.75) { t -= 1.5 / 2.75; return 7.5625 * t * t + 0.75; } if (t < 2.5 / 2.75) { t -= 2.25 / 2.75; return 7.5625 * t * t + 0.9375; } t -= 2.625 / 2.75; return 7.5625 * t * t + 0.984375; } /** * Provides a cubic bezier curve, equivalent to CSS Transitions' * `transition-timing-function`. * * A useful tool to visualize cubic bezier curves can be found at * http://cubic-bezier.com/ */ static bezier( x1: number, y1: number, x2: number, y2: number ): (t: number) => number { const _bezier = require('bezier'); return _bezier(x1, y1, x2, y2); } /** * Runs an easing function forwards. */ static in( easing: (t: number) => number, ): (t: number) => number { return easing; } /** * Runs an easing function backwards. */ static out( easing: (t: number) => number, ): (t: number) => number { return (t) => 1 - easing(1 - t); } /** * Makes any easing function symmetrical. The easing function will run * forwards for half of the duration, then backwards for the rest of the * duration. */ static inOut( easing: (t: number) => number, ): (t: number) => number { return (t) => { if (t < 0.5) { return easing(t * 2) / 2; } return 1 - easing((1 - t) * 2) / 2; }; } } module.exports = Easing; ================================================ FILE: Libraries/Animated/src/NativeAnimatedHelper.js ================================================ /** * 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 NativeAnimatedHelper * @flow * @format */ 'use strict'; const NativeAnimatedModule = require('NativeModules').NativeAnimatedModule; const NativeEventEmitter = require('NativeEventEmitter'); const invariant = require('fbjs/lib/invariant'); import type {AnimationConfig} from './animations/Animation'; import type {EventConfig} from './AnimatedEvent'; let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */ let __nativeAnimationIdCount = 1; /* used for started animations */ type EndResult = {finished: boolean}; type EndCallback = (result: EndResult) => void; type EventMapping = { nativeEventPath: Array, animatedValueTag: ?number, }; let nativeEventEmitter; /** * Simple wrappers around NativeAnimatedModule to provide flow and autocmplete support for * the native module methods */ const API = { createAnimatedNode: function(tag: ?number, config: Object): void { assertNativeAnimatedModule(); NativeAnimatedModule.createAnimatedNode(tag, config); }, startListeningToAnimatedNodeValue: function(tag: ?number) { assertNativeAnimatedModule(); NativeAnimatedModule.startListeningToAnimatedNodeValue(tag); }, stopListeningToAnimatedNodeValue: function(tag: ?number) { assertNativeAnimatedModule(); NativeAnimatedModule.stopListeningToAnimatedNodeValue(tag); }, connectAnimatedNodes: function(parentTag: ?number, childTag: ?number): void { assertNativeAnimatedModule(); NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag); }, disconnectAnimatedNodes: function( parentTag: ?number, childTag: ?number, ): void { assertNativeAnimatedModule(); NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag); }, startAnimatingNode: function( animationId: ?number, nodeTag: ?number, config: Object, endCallback: EndCallback, ): void { assertNativeAnimatedModule(); NativeAnimatedModule.startAnimatingNode( animationId, nodeTag, config, endCallback, ); }, stopAnimation: function(animationId: ?number) { assertNativeAnimatedModule(); NativeAnimatedModule.stopAnimation(animationId); }, setAnimatedNodeValue: function(nodeTag: ?number, value: ?number): void { assertNativeAnimatedModule(); NativeAnimatedModule.setAnimatedNodeValue(nodeTag, value); }, setAnimatedNodeOffset: function(nodeTag: ?number, offset: ?number): void { assertNativeAnimatedModule(); NativeAnimatedModule.setAnimatedNodeOffset(nodeTag, offset); }, flattenAnimatedNodeOffset: function(nodeTag: ?number): void { assertNativeAnimatedModule(); NativeAnimatedModule.flattenAnimatedNodeOffset(nodeTag); }, extractAnimatedNodeOffset: function(nodeTag: ?number): void { assertNativeAnimatedModule(); NativeAnimatedModule.extractAnimatedNodeOffset(nodeTag); }, connectAnimatedNodeToView: function( nodeTag: ?number, viewTag: ?number, ): void { assertNativeAnimatedModule(); NativeAnimatedModule.connectAnimatedNodeToView(nodeTag, viewTag); }, disconnectAnimatedNodeFromView: function( nodeTag: ?number, viewTag: ?number, ): void { assertNativeAnimatedModule(); NativeAnimatedModule.disconnectAnimatedNodeFromView(nodeTag, viewTag); }, dropAnimatedNode: function(tag: ?number): void { assertNativeAnimatedModule(); NativeAnimatedModule.dropAnimatedNode(tag); }, addAnimatedEventToView: function( viewTag: ?number, eventName: string, eventMapping: EventMapping, ) { assertNativeAnimatedModule(); NativeAnimatedModule.addAnimatedEventToView( viewTag, eventName, eventMapping, ); }, removeAnimatedEventFromView( viewTag: ?number, eventName: string, animatedNodeTag: ?number, ) { assertNativeAnimatedModule(); NativeAnimatedModule.removeAnimatedEventFromView( viewTag, eventName, animatedNodeTag, ); }, }; /** * Styles allowed by the native animated implementation. * * In general native animated implementation should support any numeric property that doesn't need * to be updated through the shadow view hierarchy (all non-layout properties). */ const STYLES_WHITELIST = { opacity: true, transform: true, /* ios styles */ shadowOpacity: true, shadowRadius: true, /* legacy android transform properties */ scaleX: true, scaleY: true, translateX: true, translateY: true, }; const TRANSFORM_WHITELIST = { translateX: true, translateY: true, scale: true, scaleX: true, scaleY: true, rotate: true, rotateX: true, rotateY: true, perspective: true, }; const SUPPORTED_INTERPOLATION_PARAMS = { inputRange: true, outputRange: true, extrapolate: true, extrapolateRight: true, extrapolateLeft: true, }; function addWhitelistedStyleProp(prop: string): void { STYLES_WHITELIST[prop] = true; } function addWhitelistedTransformProp(prop: string): void { TRANSFORM_WHITELIST[prop] = true; } function addWhitelistedInterpolationParam(param: string): void { SUPPORTED_INTERPOLATION_PARAMS[param] = true; } function validateTransform(configs: Array): void { configs.forEach(config => { if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) { throw new Error( `Property '${ config.property }' is not supported by native animated module`, ); } }); } function validateStyles(styles: Object): void { for (var key in styles) { if (!STYLES_WHITELIST.hasOwnProperty(key)) { throw new Error( `Style property '${key}' is not supported by native animated module`, ); } } } function validateInterpolation(config: Object): void { for (var key in config) { if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) { throw new Error( `Interpolation property '${key}' is not supported by native animated module`, ); } } } function generateNewNodeTag(): number { return __nativeAnimatedNodeTagCount++; } function generateNewAnimationId(): number { return __nativeAnimationIdCount++; } function assertNativeAnimatedModule(): void { invariant(NativeAnimatedModule, 'Native animated module is not available'); } let _warnedMissingNativeAnimated = false; function shouldUseNativeDriver(config: AnimationConfig | EventConfig): boolean { if (config.useNativeDriver && !NativeAnimatedModule) { if (!_warnedMissingNativeAnimated) { console.warn( 'Animated: `useNativeDriver` is not supported because the native ' + 'animated module is missing. Falling back to JS-based animation. To ' + 'resolve this, add `RCTAnimation` module to this app, or remove ' + '`useNativeDriver`. ' + 'More info: https://github.com/facebook/react-native/issues/11094#issuecomment-263240420', ); _warnedMissingNativeAnimated = true; } return false; } return config.useNativeDriver || false; } module.exports = { API, addWhitelistedStyleProp, addWhitelistedTransformProp, addWhitelistedInterpolationParam, validateStyles, validateTransform, validateInterpolation, generateNewNodeTag, generateNewAnimationId, assertNativeAnimatedModule, shouldUseNativeDriver, get nativeEventEmitter() { if (!nativeEventEmitter) { nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule); } return nativeEventEmitter; }, }; ================================================ FILE: Libraries/Animated/src/SpringConfig.js ================================================ /** * 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 SpringConfig * @flow */ 'use strict'; type SpringConfigType = { stiffness: number, damping: number, }; function stiffnessFromOrigamiValue(oValue) { return (oValue - 30) * 3.62 + 194; } function dampingFromOrigamiValue(oValue) { return (oValue - 8) * 3 + 25; } function fromOrigamiTensionAndFriction( tension: number, friction: number, ): SpringConfigType { return { stiffness: stiffnessFromOrigamiValue(tension), damping: dampingFromOrigamiValue(friction), }; } function fromBouncinessAndSpeed( bounciness: number, speed: number, ): SpringConfigType { function normalize(value, startValue, endValue) { return (value - startValue) / (endValue - startValue); } function projectNormal(n, start, end) { return start + (n * (end - start)); } function linearInterpolation(t, start, end) { return t * end + (1 - t) * start; } function quadraticOutInterpolation(t, start, end) { return linearInterpolation(2 * t - t * t, start, end); } function b3Friction1(x) { return (0.0007 * Math.pow(x, 3)) - (0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28; } function b3Friction2(x) { return (0.000044 * Math.pow(x, 3)) - (0.006 * Math.pow(x, 2)) + 0.36 * x + 2; } function b3Friction3(x) { return (0.00000045 * Math.pow(x, 3)) - (0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84; } function b3Nobounce(tension) { if (tension <= 18) { return b3Friction1(tension); } else if (tension > 18 && tension <= 44) { return b3Friction2(tension); } else { return b3Friction3(tension); } } var b = normalize(bounciness / 1.7, 0, 20); b = projectNormal(b, 0, 0.8); var s = normalize(speed / 1.7, 0, 20); var bouncyTension = projectNormal(s, 0.5, 200); var bouncyFriction = quadraticOutInterpolation( b, b3Nobounce(bouncyTension), 0.01 ); return { stiffness: stiffnessFromOrigamiValue(bouncyTension), damping: dampingFromOrigamiValue(bouncyFriction), }; } module.exports = { fromOrigamiTensionAndFriction, fromBouncinessAndSpeed, }; ================================================ FILE: Libraries/Animated/src/__tests__/Animated-test.js ================================================ /** * 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. * * @emails oncall+react_native */ 'use strict'; var Animated = require('Animated'); describe('Animated tests', () => { beforeEach(() => { jest.resetModules(); }); describe('Animated', () => { it('works end to end', () => { var anim = new Animated.Value(0); var callback = jest.fn(); var node = new Animated.__PropsOnlyForTests({ style: { backgroundColor: 'red', opacity: anim, transform: [ {translateX: anim.interpolate({ inputRange: [0, 1], outputRange: [100, 200], })}, {scale: anim}, ], shadowOffset: { width: anim, height: anim, }, } }, callback); expect(anim.__getChildren().length).toBe(3); expect(node.__getValue()).toEqual({ style: { backgroundColor: 'red', opacity: 0, transform: [ {translateX: 100}, {scale: 0}, ], shadowOffset: { width: 0, height: 0, }, }, }); anim.setValue(0.5); expect(callback).toBeCalled(); expect(node.__getValue()).toEqual({ style: { backgroundColor: 'red', opacity: 0.5, transform: [ {translateX: 150}, {scale: 0.5}, ], shadowOffset: { width: 0.5, height: 0.5, }, }, }); node.__detach(); expect(anim.__getChildren().length).toBe(0); anim.setValue(1); expect(callback.mock.calls.length).toBe(1); }); it('does not detach on updates', () => { var anim = new Animated.Value(0); anim.__detach = jest.fn(); var c = new Animated.View(); c.props = { style: { opacity: anim, }, }; c.componentWillMount(); expect(anim.__detach).not.toBeCalled(); c._component = {}; c.componentWillReceiveProps({ style: { opacity: anim, }, }); expect(anim.__detach).not.toBeCalled(); c.componentWillUnmount(); expect(anim.__detach).toBeCalled(); }); it('stops animation when detached', () => { var anim = new Animated.Value(0); var callback = jest.fn(); var c = new Animated.View(); c.props = { style: { opacity: anim, }, }; c.componentWillMount(); Animated.timing(anim, {toValue: 10, duration: 1000}).start(callback); c._component = {}; c.componentWillUnmount(); expect(callback).toBeCalledWith({finished: false}); expect(callback).toBeCalledWith({finished: false}); }); it('triggers callback when spring is at rest', () => { var anim = new Animated.Value(0); var callback = jest.fn(); Animated.spring(anim, {toValue: 0, velocity: 0}).start(callback); expect(callback).toBeCalled(); }); it('send toValue when an underdamped spring stops', () => { var anim = new Animated.Value(0); var listener = jest.fn(); anim.addListener(listener); Animated.spring(anim, {toValue: 15}).start(); jest.runAllTimers(); var lastValue = listener.mock.calls[listener.mock.calls.length - 2][0].value; expect(lastValue).not.toBe(15); expect(lastValue).toBeCloseTo(15); expect(anim.__getValue()).toBe(15); }); it('send toValue when a critically damped spring stops', () => { var anim = new Animated.Value(0); var listener = jest.fn(); anim.addListener(listener); Animated.spring(anim, {stiffness: 8000, damping: 2000, toValue: 15}).start(); jest.runAllTimers(); var lastValue = listener.mock.calls[listener.mock.calls.length - 2][0].value; expect(lastValue).not.toBe(15); expect(lastValue).toBeCloseTo(15); expect(anim.__getValue()).toBe(15); }); it('convert to JSON', () => { expect(JSON.stringify(new Animated.Value(10))).toBe('10'); }); }); describe('Animated Sequence', () => { it('works with an empty sequence', () => { var cb = jest.fn(); Animated.sequence([]).start(cb); expect(cb).toBeCalledWith({finished: true}); }); it('sequences well', () => { var anim1 = {start: jest.fn()}; var anim2 = {start: jest.fn()}; var cb = jest.fn(); var seq = Animated.sequence([anim1, anim2]); expect(anim1.start).not.toBeCalled(); expect(anim2.start).not.toBeCalled(); seq.start(cb); expect(anim1.start).toBeCalled(); expect(anim2.start).not.toBeCalled(); expect(cb).not.toBeCalled(); anim1.start.mock.calls[0][0]({finished: true}); expect(anim2.start).toBeCalled(); expect(cb).not.toBeCalled(); anim2.start.mock.calls[0][0]({finished: true}); expect(cb).toBeCalledWith({finished: true}); }); it('supports interrupting sequence', () => { var anim1 = {start: jest.fn()}; var anim2 = {start: jest.fn()}; var cb = jest.fn(); Animated.sequence([anim1, anim2]).start(cb); anim1.start.mock.calls[0][0]({finished: false}); expect(anim1.start).toBeCalled(); expect(anim2.start).not.toBeCalled(); expect(cb).toBeCalledWith({finished: false}); }); it('supports stopping sequence', () => { var anim1 = {start: jest.fn(), stop: jest.fn()}; var anim2 = {start: jest.fn(), stop: jest.fn()}; var cb = jest.fn(); var seq = Animated.sequence([anim1, anim2]); seq.start(cb); seq.stop(); expect(anim1.stop).toBeCalled(); expect(anim2.stop).not.toBeCalled(); expect(cb).not.toBeCalled(); anim1.start.mock.calls[0][0]({finished: false}); expect(cb).toBeCalledWith({finished: false}); }); }); describe('Animated Loop', () => { it('loops indefinitely if config not specified', () => { var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; var cb = jest.fn(); var loop = Animated.loop(animation); expect(animation.start).not.toBeCalled(); loop.start(cb); expect(animation.start).toBeCalled(); expect(animation.reset).toHaveBeenCalledTimes(1); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 expect(animation.reset).toHaveBeenCalledTimes(2); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 expect(animation.reset).toHaveBeenCalledTimes(3); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 expect(animation.reset).toHaveBeenCalledTimes(4); expect(cb).not.toBeCalled(); }); it('loops indefinitely if iterations is -1', () => { var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; var cb = jest.fn(); var loop = Animated.loop(animation, { iterations: -1 }); expect(animation.start).not.toBeCalled(); loop.start(cb); expect(animation.start).toBeCalled(); expect(animation.reset).toHaveBeenCalledTimes(1); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 expect(animation.reset).toHaveBeenCalledTimes(2); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 expect(animation.reset).toHaveBeenCalledTimes(3); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 expect(animation.reset).toHaveBeenCalledTimes(4); expect(cb).not.toBeCalled(); }); it('loops indefinitely if iterations not specified', () => { var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; var cb = jest.fn(); var loop = Animated.loop(animation, { anotherKey: 'value' }); expect(animation.start).not.toBeCalled(); loop.start(cb); expect(animation.start).toBeCalled(); expect(animation.reset).toHaveBeenCalledTimes(1); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 expect(animation.reset).toHaveBeenCalledTimes(2); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 expect(animation.reset).toHaveBeenCalledTimes(3); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 expect(animation.reset).toHaveBeenCalledTimes(4); expect(cb).not.toBeCalled(); }); it('loops three times if iterations is 3', () => { var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; var cb = jest.fn(); var loop = Animated.loop(animation, { iterations: 3 }); expect(animation.start).not.toBeCalled(); loop.start(cb); expect(animation.start).toBeCalled(); expect(animation.reset).toHaveBeenCalledTimes(1); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 expect(animation.reset).toHaveBeenCalledTimes(2); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 expect(animation.reset).toHaveBeenCalledTimes(3); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 expect(animation.reset).toHaveBeenCalledTimes(3); expect(cb).toBeCalledWith({finished: true}); }); it('does not loop if iterations is 1', () => { var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; var cb = jest.fn(); var loop = Animated.loop(animation, { iterations: 1 }); expect(animation.start).not.toBeCalled(); loop.start(cb); expect(animation.start).toBeCalled(); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 expect(cb).toBeCalledWith({finished: true}); }); it('does not animate if iterations is 0', () => { var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; var cb = jest.fn(); var loop = Animated.loop(animation, { iterations: 0 }); expect(animation.start).not.toBeCalled(); loop.start(cb); expect(animation.start).not.toBeCalled(); expect(cb).toBeCalledWith({finished: true}); }); it('supports interrupting an indefinite loop', () => { var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; var cb = jest.fn(); Animated.loop(animation).start(cb); expect(animation.start).toBeCalled(); expect(animation.reset).toHaveBeenCalledTimes(1); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 expect(animation.reset).toHaveBeenCalledTimes(2); expect(cb).not.toBeCalled(); animation.start.mock.calls[0][0]({finished: false}); // Interrupt loop expect(animation.reset).toHaveBeenCalledTimes(2); expect(cb).toBeCalledWith({finished: false}); }); it('supports stopping loop', () => { var animation = {start: jest.fn(), stop: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; var cb = jest.fn(); var loop = Animated.loop(animation); loop.start(cb); loop.stop(); expect(animation.start).toBeCalled(); expect(animation.reset).toHaveBeenCalledTimes(1); expect(animation.stop).toBeCalled(); animation.start.mock.calls[0][0]({finished: false}); // Interrupt loop expect(animation.reset).toHaveBeenCalledTimes(1); expect(cb).toBeCalledWith({finished: false}); }); }); describe('Animated Parallel', () => { it('works with an empty parallel', () => { var cb = jest.fn(); Animated.parallel([]).start(cb); expect(cb).toBeCalledWith({finished: true}); }); it('works with an empty element in array', () => { var anim1 = {start: jest.fn()}; var cb = jest.fn(); Animated.parallel([null, anim1]).start(cb); expect(anim1.start).toBeCalled(); anim1.start.mock.calls[0][0]({finished: true}); expect(cb).toBeCalledWith({finished: true}); }); it('parellelizes well', () => { var anim1 = {start: jest.fn()}; var anim2 = {start: jest.fn()}; var cb = jest.fn(); var par = Animated.parallel([anim1, anim2]); expect(anim1.start).not.toBeCalled(); expect(anim2.start).not.toBeCalled(); par.start(cb); expect(anim1.start).toBeCalled(); expect(anim2.start).toBeCalled(); expect(cb).not.toBeCalled(); anim1.start.mock.calls[0][0]({finished: true}); expect(cb).not.toBeCalled(); anim2.start.mock.calls[0][0]({finished: true}); expect(cb).toBeCalledWith({finished: true}); }); it('supports stopping parallel', () => { var anim1 = {start: jest.fn(), stop: jest.fn()}; var anim2 = {start: jest.fn(), stop: jest.fn()}; var cb = jest.fn(); var seq = Animated.parallel([anim1, anim2]); seq.start(cb); seq.stop(); expect(anim1.stop).toBeCalled(); expect(anim2.stop).toBeCalled(); expect(cb).not.toBeCalled(); anim1.start.mock.calls[0][0]({finished: false}); expect(cb).not.toBeCalled(); anim2.start.mock.calls[0][0]({finished: false}); expect(cb).toBeCalledWith({finished: false}); }); it('does not call stop more than once when stopping', () => { var anim1 = {start: jest.fn(), stop: jest.fn()}; var anim2 = {start: jest.fn(), stop: jest.fn()}; var anim3 = {start: jest.fn(), stop: jest.fn()}; var cb = jest.fn(); var seq = Animated.parallel([anim1, anim2, anim3]); seq.start(cb); anim1.start.mock.calls[0][0]({finished: false}); expect(anim1.stop.mock.calls.length).toBe(0); expect(anim2.stop.mock.calls.length).toBe(1); expect(anim3.stop.mock.calls.length).toBe(1); anim2.start.mock.calls[0][0]({finished: false}); expect(anim1.stop.mock.calls.length).toBe(0); expect(anim2.stop.mock.calls.length).toBe(1); expect(anim3.stop.mock.calls.length).toBe(1); anim3.start.mock.calls[0][0]({finished: false}); expect(anim1.stop.mock.calls.length).toBe(0); expect(anim2.stop.mock.calls.length).toBe(1); expect(anim3.stop.mock.calls.length).toBe(1); }); }); describe('Animated delays', () => { it('should call anim after delay in sequence', () => { var anim = {start: jest.fn(), stop: jest.fn()}; var cb = jest.fn(); Animated.sequence([ Animated.delay(1000), anim, ]).start(cb); jest.runAllTimers(); expect(anim.start.mock.calls.length).toBe(1); expect(cb).not.toBeCalled(); anim.start.mock.calls[0][0]({finished: true}); expect(cb).toBeCalledWith({finished: true}); }); it('should run stagger to end', () => { var cb = jest.fn(); Animated.stagger(1000, [ Animated.delay(1000), Animated.delay(1000), Animated.delay(1000), ]).start(cb); jest.runAllTimers(); expect(cb).toBeCalledWith({finished: true}); }); }); describe('Animated Events', () => { it('should map events', () => { var value = new Animated.Value(0); var handler = Animated.event( [null, {state: {foo: value}}], ); handler({bar: 'ignoreBar'}, {state: {baz: 'ignoreBaz', foo: 42}}); expect(value.__getValue()).toBe(42); }); it('should call listeners', () => { var value = new Animated.Value(0); var listener = jest.fn(); var handler = Animated.event( [{foo: value}], {listener}, ); handler({foo: 42}); expect(value.__getValue()).toBe(42); expect(listener.mock.calls.length).toBe(1); expect(listener).toBeCalledWith({foo: 42}); }); it('should call forked event listeners', () => { var value = new Animated.Value(0); var listener = jest.fn(); var handler = Animated.event( [{foo: value}], {listener}, ); var listener2 = jest.fn(); var forkedHandler = Animated.forkEvent(handler, listener2); forkedHandler({foo: 42}); expect(value.__getValue()).toBe(42); expect(listener.mock.calls.length).toBe(1); expect(listener).toBeCalledWith({foo: 42}); expect(listener2.mock.calls.length).toBe(1); expect(listener2).toBeCalledWith({foo: 42}); }); }); describe('Animated Interactions', () => { /*eslint-disable no-shadow*/ var Animated; /*eslint-enable*/ var InteractionManager; beforeEach(() => { jest.mock('InteractionManager'); Animated = require('Animated'); InteractionManager = require('InteractionManager'); }); afterEach(()=> { jest.unmock('InteractionManager'); }); it('registers an interaction by default', () => { InteractionManager.createInteractionHandle.mockReturnValue(777); var value = new Animated.Value(0); var callback = jest.fn(); Animated.timing(value, { toValue: 100, duration: 100, }).start(callback); jest.runAllTimers(); expect(InteractionManager.createInteractionHandle).toBeCalled(); expect(InteractionManager.clearInteractionHandle).toBeCalledWith(777); expect(callback).toBeCalledWith({finished: true}); }); it('does not register an interaction when specified', () => { var value = new Animated.Value(0); var callback = jest.fn(); Animated.timing(value, { toValue: 100, duration: 100, isInteraction: false, }).start(callback); jest.runAllTimers(); expect(InteractionManager.createInteractionHandle).not.toBeCalled(); expect(InteractionManager.clearInteractionHandle).not.toBeCalled(); expect(callback).toBeCalledWith({finished: true}); }); }); describe('Animated Tracking', () => { it('should track values', () => { var value1 = new Animated.Value(0); var value2 = new Animated.Value(0); Animated.timing(value2, { toValue: value1, duration: 0, }).start(); value1.setValue(42); expect(value2.__getValue()).toBe(42); value1.setValue(7); expect(value2.__getValue()).toBe(7); }); it('should track interpolated values', () => { var value1 = new Animated.Value(0); var value2 = new Animated.Value(0); Animated.timing(value2, { toValue: value1.interpolate({ inputRange: [0, 2], outputRange: [0, 1] }), duration: 0, }).start(); value1.setValue(42); expect(value2.__getValue()).toBe(42 / 2); }); it('should stop tracking when animated', () => { var value1 = new Animated.Value(0); var value2 = new Animated.Value(0); Animated.timing(value2, { toValue: value1, duration: 0, }).start(); value1.setValue(42); expect(value2.__getValue()).toBe(42); Animated.timing(value2, { toValue: 7, duration: 0, }).start(); value1.setValue(1492); expect(value2.__getValue()).toBe(7); }); }); describe('Animated Vectors', () => { it('should animate vectors', () => { var vec = new Animated.ValueXY(); var callback = jest.fn(); var node = new Animated.__PropsOnlyForTests({ style: { opacity: vec.x.interpolate({ inputRange: [0, 42], outputRange: [0.2, 0.8], }), transform: vec.getTranslateTransform(), ...vec.getLayout(), } }, callback); expect(node.__getValue()).toEqual({ style: { opacity: 0.2, transform: [ {translateX: 0}, {translateY: 0}, ], left: 0, top: 0, }, }); vec.setValue({x: 42, y: 1492}); expect(callback.mock.calls.length).toBe(2); // once each for x, y expect(node.__getValue()).toEqual({ style: { opacity: 0.8, transform: [ {translateX: 42}, {translateY: 1492}, ], left: 42, top: 1492, }, }); node.__detach(); vec.setValue({x: 1, y: 1}); expect(callback.mock.calls.length).toBe(2); }); it('should track vectors', () => { var value1 = new Animated.ValueXY(); var value2 = new Animated.ValueXY(); Animated.timing(value2, { toValue: value1, duration: 0, }).start(); value1.setValue({x: 42, y: 1492}); expect(value2.__getValue()).toEqual({x: 42, y: 1492}); // Make sure tracking keeps working (see stopTogether in ParallelConfig used // by maybeVectorAnim). value1.setValue({x: 3, y: 4}); expect(value2.__getValue()).toEqual({x: 3, y: 4}); }); it('should track with springs', () => { var value1 = new Animated.ValueXY(); var value2 = new Animated.ValueXY(); Animated.spring(value2, { toValue: value1, tension: 3000, // faster spring for faster test friction: 60, }).start(); value1.setValue({x: 1, y: 1}); jest.runAllTimers(); expect(Math.round(value2.__getValue().x)).toEqual(1); expect(Math.round(value2.__getValue().y)).toEqual(1); value1.setValue({x: 2, y: 2}); jest.runAllTimers(); expect(Math.round(value2.__getValue().x)).toEqual(2); expect(Math.round(value2.__getValue().y)).toEqual(2); }); }); describe('Animated Listeners', () => { it('should get updates', () => { var value1 = new Animated.Value(0); var listener = jest.fn(); var id = value1.addListener(listener); value1.setValue(42); expect(listener.mock.calls.length).toBe(1); expect(listener).toBeCalledWith({value: 42}); expect(value1.__getValue()).toBe(42); value1.setValue(7); expect(listener.mock.calls.length).toBe(2); expect(listener).toBeCalledWith({value: 7}); expect(value1.__getValue()).toBe(7); value1.removeListener(id); value1.setValue(1492); expect(listener.mock.calls.length).toBe(2); expect(value1.__getValue()).toBe(1492); }); it('should removeAll', () => { var value1 = new Animated.Value(0); var listener = jest.fn(); [1,2,3,4].forEach(() => value1.addListener(listener)); value1.setValue(42); expect(listener.mock.calls.length).toBe(4); expect(listener).toBeCalledWith({value: 42}); value1.removeAllListeners(); value1.setValue(7); expect(listener.mock.calls.length).toBe(4); }); }); describe('Animated Diff Clamp', () => { it('should get the proper value', () => { const inputValues = [0, 20, 40, 30, 0, -40, -10, -20, 0]; const expectedValues = [0, 20, 20, 10, 0, 0, 20, 10, 20]; const value = new Animated.Value(0); const diffClampValue = Animated.diffClamp(value, 0, 20); for (let i = 0; i < inputValues.length; i++) { value.setValue(inputValues[i]); expect(diffClampValue.__getValue()).toBe(expectedValues[i]); } }); }); }); ================================================ FILE: Libraries/Animated/src/__tests__/AnimatedNative-test.js ================================================ /** * 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. * * @emails oncall+react_native */ 'use strict'; jest .clearAllMocks() .setMock('Text', {}) .setMock('View', {}) .setMock('Image', {}) .setMock('React', {Component: class {}}) .setMock('NativeModules', { NativeAnimatedModule: {}, }) .mock('NativeEventEmitter') // findNodeHandle is imported from ReactNative so mock that whole module. .setMock('ReactNative', {findNodeHandle: () => 1}); const Animated = require('Animated'); const NativeAnimatedHelper = require('NativeAnimatedHelper'); function createAndMountComponent(ComponentClass, props) { const component = new ComponentClass(); component.props = props; component.componentWillMount(); // Simulate that refs were set. component._component = {}; component.componentDidMount(); return component; } describe('Native Animated', () => { const nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; beforeEach(() => { nativeAnimatedModule.addAnimatedEventToView = jest.fn(); nativeAnimatedModule.connectAnimatedNodes = jest.fn(); nativeAnimatedModule.connectAnimatedNodeToView = jest.fn(); nativeAnimatedModule.createAnimatedNode = jest.fn(); nativeAnimatedModule.disconnectAnimatedNodeFromView = jest.fn(); nativeAnimatedModule.disconnectAnimatedNodes = jest.fn(); nativeAnimatedModule.dropAnimatedNode = jest.fn(); nativeAnimatedModule.extractAnimatedNodeOffset = jest.fn(); nativeAnimatedModule.flattenAnimatedNodeOffset = jest.fn(); nativeAnimatedModule.removeAnimatedEventFromView = jest.fn(); nativeAnimatedModule.setAnimatedNodeOffset = jest.fn(); nativeAnimatedModule.setAnimatedNodeValue = jest.fn(); nativeAnimatedModule.startAnimatingNode = jest.fn(); nativeAnimatedModule.startListeningToAnimatedNodeValue = jest.fn(); nativeAnimatedModule.stopAnimation = jest.fn(); nativeAnimatedModule.stopListeningToAnimatedNodeValue = jest.fn(); }); describe('Animated Value', () => { it('proxies `setValue` correctly', () => { const anim = new Animated.Value(0); Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); const c = createAndMountComponent(Animated.View, { style: { opacity: anim, }, }); // We expect `setValue` not to propagate down to `setNativeProps`, otherwise it may try to access `setNativeProps` // via component refs table that we override here. c.refs = { node: { setNativeProps: jest.genMockFunction(), }, }; anim.setValue(0.5); expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(expect.any(Number), 0.5); expect(c.refs.node.setNativeProps).not.toHaveBeenCalled(); }); it('should set offset', () => { const anim = new Animated.Value(0); anim.setOffset(10); anim.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: anim, }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), {type: 'value', value: 0, offset: 10}, ); anim.setOffset(20); expect(nativeAnimatedModule.setAnimatedNodeOffset) .toBeCalledWith(expect.any(Number), 20); }); it('should flatten offset', () => { const anim = new Animated.Value(0); anim.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: anim, }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), {type: 'value', value: 0, offset: 0}, ); anim.flattenOffset(); expect(nativeAnimatedModule.flattenAnimatedNodeOffset) .toBeCalledWith(expect.any(Number)); }); it('should extract offset', () => { const anim = new Animated.Value(0); anim.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: anim, }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), {type: 'value', value: 0, offset: 0}, ); anim.extractOffset(); expect(nativeAnimatedModule.extractAnimatedNodeOffset) .toBeCalledWith(expect.any(Number)); }); }); describe('Animated Listeners', () => { it('should get updates', () => { const value1 = new Animated.Value(0); value1.__makeNative(); const listener = jest.fn(); const id = value1.addListener(listener); expect(nativeAnimatedModule.startListeningToAnimatedNodeValue) .toHaveBeenCalledWith(value1.__getNativeTag()); NativeAnimatedHelper.nativeEventEmitter.emit( 'onAnimatedValueUpdate', {value: 42, tag: value1.__getNativeTag()}, ); expect(listener).toHaveBeenCalledTimes(1); expect(listener).toBeCalledWith({value: 42}); expect(value1.__getValue()).toBe(42); NativeAnimatedHelper.nativeEventEmitter.emit( 'onAnimatedValueUpdate', {value: 7, tag: value1.__getNativeTag()}, ); expect(listener).toHaveBeenCalledTimes(2); expect(listener).toBeCalledWith({value: 7}); expect(value1.__getValue()).toBe(7); value1.removeListener(id); expect(nativeAnimatedModule.stopListeningToAnimatedNodeValue) .toHaveBeenCalledWith(value1.__getNativeTag()); NativeAnimatedHelper.nativeEventEmitter.emit( 'onAnimatedValueUpdate', {value: 1492, tag: value1.__getNativeTag()}, ); expect(listener).toHaveBeenCalledTimes(2); expect(value1.__getValue()).toBe(7); }); it('should removeAll', () => { const value1 = new Animated.Value(0); value1.__makeNative(); const listener = jest.fn(); [1,2,3,4].forEach(() => value1.addListener(listener)); expect(nativeAnimatedModule.startListeningToAnimatedNodeValue) .toHaveBeenCalledWith(value1.__getNativeTag()); NativeAnimatedHelper.nativeEventEmitter.emit( 'onAnimatedValueUpdate', {value: 42, tag: value1.__getNativeTag()}, ); expect(listener).toHaveBeenCalledTimes(4); expect(listener).toBeCalledWith({value: 42}); value1.removeAllListeners(); expect(nativeAnimatedModule.stopListeningToAnimatedNodeValue) .toHaveBeenCalledWith(value1.__getNativeTag()); NativeAnimatedHelper.nativeEventEmitter.emit( 'onAnimatedValueUpdate', {value: 7, tag: value1.__getNativeTag()}, ); expect(listener).toHaveBeenCalledTimes(4); }); }); describe('Animated Events', () => { it('should map events', () => { const value = new Animated.Value(0); value.__makeNative(); const event = Animated.event( [{nativeEvent: {state: {foo: value}}}], {useNativeDriver: true}, ); const c = createAndMountComponent(Animated.View, {onTouchMove: event}); expect(nativeAnimatedModule.addAnimatedEventToView).toBeCalledWith( expect.any(Number), 'onTouchMove', {nativeEventPath: ['state', 'foo'], animatedValueTag: value.__getNativeTag()}, ); c.componentWillUnmount(); expect(nativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith( expect.any(Number), 'onTouchMove', value.__getNativeTag(), ); }); it('should throw on invalid event path', () => { const value = new Animated.Value(0); value.__makeNative(); const event = Animated.event( [{notNativeEvent: {foo: value}}], {useNativeDriver: true}, ); expect(() => createAndMountComponent(Animated.View, {onTouchMove: event})) .toThrowError(/nativeEvent/); expect(nativeAnimatedModule.addAnimatedEventToView).not.toBeCalled(); }); it('should call listeners', () => { const value = new Animated.Value(0); value.__makeNative(); const listener = jest.fn(); const event = Animated.event( [{nativeEvent: {foo: value}}], {useNativeDriver: true, listener}, ); const handler = event.__getHandler(); handler({foo: 42}); expect(listener).toHaveBeenCalledTimes(1); expect(listener).toBeCalledWith({foo: 42}); }); }); describe('Animated Graph', () => { it('creates and detaches nodes', () => { const anim = new Animated.Value(0); const c = createAndMountComponent(Animated.View, { style: { opacity: anim, }, }); Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); c.componentWillUnmount(); expect(nativeAnimatedModule.createAnimatedNode).toHaveBeenCalledTimes(3); expect(nativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(2); expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( expect.any(Number), expect.any(Number), {type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1}, expect.any(Function) ); expect(nativeAnimatedModule.disconnectAnimatedNodes).toHaveBeenCalledTimes(2); expect(nativeAnimatedModule.dropAnimatedNode).toHaveBeenCalledTimes(3); }); it('sends a valid description for value, style and props nodes', () => { const anim = new Animated.Value(0); createAndMountComponent(Animated.View, { style: { opacity: anim, }, }); Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(expect.any(Number), {type: 'value', value: 0, offset: 0}); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(expect.any(Number), {type: 'style', style: {opacity: expect.any(Number)}}); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(expect.any(Number), {type: 'props', props: {style: expect.any(Number)}}); }); it('sends a valid graph description for Animated.add nodes', () => { const first = new Animated.Value(1); const second = new Animated.Value(2); first.__makeNative(); second.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: Animated.add(first, second), }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), {type: 'addition', input: expect.any(Array)}, ); const additionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( (call) => call[1].type === 'addition' ); expect(additionCalls.length).toBe(1); const additionCall = additionCalls[0]; const additionNodeTag = additionCall[0]; const additionConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( (call) => call[1] === additionNodeTag ); expect(additionConnectionCalls.length).toBe(2); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(additionCall[1].input[0], {type: 'value', value: 1, offset: 0}); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(additionCall[1].input[1], {type: 'value', value: 2, offset: 0}); }); it('sends a valid graph description for Animated.multiply nodes', () => { const first = new Animated.Value(2); const second = new Animated.Value(1); first.__makeNative(); second.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: Animated.multiply(first, second), }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), {type: 'multiplication', input: expect.any(Array)}, ); const multiplicationCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( (call) => call[1].type === 'multiplication' ); expect(multiplicationCalls.length).toBe(1); const multiplicationCall = multiplicationCalls[0]; const multiplicationNodeTag = multiplicationCall[0]; const multiplicationConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( (call) => call[1] === multiplicationNodeTag ); expect(multiplicationConnectionCalls.length).toBe(2); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(multiplicationCall[1].input[0], {type: 'value', value: 2, offset: 0}); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(multiplicationCall[1].input[1], {type: 'value', value: 1, offset: 0}); }); it('sends a valid graph description for Animated.divide nodes', () => { const first = new Animated.Value(4); const second = new Animated.Value(2); first.__makeNative(); second.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: Animated.divide(first, second), }, }); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(expect.any(Number), {type: 'division', input: expect.any(Array)}); const divisionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( (call) => call[1].type === 'division' ); expect(divisionCalls.length).toBe(1); const divisionCall = divisionCalls[0]; const divisionNodeTag = divisionCall[0]; const divisionConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( (call) => call[1] === divisionNodeTag ); expect(divisionConnectionCalls.length).toBe(2); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(divisionCall[1].input[0], {type: 'value', value: 4, offset: 0}); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(divisionCall[1].input[1], {type: 'value', value: 2, offset: 0}); }); it('sends a valid graph description for Animated.modulo nodes', () => { const value = new Animated.Value(4); value.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: Animated.modulo(value, 4), }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), {type: 'modulus', modulus: 4, input: expect.any(Number)}, ); const moduloCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( (call) => call[1].type === 'modulus' ); expect(moduloCalls.length).toBe(1); const moduloCall = moduloCalls[0]; const moduloNodeTag = moduloCall[0]; const moduloConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( (call) => call[1] === moduloNodeTag ); expect(moduloConnectionCalls.length).toBe(1); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(moduloCall[1].input, {type: 'value', value: 4, offset: 0}); }); it('sends a valid graph description for interpolate() nodes', () => { const value = new Animated.Value(10); value.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: value.interpolate({ inputRange: [10, 20], outputRange: [0, 1], }), }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), {type: 'value', value: 10, offset: 0} ); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(expect.any(Number), { type: 'interpolation', inputRange: [10, 20], outputRange: [0, 1], extrapolateLeft: 'extend', extrapolateRight: 'extend', }); const interpolationNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find( (call) => call[1].type === 'interpolation' )[0]; const valueNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find( (call) => call[1].type === 'value' )[0]; expect(nativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(valueNodeTag, interpolationNodeTag); }); it('sends a valid graph description for transform nodes', () => { const value = new Animated.Value(0); value.__makeNative(); createAndMountComponent(Animated.View, { style: { transform: [{translateX: value}, {scale: 2}], }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), { type: 'transform', transforms: [{ nodeTag: expect.any(Number), property: 'translateX', type: 'animated', }, { value: 2, property: 'scale', type: 'static', }], }, ); }); it('sends a valid graph description for Animated.diffClamp nodes', () => { const value = new Animated.Value(2); value.__makeNative(); createAndMountComponent(Animated.View, { style: { opacity: Animated.diffClamp(value, 0, 20), }, }); expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( expect.any(Number), {type: 'diffclamp', input: expect.any(Number), max: 20, min: 0}, ); const diffClampCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( (call) => call[1].type === 'diffclamp' ); expect(diffClampCalls.length).toBe(1); const diffClampCall = diffClampCalls[0]; const diffClampNodeTag = diffClampCall[0]; const diffClampConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( (call) => call[1] === diffClampNodeTag ); expect(diffClampConnectionCalls.length).toBe(1); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(diffClampCall[1].input, {type: 'value', value: 2, offset: 0}); }); it('doesn\'t call into native API if useNativeDriver is set to false', () => { const anim = new Animated.Value(0); const c = createAndMountComponent(Animated.View, { style: { opacity: anim, }, }); Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: false}).start(); c.componentWillUnmount(); expect(nativeAnimatedModule.createAnimatedNode).not.toBeCalled(); }); it('fails when trying to run non-native animation on native node', () => { const anim = new Animated.Value(0); createAndMountComponent(Animated.View, { style: { opacity: anim, }, }); Animated.timing(anim, {toValue: 10, duration: 50, useNativeDriver: true}).start(); jest.runAllTimers(); Animated.timing(anim, {toValue: 4, duration: 500, useNativeDriver: false}).start(); expect(jest.runAllTimers).toThrow(); }); it('fails for unsupported styles', () => { const anim = new Animated.Value(0); createAndMountComponent(Animated.View, { style: { left: anim, }, }); const animation = Animated.timing(anim, {toValue: 10, duration: 50, useNativeDriver: true}); expect(animation.start).toThrowError(/left/); }); it('works for any `static` props and styles', () => { // Passing "unsupported" props should work just fine as long as they are not animated const value = new Animated.Value(0); value.__makeNative(); createAndMountComponent(Animated.View, { style: { left: 10, top: 20, opacity: value, }, removeClippedSubviews: true, }); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(expect.any(Number), { type: 'style', style: { opacity: expect.any(Number) }}); expect(nativeAnimatedModule.createAnimatedNode) .toBeCalledWith(expect.any(Number), { type: 'props', props: { style: expect.any(Number) }}); }); }); describe('Animations', () => { it('sends a valid timing animation description', () => { const anim = new Animated.Value(0); Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( expect.any(Number), expect.any(Number), {type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1}, expect.any(Function) ); }); it('sends a valid spring animation description', () => { const anim = new Animated.Value(0); Animated.spring(anim, {toValue: 10, friction: 5, tension: 164, useNativeDriver: true}).start(); expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( expect.any(Number), expect.any(Number), { type: 'spring', stiffness: 679.08, damping: 16, mass: 1, initialVelocity: 0, overshootClamping: false, restDisplacementThreshold: 0.001, restSpeedThreshold: 0.001, toValue: 10, iterations: 1, }, expect.any(Function) ); Animated.spring(anim, { toValue: 10, stiffness: 1000, damping: 500, mass: 3, useNativeDriver: true }).start(); expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( expect.any(Number), expect.any(Number), { type: 'spring', stiffness: 1000, damping: 500, mass: 3, initialVelocity: 0, overshootClamping: false, restDisplacementThreshold: 0.001, restSpeedThreshold: 0.001, toValue: 10, iterations: 1, }, expect.any(Function) ); Animated.spring(anim, {toValue: 10, bounciness: 8, speed: 10, useNativeDriver: true}).start(); expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( expect.any(Number), expect.any(Number), { type: 'spring', damping: 23.05223140901191, initialVelocity: 0, overshootClamping: false, restDisplacementThreshold: 0.001, restSpeedThreshold: 0.001, stiffness: 299.61882352941177, mass: 1, toValue: 10, iterations: 1, }, expect.any(Function) ); }); it('sends a valid decay animation description', () => { const anim = new Animated.Value(0); Animated.decay(anim, {velocity: 10, deceleration: 0.1, useNativeDriver: true}).start(); expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( expect.any(Number), expect.any(Number), {type: 'decay', deceleration: 0.1, velocity: 10, iterations: 1}, expect.any(Function) ); }); it('works with Animated.loop', () => { const anim = new Animated.Value(0); Animated.loop( Animated.decay(anim, {velocity: 10, deceleration: 0.1, useNativeDriver: true}), { iterations: 10 }, ).start(); expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( expect.any(Number), expect.any(Number), {type: 'decay', deceleration: 0.1, velocity: 10, iterations: 10}, expect.any(Function) ); }); it('sends stopAnimation command to native', () => { const value = new Animated.Value(0); const animation = Animated.timing(value, {toValue: 10, duration: 50, useNativeDriver: true}); animation.start(); expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( expect.any(Number), expect.any(Number), {type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1}, expect.any(Function) ); const animationId = nativeAnimatedModule.startAnimatingNode.mock.calls[0][0]; animation.stop(); expect(nativeAnimatedModule.stopAnimation).toBeCalledWith(animationId); }); }); }); ================================================ FILE: Libraries/Animated/src/__tests__/Easing-test.js ================================================ /** * 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. * * @emails oncall+react_native */ 'use strict'; var Easing = require('Easing'); describe('Easing', () => { it('should work with linear', () => { var easing = Easing.linear; expect(easing(0)).toBe(0); expect(easing(0.5)).toBe(0.5); expect(easing(0.8)).toBe(0.8); expect(easing(1)).toBe(1); }); it('should work with ease in linear', () => { var easing = Easing.in(Easing.linear); expect(easing(0)).toBe(0); expect(easing(0.5)).toBe(0.5); expect(easing(0.8)).toBe(0.8); expect(easing(1)).toBe(1); }); it('should work with easy out linear', () => { var easing = Easing.out(Easing.linear); expect(easing(0)).toBe(0); expect(easing(0.5)).toBe(0.5); expect(easing(0.6)).toBe(0.6); expect(easing(1)).toBe(1); }); it('should work with ease in quad', () => { function easeInQuad(t) { return t * t; } var easing = Easing.in(Easing.quad); for (var t = -0.5; t < 1.5; t += 0.1) { expect(easing(t)).toBe(easeInQuad(t)); } }); it('should work with ease out quad', () => { function easeOutQuad(t) { return -t * (t - 2); } var easing = Easing.out(Easing.quad); for (var t = 0; t <= 1; t += 0.1) { expect(easing(1)).toBe(easeOutQuad(1)); } }); it('should work with ease in-out quad', () => { function easeInOutQuad(t) { t = t * 2; if (t < 1) { return 0.5 * t * t; } return -((t - 1) * (t - 3) - 1) / 2; } var easing = Easing.inOut(Easing.quad); for (var t = -0.5; t < 1.5; t += 0.1) { expect(easing(t)).toBeCloseTo(easeInOutQuad(t), 4); } }); it('should satisfy boundary conditions with elastic', () => { for (var b = 0; b < 4; b += 0.3) { var easing = Easing.elastic(b); expect(easing(0)).toBe(0); expect(easing(1)).toBe(1); } }); function sampleEasingFunction(easing) { var DURATION = 300; var tickCount = Math.round(DURATION * 60 / 1000); var samples = []; for (var i = 0; i <= tickCount; i++) { samples.push(easing(i / tickCount)); } return samples; } var Samples = { in_quad: [0,0.0030864197530864196,0.012345679012345678,0.027777777777777776,0.04938271604938271,0.0771604938271605,0.1111111111111111,0.15123456790123457,0.19753086419753085,0.25,0.308641975308642,0.37345679012345684,0.4444444444444444,0.5216049382716049,0.6049382716049383,0.6944444444444445,0.7901234567901234,0.8919753086419753,1], out_quad: [0,0.10802469135802469,0.20987654320987653,0.3055555555555555,0.3950617283950617,0.47839506172839513,0.5555555555555556,0.6265432098765432,0.691358024691358,0.75,0.8024691358024691,0.8487654320987654,0.888888888888889,0.9228395061728394,0.9506172839506174,0.9722222222222221,0.9876543209876543,0.9969135802469136,1], inOut_quad: [0,0.006172839506172839,0.024691358024691357,0.05555555555555555,0.09876543209876543,0.154320987654321,0.2222222222222222,0.30246913580246915,0.3950617283950617,0.5,0.6049382716049383,0.697530864197531,0.7777777777777777,0.845679012345679,0.9012345679012346,0.9444444444444444,0.9753086419753086,0.9938271604938271,1], in_cubic: [0,0.00017146776406035664,0.0013717421124828531,0.004629629629629629,0.010973936899862825,0.021433470507544586,0.037037037037037035,0.05881344307270234,0.0877914951989026,0.125,0.1714677640603567,0.22822359396433475,0.2962962962962963,0.37671467764060357,0.4705075445816187,0.5787037037037038,0.7023319615912208,0.8424211248285322,1], out_cubic: [0,0.15757887517146785,0.2976680384087792,0.42129629629629617,0.5294924554183813,0.6232853223593964,0.7037037037037036,0.7717764060356652,0.8285322359396433,0.875,0.9122085048010974,0.9411865569272977,0.9629629629629629,0.9785665294924554,0.9890260631001372,0.9953703703703703,0.9986282578875172,0.9998285322359396,1], inOut_cubic: [0,0.0006858710562414266,0.0054869684499314125,0.018518518518518517,0.0438957475994513,0.08573388203017834,0.14814814814814814,0.23525377229080935,0.3511659807956104,0.5,0.6488340192043895,0.7647462277091908,0.8518518518518519,0.9142661179698217,0.9561042524005487,0.9814814814814815,0.9945130315500685,0.9993141289437586,1], in_sin: [0,0.003805301908254455,0.01519224698779198,0.03407417371093169,0.06030737921409157,0.09369221296335006,0.1339745962155613,0.1808479557110082,0.233955556881022,0.2928932188134524,0.35721239031346064,0.42642356364895384,0.4999999999999999,0.5773817382593005,0.6579798566743311,0.7411809548974793,0.8263518223330696,0.9128442572523416,0.9999999999999999], out_sin: [0,0.08715574274765817,0.17364817766693033,0.25881904510252074,0.3420201433256687,0.42261826174069944,0.49999999999999994,0.573576436351046,0.6427876096865393,0.7071067811865475,0.766044443118978,0.8191520442889918,0.8660254037844386,0.9063077870366499,0.9396926207859083,0.9659258262890683,0.984807753012208,0.9961946980917455,1], inOut_sin: [0,0.00759612349389599,0.030153689607045786,0.06698729810778065,0.116977778440511,0.17860619515673032,0.24999999999999994,0.32898992833716556,0.4131759111665348,0.49999999999999994,0.5868240888334652,0.6710100716628343,0.7499999999999999,0.8213938048432696,0.883022221559489,0.9330127018922194,0.9698463103929542,0.9924038765061041,1], in_exp: [0,0.0014352875901128893,0.002109491677524035,0.0031003926796253885,0.004556754060844206,0.006697218616039631,0.009843133202303688,0.014466792379488908,0.021262343752724643,0.03125,0.045929202883612456,0.06750373368076916,0.09921256574801243,0.1458161299470146,0.2143109957132682,0.31498026247371835,0.46293735614364506,0.6803950000871883,1], out_exp: [0,0.31960499991281155,0.5370626438563548,0.6850197375262816,0.7856890042867318,0.8541838700529854,0.9007874342519875,0.9324962663192309,0.9540707971163875,0.96875,0.9787376562472754,0.9855332076205111,0.9901568667976963,0.9933027813839603,0.9954432459391558,0.9968996073203746,0.9978905083224759,0.9985647124098871,1], inOut_exp: [0,0.0010547458387620175,0.002278377030422103,0.004921566601151844,0.010631171876362321,0.022964601441806228,0.049606282874006216,0.1071554978566341,0.23146867807182253,0.5,0.7685313219281775,0.892844502143366,0.9503937171259937,0.9770353985581938,0.9893688281236377,0.9950784333988482,0.9977216229695779,0.998945254161238,1], in_circle: [0,0.0015444024660317135,0.006192010000093506,0.013986702816730645,0.025003956956430873,0.03935464078941209,0.057190958417936644,0.07871533601238889,0.10419358352238339,0.1339745962155614,0.1685205807169019,0.20845517506805522,0.2546440075000701,0.3083389112228482,0.37146063894529113,0.4472292016074334,0.5418771527091488,0.6713289009389102,1], out_circle: [0,0.3286710990610898,0.45812284729085123,0.5527707983925666,0.6285393610547089,0.6916610887771518,0.7453559924999298,0.7915448249319448,0.8314794192830981,0.8660254037844386,0.8958064164776166,0.9212846639876111,0.9428090415820634,0.9606453592105879,0.9749960430435691,0.9860132971832694,0.9938079899999065,0.9984555975339683,1], inOut_circle: [0,0.003096005000046753,0.012501978478215436,0.028595479208968322,0.052096791761191696,0.08426029035845095,0.12732200375003505,0.18573031947264557,0.2709385763545744,0.5,0.7290614236454256,0.8142696805273546,0.8726779962499649,0.915739709641549,0.9479032082388084,0.9714045207910317,0.9874980215217846,0.9969039949999532,1], in_back_: [0,-0.004788556241426612,-0.017301289437585736,-0.0347587962962963,-0.05438167352537723,-0.07339051783264748,-0.08900592592592595,-0.09844849451303156,-0.0989388203017833,-0.08769750000000004,-0.06194513031550073,-0.018902307956104283,0.044210370370370254,0.13017230795610413,0.2417629080932785,0.3817615740740742,0.5529477091906719,0.7581007167352535,0.9999999999999998], out_back_: [2.220446049250313e-16,0.24189928326474652,0.44705229080932807,0.6182384259259258,0.7582370919067215,0.8698276920438959,0.9557896296296297,1.0189023079561044,1.0619451303155008,1.0876975,1.0989388203017834,1.0984484945130315,1.089005925925926,1.0733905178326475,1.0543816735253773,1.0347587962962963,1.0173012894375857,1.0047885562414267,1], }; Object.keys(Samples).forEach(function(type) { it('should ease ' + type, function() { var [modeName, easingName, isFunction] = type.split('_'); var easing = Easing[easingName]; if (isFunction !== undefined) { easing = easing(); } var computed = sampleEasingFunction(Easing[modeName](easing)); var samples = Samples[type]; computed.forEach((value, key) => { expect(value).toBeCloseTo(samples[key], 2); }); }); }); }); ================================================ FILE: Libraries/Animated/src/__tests__/Interpolation-test.js ================================================ /** * 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. * * @emails oncall+react_native */ 'use strict'; var AnimatedInterpolation = require('../nodes/AnimatedInterpolation'); var Easing = require('Easing'); describe('Interpolation', () => { it('should work with defaults', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: [0, 1], }); expect(interpolation(0)).toBe(0); expect(interpolation(0.5)).toBe(0.5); expect(interpolation(0.8)).toBe(0.8); expect(interpolation(1)).toBe(1); }); it('should work with output range', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: [100, 200], }); expect(interpolation(0)).toBe(100); expect(interpolation(0.5)).toBe(150); expect(interpolation(0.8)).toBe(180); expect(interpolation(1)).toBe(200); }); it('should work with input range', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [100, 200], outputRange: [0, 1], }); expect(interpolation(100)).toBe(0); expect(interpolation(150)).toBe(0.5); expect(interpolation(180)).toBe(0.8); expect(interpolation(200)).toBe(1); }); it('should throw for non monotonic input ranges', () => { expect(() => AnimatedInterpolation.__createInterpolation({ inputRange: [0, 2, 1], outputRange: [0, 1, 2], }), ).toThrow(); expect(() => AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1, 2], outputRange: [0, 3, 1], }), ).not.toThrow(); }); it('should work with empty input range', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 10, 10], outputRange: [1, 2, 3], extrapolate: 'extend', }); expect(interpolation(0)).toBe(1); expect(interpolation(5)).toBe(1.5); expect(interpolation(10)).toBe(2); expect(interpolation(10.1)).toBe(3); expect(interpolation(15)).toBe(3); }); it('should work with empty output range', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [1, 2, 3], outputRange: [0, 10, 10], extrapolate: 'extend', }); expect(interpolation(0)).toBe(-10); expect(interpolation(1.5)).toBe(5); expect(interpolation(2)).toBe(10); expect(interpolation(2.5)).toBe(10); expect(interpolation(3)).toBe(10); expect(interpolation(4)).toBe(10); }); it('should work with easing', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: [0, 1], easing: Easing.quad, }); expect(interpolation(0)).toBe(0); expect(interpolation(0.5)).toBe(0.25); expect(interpolation(0.9)).toBe(0.81); expect(interpolation(1)).toBe(1); }); it('should work with extrapolate', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: [0, 1], extrapolate: 'extend', easing: Easing.quad, }); expect(interpolation(-2)).toBe(4); expect(interpolation(2)).toBe(4); interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: [0, 1], extrapolate: 'clamp', easing: Easing.quad, }); expect(interpolation(-2)).toBe(0); expect(interpolation(2)).toBe(1); interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: [0, 1], extrapolate: 'identity', easing: Easing.quad, }); expect(interpolation(-2)).toBe(-2); expect(interpolation(2)).toBe(2); }); it('should work with keyframes with extrapolate', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 10, 100, 1000], outputRange: [0, 5, 50, 500], extrapolate: true, }); expect(interpolation(-5)).toBe(-2.5); expect(interpolation(0)).toBe(0); expect(interpolation(5)).toBe(2.5); expect(interpolation(10)).toBe(5); expect(interpolation(50)).toBe(25); expect(interpolation(100)).toBe(50); expect(interpolation(500)).toBe(250); expect(interpolation(1000)).toBe(500); expect(interpolation(2000)).toBe(1000); }); it('should work with keyframes without extrapolate', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1, 2], outputRange: [0.2, 1, 0.2], extrapolate: 'clamp', }); expect(interpolation(5)).toBeCloseTo(0.2); }); it('should throw for an infinite input range', () => { expect(() => AnimatedInterpolation.__createInterpolation({ inputRange: [-Infinity, Infinity], outputRange: [0, 1], }), ).toThrow(); expect(() => AnimatedInterpolation.__createInterpolation({ inputRange: [-Infinity, 0, Infinity], outputRange: [1, 2, 3], }), ).not.toThrow(); }); it('should work with negative infinite', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [-Infinity, 0], outputRange: [-Infinity, 0], easing: Easing.quad, extrapolate: 'identity', }); expect(interpolation(-Infinity)).toBe(-Infinity); expect(interpolation(-100)).toBeCloseTo(-10000); expect(interpolation(-10)).toBeCloseTo(-100); expect(interpolation(0)).toBeCloseTo(0); expect(interpolation(1)).toBeCloseTo(1); expect(interpolation(100)).toBeCloseTo(100); }); it('should work with positive infinite', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [5, Infinity], outputRange: [5, Infinity], easing: Easing.quad, extrapolate: 'identity', }); expect(interpolation(-100)).toBeCloseTo(-100); expect(interpolation(-10)).toBeCloseTo(-10); expect(interpolation(0)).toBeCloseTo(0); expect(interpolation(5)).toBeCloseTo(5); expect(interpolation(6)).toBeCloseTo(5 + 1); expect(interpolation(10)).toBeCloseTo(5 + 25); expect(interpolation(100)).toBeCloseTo(5 + 95 * 95); expect(interpolation(Infinity)).toBe(Infinity); }); it('should work with output ranges as string', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.4)'], }); expect(interpolation(0)).toBe('rgba(0, 100, 200, 0)'); expect(interpolation(0.5)).toBe('rgba(25, 125, 225, 0.2)'); expect(interpolation(1)).toBe('rgba(50, 150, 250, 0.4)'); }); it('should work with output ranges as short hex string', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: ['#024', '#9BF'], }); expect(interpolation(0)).toBe('rgba(0, 34, 68, 1)'); expect(interpolation(0.5)).toBe('rgba(77, 111, 162, 1)'); expect(interpolation(1)).toBe('rgba(153, 187, 255, 1)'); }); it('should work with output ranges as long hex string', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: ['#FF9500', '#87FC70'], }); expect(interpolation(0)).toBe('rgba(255, 149, 0, 1)'); expect(interpolation(0.5)).toBe('rgba(195, 201, 56, 1)'); expect(interpolation(1)).toBe('rgba(135, 252, 112, 1)'); }); it('should work with output ranges with mixed hex and rgba strings', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: ['rgba(100, 120, 140, .4)', '#87FC70'], }); expect(interpolation(0)).toBe('rgba(100, 120, 140, 0.4)'); expect(interpolation(0.5)).toBe('rgba(118, 186, 126, 0.7)'); expect(interpolation(1)).toBe('rgba(135, 252, 112, 1)'); }); it('should work with negative and decimal values in string ranges', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: ['-100.5deg', '100deg'], }); expect(interpolation(0)).toBe('-100.5deg'); expect(interpolation(0.5)).toBe('-0.25deg'); expect(interpolation(1)).toBe('100deg'); }); it('should crash when chaining an interpolation that returns a string', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: [0, 1], }); expect(() => { interpolation('45rad'); }).toThrow(); }); it('should support a mix of color patterns', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1, 2], outputRange: ['rgba(0, 100, 200, 0)', 'rgb(50, 150, 250)', 'red'], }); expect(interpolation(0)).toBe('rgba(0, 100, 200, 0)'); expect(interpolation(0.5)).toBe('rgba(25, 125, 225, 0.5)'); expect(interpolation(1.5)).toBe('rgba(153, 75, 125, 1)'); expect(interpolation(2)).toBe('rgba(255, 0, 0, 1)'); }); it('should crash when defining output range with different pattern', () => { expect(() => AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: ['20deg', '30rad'], }), ).toThrow(); }); it('should round the alpha channel of a color to the nearest thousandth', () => { var interpolation = AnimatedInterpolation.__createInterpolation({ inputRange: [0, 1], outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 1)'], }); expect(interpolation(1e-12)).toBe('rgba(0, 0, 0, 0)'); expect(interpolation(2 / 3)).toBe('rgba(0, 0, 0, 0.667)'); }); }); ================================================ FILE: Libraries/Animated/src/__tests__/bezier-test.js ================================================ /** * BezierEasing - use bezier curve for transition easing function * https://github.com/gre/bezier-easing * * @copyright 2014-2015 Gaetan Renaudeau. MIT License. * @noflow * @emails oncall+react_native * @format */ 'use strict'; var bezier = require('bezier'); var identity = function(x) { return x; }; function assertClose(a, b, precision = 3) { expect(a).toBeCloseTo(b, precision); } function makeAssertCloseWithPrecision(precision) { return function(a, b) { assertClose(a, b, precision); }; } function allEquals(be1, be2, samples, assertion) { if (!assertion) { assertion = assertClose; } for (var i = 0; i <= samples; ++i) { var x = i / samples; assertion(be1(x), be2(x)); } } function repeat(n) { return function(f) { for (var i = 0; i < n; ++i) { f(i); } }; } describe('bezier', function() { it('should be a function', function() { expect(typeof bezier === 'function').toBe(true); }); it('should creates an object', function() { expect(typeof bezier(0, 0, 1, 1) === 'function').toBe(true); }); it('should fail with wrong arguments', function() { expect(function() { bezier(0.5, 0.5, -5, 0.5); }).toThrow(); expect(function() { bezier(0.5, 0.5, 5, 0.5); }).toThrow(); expect(function() { bezier(-2, 0.5, 0.5, 0.5); }).toThrow(); expect(function() { bezier(2, 0.5, 0.5, 0.5); }).toThrow(); }); describe('linear curves', function() { it('should be linear', function() { allEquals(bezier(0, 0, 1, 1), bezier(1, 1, 0, 0), 100); allEquals(bezier(0, 0, 1, 1), identity, 100); }); }); describe('common properties', function() { it('should be the right value at extremes', function() { repeat(10)(function() { var a = Math.random(), b = 2 * Math.random() - 0.5, c = Math.random(), d = 2 * Math.random() - 0.5; var easing = bezier(a, b, c, d); expect(easing(0)).toBe(0); expect(easing(1)).toBe(1); }); }); it('should approach the projected value of its x=y projected curve', function() { repeat(10)(function() { var a = Math.random(), b = Math.random(), c = Math.random(), d = Math.random(); var easing = bezier(a, b, c, d); var projected = bezier(b, a, d, c); var composed = function(x) { return projected(easing(x)); }; allEquals(identity, composed, 100, makeAssertCloseWithPrecision(2)); }); }); }); describe('two same instances', function() { it('should be strictly equals', function() { repeat(10)(function() { var a = Math.random(), b = 2 * Math.random() - 0.5, c = Math.random(), d = 2 * Math.random() - 0.5; allEquals(bezier(a, b, c, d), bezier(a, b, c, d), 100, 0); }); }); }); describe('symetric curves', function() { it('should have a central value y~=0.5 at x=0.5', function() { repeat(10)(function() { var a = Math.random(), b = 2 * Math.random() - 0.5, c = 1 - a, d = 1 - b; var easing = bezier(a, b, c, d); assertClose(easing(0.5), 0.5, 2); }); }); it('should be symetrical', function() { repeat(10)(function() { var a = Math.random(), b = 2 * Math.random() - 0.5, c = 1 - a, d = 1 - b; var easing = bezier(a, b, c, d); var sym = function(x) { return 1 - easing(1 - x); }; allEquals(easing, sym, 100, makeAssertCloseWithPrecision(2)); }); }); }); }); ================================================ FILE: Libraries/Animated/src/animations/Animation.js ================================================ /** * 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 Animation * @flow * @format */ 'use strict'; const NativeAnimatedHelper = require('NativeAnimatedHelper'); import type AnimatedValue from '../nodes/AnimatedValue'; export type EndResult = {finished: boolean}; export type EndCallback = (result: EndResult) => void; export type AnimationConfig = { isInteraction?: boolean, useNativeDriver?: boolean, onComplete?: ?EndCallback, iterations?: number, }; // Important note: start() and stop() will only be called at most once. // Once an animation has been stopped or finished its course, it will // not be reused. class Animation { __active: boolean; __isInteraction: boolean; __nativeId: number; __onEnd: ?EndCallback; __iterations: number; start( fromValue: number, onUpdate: (value: number) => void, onEnd: ?EndCallback, previousAnimation: ?Animation, animatedValue: AnimatedValue, ): void {} stop(): void { if (this.__nativeId) { NativeAnimatedHelper.API.stopAnimation(this.__nativeId); } } __getNativeAnimationConfig(): any { // Subclasses that have corresponding animation implementation done in native // should override this method throw new Error('This animation type cannot be offloaded to native'); } // Helper function for subclasses to make sure onEnd is only called once. __debouncedOnEnd(result: EndResult): void { const onEnd = this.__onEnd; this.__onEnd = null; onEnd && onEnd(result); } __startNativeAnimation(animatedValue: AnimatedValue): void { animatedValue.__makeNative(); this.__nativeId = NativeAnimatedHelper.generateNewAnimationId(); NativeAnimatedHelper.API.startAnimatingNode( this.__nativeId, animatedValue.__getNativeTag(), this.__getNativeAnimationConfig(), this.__debouncedOnEnd.bind(this), ); } } module.exports = Animation; ================================================ FILE: Libraries/Animated/src/animations/DecayAnimation.js ================================================ /** * 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 DecayAnimation * @flow * @format */ 'use strict'; const Animation = require('./Animation'); const {shouldUseNativeDriver} = require('../NativeAnimatedHelper'); import type {AnimationConfig, EndCallback} from './Animation'; import type AnimatedValue from '../nodes/AnimatedValue'; export type DecayAnimationConfig = AnimationConfig & { velocity: number | {x: number, y: number}, deceleration?: number, }; export type DecayAnimationConfigSingle = AnimationConfig & { velocity: number, deceleration?: number, }; class DecayAnimation extends Animation { _startTime: number; _lastValue: number; _fromValue: number; _deceleration: number; _velocity: number; _onUpdate: (value: number) => void; _animationFrame: any; _useNativeDriver: boolean; constructor(config: DecayAnimationConfigSingle) { super(); this._deceleration = config.deceleration !== undefined ? config.deceleration : 0.998; this._velocity = config.velocity; this._useNativeDriver = shouldUseNativeDriver(config); this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; this.__iterations = config.iterations !== undefined ? config.iterations : 1; } __getNativeAnimationConfig() { return { type: 'decay', deceleration: this._deceleration, velocity: this._velocity, iterations: this.__iterations, }; } start( fromValue: number, onUpdate: (value: number) => void, onEnd: ?EndCallback, previousAnimation: ?Animation, animatedValue: AnimatedValue, ): void { this.__active = true; this._lastValue = fromValue; this._fromValue = fromValue; this._onUpdate = onUpdate; this.__onEnd = onEnd; this._startTime = Date.now(); if (this._useNativeDriver) { this.__startNativeAnimation(animatedValue); } else { this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); } } onUpdate(): void { const now = Date.now(); const value = this._fromValue + this._velocity / (1 - this._deceleration) * (1 - Math.exp(-(1 - this._deceleration) * (now - this._startTime))); this._onUpdate(value); if (Math.abs(this._lastValue - value) < 0.1) { this.__debouncedOnEnd({finished: true}); return; } this._lastValue = value; if (this.__active) { this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); } } stop(): void { super.stop(); this.__active = false; global.cancelAnimationFrame(this._animationFrame); this.__debouncedOnEnd({finished: false}); } } module.exports = DecayAnimation; ================================================ FILE: Libraries/Animated/src/animations/SpringAnimation.js ================================================ /** * 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 SpringAnimation * @flow * @format */ 'use strict'; const AnimatedValue = require('../nodes/AnimatedValue'); const AnimatedValueXY = require('../nodes/AnimatedValueXY'); const Animation = require('./Animation'); const SpringConfig = require('../SpringConfig'); const invariant = require('fbjs/lib/invariant'); const {shouldUseNativeDriver} = require('../NativeAnimatedHelper'); import type {AnimationConfig, EndCallback} from './Animation'; export type SpringAnimationConfig = AnimationConfig & { toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY, overshootClamping?: boolean, restDisplacementThreshold?: number, restSpeedThreshold?: number, velocity?: number | {x: number, y: number}, bounciness?: number, speed?: number, tension?: number, friction?: number, stiffness?: number, damping?: number, mass?: number, delay?: number, }; export type SpringAnimationConfigSingle = AnimationConfig & { toValue: number | AnimatedValue, overshootClamping?: boolean, restDisplacementThreshold?: number, restSpeedThreshold?: number, velocity?: number, bounciness?: number, speed?: number, tension?: number, friction?: number, stiffness?: number, damping?: number, mass?: number, delay?: number, }; function withDefault(value: ?T, defaultValue: T): T { if (value === undefined || value === null) { return defaultValue; } return value; } class SpringAnimation extends Animation { _overshootClamping: boolean; _restDisplacementThreshold: number; _restSpeedThreshold: number; _lastVelocity: number; _startPosition: number; _lastPosition: number; _fromValue: number; _toValue: any; _stiffness: number; _damping: number; _mass: number; _initialVelocity: number; _delay: number; _timeout: any; _startTime: number; _lastTime: number; _frameTime: number; _onUpdate: (value: number) => void; _animationFrame: any; _useNativeDriver: boolean; constructor(config: SpringAnimationConfigSingle) { super(); this._overshootClamping = withDefault(config.overshootClamping, false); this._restDisplacementThreshold = withDefault( config.restDisplacementThreshold, 0.001, ); this._restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001); this._initialVelocity = withDefault(config.velocity, 0); this._lastVelocity = withDefault(config.velocity, 0); this._toValue = config.toValue; this._delay = withDefault(config.delay, 0); this._useNativeDriver = shouldUseNativeDriver(config); this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; this.__iterations = config.iterations !== undefined ? config.iterations : 1; if ( config.stiffness !== undefined || config.damping !== undefined || config.mass !== undefined ) { invariant( config.bounciness === undefined && config.speed === undefined && config.tension === undefined && config.friction === undefined, 'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one', ); this._stiffness = withDefault(config.stiffness, 100); this._damping = withDefault(config.damping, 10); this._mass = withDefault(config.mass, 1); } else if (config.bounciness !== undefined || config.speed !== undefined) { // Convert the origami bounciness/speed values to stiffness/damping // We assume mass is 1. invariant( config.tension === undefined && config.friction === undefined && config.stiffness === undefined && config.damping === undefined && config.mass === undefined, 'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one', ); const springConfig = SpringConfig.fromBouncinessAndSpeed( withDefault(config.bounciness, 8), withDefault(config.speed, 12), ); this._stiffness = springConfig.stiffness; this._damping = springConfig.damping; this._mass = 1; } else { // Convert the origami tension/friction values to stiffness/damping // We assume mass is 1. const springConfig = SpringConfig.fromOrigamiTensionAndFriction( withDefault(config.tension, 40), withDefault(config.friction, 7), ); this._stiffness = springConfig.stiffness; this._damping = springConfig.damping; this._mass = 1; } invariant(this._stiffness > 0, 'Stiffness value must be greater than 0'); invariant(this._damping > 0, 'Damping value must be greater than 0'); invariant(this._mass > 0, 'Mass value must be greater than 0'); } __getNativeAnimationConfig() { return { type: 'spring', overshootClamping: this._overshootClamping, restDisplacementThreshold: this._restDisplacementThreshold, restSpeedThreshold: this._restSpeedThreshold, stiffness: this._stiffness, damping: this._damping, mass: this._mass, initialVelocity: withDefault(this._initialVelocity, this._lastVelocity), toValue: this._toValue, iterations: this.__iterations, }; } start( fromValue: number, onUpdate: (value: number) => void, onEnd: ?EndCallback, previousAnimation: ?Animation, animatedValue: AnimatedValue, ): void { this.__active = true; this._startPosition = fromValue; this._lastPosition = this._startPosition; this._onUpdate = onUpdate; this.__onEnd = onEnd; this._lastTime = Date.now(); this._frameTime = 0.0; if (previousAnimation instanceof SpringAnimation) { const internalState = previousAnimation.getInternalState(); this._lastPosition = internalState.lastPosition; this._lastVelocity = internalState.lastVelocity; // Set the initial velocity to the last velocity this._initialVelocity = this._lastVelocity; this._lastTime = internalState.lastTime; } const start = () => { if (this._useNativeDriver) { this.__startNativeAnimation(animatedValue); } else { this.onUpdate(); } }; // If this._delay is more than 0, we start after the timeout. if (this._delay) { this._timeout = setTimeout(start, this._delay); } else { start(); } } getInternalState(): Object { return { lastPosition: this._lastPosition, lastVelocity: this._lastVelocity, lastTime: this._lastTime, }; } /** * This spring model is based off of a damped harmonic oscillator * (https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator). * * We use the closed form of the second order differential equation: * * x'' + (2ζ⍵_0)x' + ⍵^2x = 0 * * where * ⍵_0 = √(k / m) (undamped angular frequency of the oscillator), * ζ = c / 2√mk (damping ratio), * c = damping constant * k = stiffness * m = mass * * The derivation of the closed form is described in detail here: * http://planetmath.org/sites/default/files/texpdf/39745.pdf * * This algorithm happens to match the algorithm used by CASpringAnimation, * a QuartzCore (iOS) API that creates spring animations. */ onUpdate(): void { // If for some reason we lost a lot of frames (e.g. process large payload or // stopped in the debugger), we only advance by 4 frames worth of // computation and will continue on the next frame. It's better to have it // running at faster speed than jumping to the end. const MAX_STEPS = 64; let now = Date.now(); if (now > this._lastTime + MAX_STEPS) { now = this._lastTime + MAX_STEPS; } const deltaTime = (now - this._lastTime) / 1000; this._frameTime += deltaTime; const c: number = this._damping; const m: number = this._mass; const k: number = this._stiffness; const v0: number = -this._initialVelocity; const zeta = c / (2 * Math.sqrt(k * m)); // damping ratio const omega0 = Math.sqrt(k / m); // undamped angular frequency of the oscillator (rad/ms) const omega1 = omega0 * Math.sqrt(1.0 - zeta * zeta); // exponential decay const x0 = this._toValue - this._startPosition; // calculate the oscillation from x0 = 1 to x = 0 let position = 0.0; let velocity = 0.0; const t = this._frameTime; if (zeta < 1) { // Under damped const envelope = Math.exp(-zeta * omega0 * t); position = this._toValue - envelope * ((v0 + zeta * omega0 * x0) / omega1 * Math.sin(omega1 * t) + x0 * Math.cos(omega1 * t)); // This looks crazy -- it's actually just the derivative of the // oscillation function velocity = zeta * omega0 * envelope * (Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0) / omega1 + x0 * Math.cos(omega1 * t)) - envelope * (Math.cos(omega1 * t) * (v0 + zeta * omega0 * x0) - omega1 * x0 * Math.sin(omega1 * t)); } else { // Critically damped const envelope = Math.exp(-omega0 * t); position = this._toValue - envelope * (x0 + (v0 + omega0 * x0) * t); velocity = envelope * (v0 * (t * omega0 - 1) + t * x0 * (omega0 * omega0)); } this._lastTime = now; this._lastPosition = position; this._lastVelocity = velocity; this._onUpdate(position); if (!this.__active) { // a listener might have stopped us in _onUpdate return; } // Conditions for stopping the spring animation let isOvershooting = false; if (this._overshootClamping && this._stiffness !== 0) { if (this._startPosition < this._toValue) { isOvershooting = position > this._toValue; } else { isOvershooting = position < this._toValue; } } const isVelocity = Math.abs(velocity) <= this._restSpeedThreshold; let isDisplacement = true; if (this._stiffness !== 0) { isDisplacement = Math.abs(this._toValue - position) <= this._restDisplacementThreshold; } if (isOvershooting || (isVelocity && isDisplacement)) { if (this._stiffness !== 0) { // Ensure that we end up with a round value this._lastPosition = this._toValue; this._lastVelocity = 0; this._onUpdate(this._toValue); } this.__debouncedOnEnd({finished: true}); return; } this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); } stop(): void { super.stop(); this.__active = false; clearTimeout(this._timeout); global.cancelAnimationFrame(this._animationFrame); this.__debouncedOnEnd({finished: false}); } } module.exports = SpringAnimation; ================================================ FILE: Libraries/Animated/src/animations/TimingAnimation.js ================================================ /** * 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 TimingAnimation * @flow * @format */ 'use strict'; const AnimatedValue = require('../nodes/AnimatedValue'); const AnimatedValueXY = require('../nodes/AnimatedValueXY'); const Animation = require('./Animation'); const {shouldUseNativeDriver} = require('../NativeAnimatedHelper'); import type {AnimationConfig, EndCallback} from './Animation'; export type TimingAnimationConfig = AnimationConfig & { toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY, easing?: (value: number) => number, duration?: number, delay?: number, }; export type TimingAnimationConfigSingle = AnimationConfig & { toValue: number | AnimatedValue, easing?: (value: number) => number, duration?: number, delay?: number, }; let _easeInOut; function easeInOut() { if (!_easeInOut) { const Easing = require('Easing'); _easeInOut = Easing.inOut(Easing.ease); } return _easeInOut; } class TimingAnimation extends Animation { _startTime: number; _fromValue: number; _toValue: any; _duration: number; _delay: number; _easing: (value: number) => number; _onUpdate: (value: number) => void; _animationFrame: any; _timeout: any; _useNativeDriver: boolean; constructor(config: TimingAnimationConfigSingle) { super(); this._toValue = config.toValue; this._easing = config.easing !== undefined ? config.easing : easeInOut(); this._duration = config.duration !== undefined ? config.duration : 500; this._delay = config.delay !== undefined ? config.delay : 0; this.__iterations = config.iterations !== undefined ? config.iterations : 1; this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; this._useNativeDriver = shouldUseNativeDriver(config); } __getNativeAnimationConfig(): any { const frameDuration = 1000.0 / 60.0; const frames = []; for (let dt = 0.0; dt < this._duration; dt += frameDuration) { frames.push(this._easing(dt / this._duration)); } frames.push(this._easing(1)); return { type: 'frames', frames, toValue: this._toValue, iterations: this.__iterations, }; } start( fromValue: number, onUpdate: (value: number) => void, onEnd: ?EndCallback, previousAnimation: ?Animation, animatedValue: AnimatedValue, ): void { this.__active = true; this._fromValue = fromValue; this._onUpdate = onUpdate; this.__onEnd = onEnd; const start = () => { // Animations that sometimes have 0 duration and sometimes do not // still need to use the native driver when duration is 0 so as to // not cause intermixed JS and native animations. if (this._duration === 0 && !this._useNativeDriver) { this._onUpdate(this._toValue); this.__debouncedOnEnd({finished: true}); } else { this._startTime = Date.now(); if (this._useNativeDriver) { this.__startNativeAnimation(animatedValue); } else { this._animationFrame = requestAnimationFrame( this.onUpdate.bind(this), ); } } }; if (this._delay) { this._timeout = setTimeout(start, this._delay); } else { start(); } } onUpdate(): void { const now = Date.now(); if (now >= this._startTime + this._duration) { if (this._duration === 0) { this._onUpdate(this._toValue); } else { this._onUpdate( this._fromValue + this._easing(1) * (this._toValue - this._fromValue), ); } this.__debouncedOnEnd({finished: true}); return; } this._onUpdate( this._fromValue + this._easing((now - this._startTime) / this._duration) * (this._toValue - this._fromValue), ); if (this.__active) { this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this)); } } stop(): void { super.stop(); this.__active = false; clearTimeout(this._timeout); global.cancelAnimationFrame(this._animationFrame); this.__debouncedOnEnd({finished: false}); } } module.exports = TimingAnimation; ================================================ FILE: Libraries/Animated/src/bezier.js ================================================ /** * BezierEasing - use bezier curve for transition easing function * https://github.com/gre/bezier-easing * * @copyright 2014-2015 Gaëtan Renaudeau. MIT License. * @providesModule bezier * @noflow */ 'use strict'; // These values are established by empiricism with tests (tradeoff: performance VS precision) var NEWTON_ITERATIONS = 4; var NEWTON_MIN_SLOPE = 0.001; var SUBDIVISION_PRECISION = 0.0000001; var SUBDIVISION_MAX_ITERATIONS = 10; var kSplineTableSize = 11; var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); var float32ArraySupported = typeof Float32Array === 'function'; function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } function C (aA1) { return 3.0 * aA1; } // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } function binarySubdivide (aX, aA, aB, mX1, mX2) { var currentX, currentT, i = 0; do { currentT = aA + (aB - aA) / 2.0; currentX = calcBezier(currentT, mX1, mX2) - aX; if (currentX > 0.0) { aB = currentT; } else { aA = currentT; } } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); return currentT; } function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { for (var i = 0; i < NEWTON_ITERATIONS; ++i) { var currentSlope = getSlope(aGuessT, mX1, mX2); if (currentSlope === 0.0) { return aGuessT; } var currentX = calcBezier(aGuessT, mX1, mX2) - aX; aGuessT -= currentX / currentSlope; } return aGuessT; } module.exports = function bezier (mX1, mY1, mX2, mY2) { if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { // eslint-disable-line yoda throw new Error('bezier x values must be in [0, 1] range'); } // Precompute samples table var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); if (mX1 !== mY1 || mX2 !== mY2) { for (var i = 0; i < kSplineTableSize; ++i) { sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); } } function getTForX (aX) { var intervalStart = 0.0; var currentSample = 1; var lastSample = kSplineTableSize - 1; for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { intervalStart += kSampleStepSize; } --currentSample; // Interpolate to provide an initial guess for t var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); var guessForT = intervalStart + dist * kSampleStepSize; var initialSlope = getSlope(guessForT, mX1, mX2); if (initialSlope >= NEWTON_MIN_SLOPE) { return newtonRaphsonIterate(aX, guessForT, mX1, mX2); } else if (initialSlope === 0.0) { return guessForT; } else { return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); } } return function BezierEasing (x) { if (mX1 === mY1 && mX2 === mY2) { return x; // linear } // Because JavaScript number are imprecise, we should guarantee the extremes are right. if (x === 0) { return 0; } if (x === 1) { return 1; } return calcBezier(getTForX(x), mY1, mY2); }; }; ================================================ FILE: Libraries/Animated/src/createAnimatedComponent.js ================================================ /** * 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 createAnimatedComponent * @flow * @format */ 'use strict'; const {AnimatedEvent} = require('./AnimatedEvent'); const AnimatedProps = require('./nodes/AnimatedProps'); const React = require('React'); const ViewStylePropTypes = require('ViewStylePropTypes'); function createAnimatedComponent(Component: any): any { class AnimatedComponent extends React.Component { _component: any; _invokeAnimatedPropsCallbackOnMount: boolean = false; _prevComponent: any; _propsAnimated: AnimatedProps; _eventDetachers: Array = []; _setComponentRef: Function; static __skipSetNativeProps_FOR_TESTS_ONLY = false; constructor(props: Object) { super(props); this._setComponentRef = this._setComponentRef.bind(this); } componentWillUnmount() { this._propsAnimated && this._propsAnimated.__detach(); this._detachNativeEvents(); } setNativeProps(props) { this._component.setNativeProps(props); } componentWillMount() { this._attachProps(this.props); } componentDidMount() { if (this._invokeAnimatedPropsCallbackOnMount) { this._invokeAnimatedPropsCallbackOnMount = false; this._animatedPropsCallback(); } this._propsAnimated.setNativeView(this._component); this._attachNativeEvents(); } _attachNativeEvents() { // Make sure to get the scrollable node for components that implement // `ScrollResponder.Mixin`. const scrollableNode = this._component.getScrollableNode ? this._component.getScrollableNode() : this._component; for (const key in this.props) { const prop = this.props[key]; if (prop instanceof AnimatedEvent && prop.__isNative) { prop.__attach(scrollableNode, key); this._eventDetachers.push(() => prop.__detach(scrollableNode, key)); } } } _detachNativeEvents() { this._eventDetachers.forEach(remove => remove()); this._eventDetachers = []; } // The system is best designed when setNativeProps is implemented. It is // able to avoid re-rendering and directly set the attributes that changed. // However, setNativeProps can only be implemented on leaf native // components. If you want to animate a composite component, you need to // re-render it. In this case, we have a fallback that uses forceUpdate. _animatedPropsCallback = () => { if (this._component == null) { // AnimatedProps is created in will-mount because it's used in render. // But this callback may be invoked before mount in async mode, // In which case we should defer the setNativeProps() call. // React may throw away uncommitted work in async mode, // So a deferred call won't always be invoked. this._invokeAnimatedPropsCallbackOnMount = true; } else if ( AnimatedComponent.__skipSetNativeProps_FOR_TESTS_ONLY || typeof this._component.setNativeProps !== 'function' ) { this.forceUpdate(); } else if (!this._propsAnimated.__isNative) { this._component.setNativeProps( this._propsAnimated.__getAnimatedValue(), ); } else { throw new Error( 'Attempting to run JS driven animation on animated ' + 'node that has been moved to "native" earlier by starting an ' + 'animation with `useNativeDriver: true`', ); } }; _attachProps(nextProps) { const oldPropsAnimated = this._propsAnimated; this._propsAnimated = new AnimatedProps( nextProps, this._animatedPropsCallback, ); // When you call detach, it removes the element from the parent list // of children. If it goes to 0, then the parent also detaches itself // and so on. // An optimization is to attach the new elements and THEN detach the old // ones instead of detaching and THEN attaching. // This way the intermediate state isn't to go to 0 and trigger // this expensive recursive detaching to then re-attach everything on // the very next operation. oldPropsAnimated && oldPropsAnimated.__detach(); } componentWillReceiveProps(newProps) { this._attachProps(newProps); } componentDidUpdate(prevProps) { if (this._component !== this._prevComponent) { this._propsAnimated.setNativeView(this._component); } if (this._component !== this._prevComponent || prevProps !== this.props) { this._detachNativeEvents(); this._attachNativeEvents(); } } render() { const props = this._propsAnimated.__getValue(); return ( ); } _setComponentRef(c) { this._prevComponent = this._component; this._component = c; } // A third party library can use getNode() // to get the node reference of the decorated component getNode() { return this._component; } } const propTypes = Component.propTypes; AnimatedComponent.propTypes = { style: function(props, propName, componentName) { if (!propTypes) { return; } for (const key in ViewStylePropTypes) { if (!propTypes[key] && props[key] !== undefined) { console.warn( 'You are setting the style `{ ' + key + ': ... }` as a prop. You ' + 'should nest it in a style object. ' + 'E.g. `{ style: { ' + key + ': ... } }`', ); } } }, }; return AnimatedComponent; } module.exports = createAnimatedComponent; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedAddition.js ================================================ /** * 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 AnimatedAddition * @flow * @format */ 'use strict'; const AnimatedInterpolation = require('./AnimatedInterpolation'); const AnimatedNode = require('./AnimatedNode'); const AnimatedValue = require('./AnimatedValue'); const AnimatedWithChildren = require('./AnimatedWithChildren'); import type {InterpolationConfigType} from './AnimatedInterpolation'; class AnimatedAddition extends AnimatedWithChildren { _a: AnimatedNode; _b: AnimatedNode; constructor(a: AnimatedNode | number, b: AnimatedNode | number) { super(); this._a = typeof a === 'number' ? new AnimatedValue(a) : a; this._b = typeof b === 'number' ? new AnimatedValue(b) : b; } __makeNative() { this._a.__makeNative(); this._b.__makeNative(); super.__makeNative(); } __getValue(): number { return this._a.__getValue() + this._b.__getValue(); } interpolate(config: InterpolationConfigType): AnimatedInterpolation { return new AnimatedInterpolation(this, config); } __attach(): void { this._a.__addChild(this); this._b.__addChild(this); } __detach(): void { this._a.__removeChild(this); this._b.__removeChild(this); super.__detach(); } __getNativeConfig(): any { return { type: 'addition', input: [this._a.__getNativeTag(), this._b.__getNativeTag()], }; } } module.exports = AnimatedAddition; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedDiffClamp.js ================================================ /** * 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 AnimatedDiffClamp * @flow * @format */ 'use strict'; const AnimatedInterpolation = require('./AnimatedInterpolation'); const AnimatedNode = require('./AnimatedNode'); const AnimatedWithChildren = require('./AnimatedWithChildren'); import type {InterpolationConfigType} from './AnimatedInterpolation'; class AnimatedDiffClamp extends AnimatedWithChildren { _a: AnimatedNode; _min: number; _max: number; _value: number; _lastValue: number; constructor(a: AnimatedNode, min: number, max: number) { super(); this._a = a; this._min = min; this._max = max; this._value = this._lastValue = this._a.__getValue(); } __makeNative() { this._a.__makeNative(); super.__makeNative(); } interpolate(config: InterpolationConfigType): AnimatedInterpolation { return new AnimatedInterpolation(this, config); } __getValue(): number { const value = this._a.__getValue(); const diff = value - this._lastValue; this._lastValue = value; this._value = Math.min(Math.max(this._value + diff, this._min), this._max); return this._value; } __attach(): void { this._a.__addChild(this); } __detach(): void { this._a.__removeChild(this); super.__detach(); } __getNativeConfig(): any { return { type: 'diffclamp', input: this._a.__getNativeTag(), min: this._min, max: this._max, }; } } module.exports = AnimatedDiffClamp; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedDivision.js ================================================ /** * 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 AnimatedDivision * @flow * @format */ 'use strict'; const AnimatedInterpolation = require('./AnimatedInterpolation'); const AnimatedNode = require('./AnimatedNode'); const AnimatedValue = require('./AnimatedValue'); const AnimatedWithChildren = require('./AnimatedWithChildren'); import type {InterpolationConfigType} from './AnimatedInterpolation'; class AnimatedDivision extends AnimatedWithChildren { _a: AnimatedNode; _b: AnimatedNode; constructor(a: AnimatedNode | number, b: AnimatedNode | number) { super(); this._a = typeof a === 'number' ? new AnimatedValue(a) : a; this._b = typeof b === 'number' ? new AnimatedValue(b) : b; } __makeNative() { this._a.__makeNative(); this._b.__makeNative(); super.__makeNative(); } __getValue(): number { const a = this._a.__getValue(); const b = this._b.__getValue(); if (b === 0) { console.error('Detected division by zero in AnimatedDivision'); } return a / b; } interpolate(config: InterpolationConfigType): AnimatedInterpolation { return new AnimatedInterpolation(this, config); } __attach(): void { this._a.__addChild(this); this._b.__addChild(this); } __detach(): void { this._a.__removeChild(this); this._b.__removeChild(this); super.__detach(); } __getNativeConfig(): any { return { type: 'division', input: [this._a.__getNativeTag(), this._b.__getNativeTag()], }; } } module.exports = AnimatedDivision; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedInterpolation.js ================================================ /** * 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 AnimatedInterpolation * @flow * @format */ /* eslint no-bitwise: 0 */ 'use strict'; const AnimatedNode = require('./AnimatedNode'); const AnimatedWithChildren = require('./AnimatedWithChildren'); const NativeAnimatedHelper = require('../NativeAnimatedHelper'); const invariant = require('fbjs/lib/invariant'); const normalizeColor = require('normalizeColor'); type ExtrapolateType = 'extend' | 'identity' | 'clamp'; export type InterpolationConfigType = { inputRange: Array, /* $FlowFixMe(>=0.38.0 site=react_native_fb,react_native_oss) - Flow error * detected during the deployment of v0.38.0. To see the error, remove this * comment and run flow */ outputRange: Array | Array, easing?: (input: number) => number, extrapolate?: ExtrapolateType, extrapolateLeft?: ExtrapolateType, extrapolateRight?: ExtrapolateType, }; const linear = t => t; /** * Very handy helper to map input ranges to output ranges with an easing * function and custom behavior outside of the ranges. */ function createInterpolation( config: InterpolationConfigType, ): (input: number) => number | string { if (config.outputRange && typeof config.outputRange[0] === 'string') { return createInterpolationFromStringOutputRange(config); } const outputRange: Array = (config.outputRange: any); checkInfiniteRange('outputRange', outputRange); const inputRange = config.inputRange; checkInfiniteRange('inputRange', inputRange); checkValidInputRange(inputRange); invariant( inputRange.length === outputRange.length, 'inputRange (' + inputRange.length + ') and outputRange (' + outputRange.length + ') must have the same length', ); const easing = config.easing || linear; let extrapolateLeft: ExtrapolateType = 'extend'; if (config.extrapolateLeft !== undefined) { extrapolateLeft = config.extrapolateLeft; } else if (config.extrapolate !== undefined) { extrapolateLeft = config.extrapolate; } let extrapolateRight: ExtrapolateType = 'extend'; if (config.extrapolateRight !== undefined) { extrapolateRight = config.extrapolateRight; } else if (config.extrapolate !== undefined) { extrapolateRight = config.extrapolate; } return input => { invariant( typeof input === 'number', 'Cannot interpolation an input which is not a number', ); const range = findRange(input, inputRange); return interpolate( input, inputRange[range], inputRange[range + 1], outputRange[range], outputRange[range + 1], easing, extrapolateLeft, extrapolateRight, ); }; } function interpolate( input: number, inputMin: number, inputMax: number, outputMin: number, outputMax: number, easing: (input: number) => number, extrapolateLeft: ExtrapolateType, extrapolateRight: ExtrapolateType, ) { let result = input; // Extrapolate if (result < inputMin) { if (extrapolateLeft === 'identity') { return result; } else if (extrapolateLeft === 'clamp') { result = inputMin; } else if (extrapolateLeft === 'extend') { // noop } } if (result > inputMax) { if (extrapolateRight === 'identity') { return result; } else if (extrapolateRight === 'clamp') { result = inputMax; } else if (extrapolateRight === 'extend') { // noop } } if (outputMin === outputMax) { return outputMin; } if (inputMin === inputMax) { if (input <= inputMin) { return outputMin; } return outputMax; } // Input Range if (inputMin === -Infinity) { result = -result; } else if (inputMax === Infinity) { result = result - inputMin; } else { result = (result - inputMin) / (inputMax - inputMin); } // Easing result = easing(result); // Output Range if (outputMin === -Infinity) { result = -result; } else if (outputMax === Infinity) { result = result + outputMin; } else { result = result * (outputMax - outputMin) + outputMin; } return result; } function colorToRgba(input: string): string { let int32Color = normalizeColor(input); if (int32Color === null) { return input; } int32Color = int32Color || 0; const r = (int32Color & 0xff000000) >>> 24; const g = (int32Color & 0x00ff0000) >>> 16; const b = (int32Color & 0x0000ff00) >>> 8; const a = (int32Color & 0x000000ff) / 255; return `rgba(${r}, ${g}, ${b}, ${a})`; } const stringShapeRegex = /[0-9\.-]+/g; /** * Supports string shapes by extracting numbers so new values can be computed, * and recombines those values into new strings of the same shape. Supports * things like: * * rgba(123, 42, 99, 0.36) // colors * -45deg // values with units */ function createInterpolationFromStringOutputRange( config: InterpolationConfigType, ): (input: number) => string { let outputRange: Array = (config.outputRange: any); invariant(outputRange.length >= 2, 'Bad output range'); outputRange = outputRange.map(colorToRgba); checkPattern(outputRange); // ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.5)'] // -> // [ // [0, 50], // [100, 150], // [200, 250], // [0, 0.5], // ] /* $FlowFixMe(>=0.18.0): `outputRange[0].match()` can return `null`. Need to * guard against this possibility. */ const outputRanges = outputRange[0].match(stringShapeRegex).map(() => []); outputRange.forEach(value => { /* $FlowFixMe(>=0.18.0): `value.match()` can return `null`. Need to guard * against this possibility. */ value.match(stringShapeRegex).forEach((number, i) => { outputRanges[i].push(+number); }); }); /* $FlowFixMe(>=0.18.0): `outputRange[0].match()` can return `null`. Need to * guard against this possibility. */ const interpolations = outputRange[0] .match(stringShapeRegex) .map((value, i) => { return createInterpolation({ ...config, outputRange: outputRanges[i], }); }); // rgba requires that the r,g,b are integers.... so we want to round them, but we *dont* want to // round the opacity (4th column). const shouldRound = isRgbOrRgba(outputRange[0]); return input => { let i = 0; // 'rgba(0, 100, 200, 0)' // -> // 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...' return outputRange[0].replace(stringShapeRegex, () => { const val = +interpolations[i++](input); const rounded = shouldRound && i < 4 ? Math.round(val) : Math.round(val * 1000) / 1000; return String(rounded); }); }; } function isRgbOrRgba(range) { return typeof range === 'string' && range.startsWith('rgb'); } function checkPattern(arr: Array) { const pattern = arr[0].replace(stringShapeRegex, ''); for (let i = 1; i < arr.length; ++i) { invariant( pattern === arr[i].replace(stringShapeRegex, ''), 'invalid pattern ' + arr[0] + ' and ' + arr[i], ); } } function findRange(input: number, inputRange: Array) { let i; for (i = 1; i < inputRange.length - 1; ++i) { if (inputRange[i] >= input) { break; } } return i - 1; } function checkValidInputRange(arr: Array) { invariant(arr.length >= 2, 'inputRange must have at least 2 elements'); for (let i = 1; i < arr.length; ++i) { invariant( arr[i] >= arr[i - 1], /* $FlowFixMe(>=0.13.0) - In the addition expression below this comment, * one or both of the operands may be something that doesn't cleanly * convert to a string, like undefined, null, and object, etc. If you really * mean this implicit string conversion, you can do something like * String(myThing) */ 'inputRange must be monotonically increasing ' + arr, ); } } function checkInfiniteRange(name: string, arr: Array) { invariant(arr.length >= 2, name + ' must have at least 2 elements'); invariant( arr.length !== 2 || arr[0] !== -Infinity || arr[1] !== Infinity, /* $FlowFixMe(>=0.13.0) - In the addition expression below this comment, * one or both of the operands may be something that doesn't cleanly convert * to a string, like undefined, null, and object, etc. If you really mean * this implicit string conversion, you can do something like * String(myThing) */ name + 'cannot be ]-infinity;+infinity[ ' + arr, ); } class AnimatedInterpolation extends AnimatedWithChildren { // Export for testing. static __createInterpolation = createInterpolation; _parent: AnimatedNode; _config: InterpolationConfigType; _interpolation: (input: number) => number | string; constructor(parent: AnimatedNode, config: InterpolationConfigType) { super(); this._parent = parent; this._config = config; this._interpolation = createInterpolation(config); } __makeNative() { this._parent.__makeNative(); super.__makeNative(); } __getValue(): number | string { const parentValue: number = this._parent.__getValue(); invariant( typeof parentValue === 'number', 'Cannot interpolate an input which is not a number.', ); return this._interpolation(parentValue); } interpolate(config: InterpolationConfigType): AnimatedInterpolation { return new AnimatedInterpolation(this, config); } __attach(): void { this._parent.__addChild(this); } __detach(): void { this._parent.__removeChild(this); super.__detach(); } __transformDataType(range: Array) { // Change the string array type to number array // So we can reuse the same logic in iOS and Android platform return range.map(function(value) { if (typeof value !== 'string') { return value; } if (/deg$/.test(value)) { const degrees = parseFloat(value) || 0; const radians = degrees * Math.PI / 180.0; return radians; } else { // Assume radians return parseFloat(value) || 0; } }); } __getNativeConfig(): any { if (__DEV__) { NativeAnimatedHelper.validateInterpolation(this._config); } return { inputRange: this._config.inputRange, // Only the `outputRange` can contain strings so we don't need to tranform `inputRange` here outputRange: this.__transformDataType(this._config.outputRange), extrapolateLeft: this._config.extrapolateLeft || this._config.extrapolate || 'extend', extrapolateRight: this._config.extrapolateRight || this._config.extrapolate || 'extend', type: 'interpolation', }; } } module.exports = AnimatedInterpolation; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedModulo.js ================================================ /** * 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 AnimatedModulo * @flow * @format */ 'use strict'; const AnimatedInterpolation = require('./AnimatedInterpolation'); const AnimatedNode = require('./AnimatedNode'); const AnimatedWithChildren = require('./AnimatedWithChildren'); import type {InterpolationConfigType} from './AnimatedInterpolation'; class AnimatedModulo extends AnimatedWithChildren { _a: AnimatedNode; _modulus: number; constructor(a: AnimatedNode, modulus: number) { super(); this._a = a; this._modulus = modulus; } __makeNative() { this._a.__makeNative(); super.__makeNative(); } __getValue(): number { return ( (this._a.__getValue() % this._modulus + this._modulus) % this._modulus ); } interpolate(config: InterpolationConfigType): AnimatedInterpolation { return new AnimatedInterpolation(this, config); } __attach(): void { this._a.__addChild(this); } __detach(): void { this._a.__removeChild(this); super.__detach(); } __getNativeConfig(): any { return { type: 'modulus', input: this._a.__getNativeTag(), modulus: this._modulus, }; } } module.exports = AnimatedModulo; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedMultiplication.js ================================================ /** * 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 AnimatedMultiplication * @flow * @format */ 'use strict'; const AnimatedInterpolation = require('./AnimatedInterpolation'); const AnimatedNode = require('./AnimatedNode'); const AnimatedValue = require('./AnimatedValue'); const AnimatedWithChildren = require('./AnimatedWithChildren'); import type {InterpolationConfigType} from './AnimatedInterpolation'; class AnimatedMultiplication extends AnimatedWithChildren { _a: AnimatedNode; _b: AnimatedNode; constructor(a: AnimatedNode | number, b: AnimatedNode | number) { super(); this._a = typeof a === 'number' ? new AnimatedValue(a) : a; this._b = typeof b === 'number' ? new AnimatedValue(b) : b; } __makeNative() { this._a.__makeNative(); this._b.__makeNative(); super.__makeNative(); } __getValue(): number { return this._a.__getValue() * this._b.__getValue(); } interpolate(config: InterpolationConfigType): AnimatedInterpolation { return new AnimatedInterpolation(this, config); } __attach(): void { this._a.__addChild(this); this._b.__addChild(this); } __detach(): void { this._a.__removeChild(this); this._b.__removeChild(this); super.__detach(); } __getNativeConfig(): any { return { type: 'multiplication', input: [this._a.__getNativeTag(), this._b.__getNativeTag()], }; } } module.exports = AnimatedMultiplication; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedNode.js ================================================ /** * 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 AnimatedNode * @flow * @format */ 'use strict'; const NativeAnimatedHelper = require('../NativeAnimatedHelper'); const invariant = require('fbjs/lib/invariant'); // Note(vjeux): this would be better as an interface but flow doesn't // support them yet class AnimatedNode { __attach(): void {} __detach(): void { if (this.__isNative && this.__nativeTag != null) { NativeAnimatedHelper.API.dropAnimatedNode(this.__nativeTag); this.__nativeTag = undefined; } } __getValue(): any {} __getAnimatedValue(): any { return this.__getValue(); } __addChild(child: AnimatedNode) {} __removeChild(child: AnimatedNode) {} __getChildren(): Array { return []; } /* Methods and props used by native Animated impl */ __isNative: boolean; __nativeTag: ?number; __makeNative() { if (!this.__isNative) { throw new Error('This node cannot be made a "native" animated node'); } } __getNativeTag(): ?number { NativeAnimatedHelper.assertNativeAnimatedModule(); invariant( this.__isNative, 'Attempt to get native tag from node not marked as "native"', ); if (this.__nativeTag == null) { const nativeTag: ?number = NativeAnimatedHelper.generateNewNodeTag(); NativeAnimatedHelper.API.createAnimatedNode( nativeTag, this.__getNativeConfig(), ); this.__nativeTag = nativeTag; } return this.__nativeTag; } __getNativeConfig(): Object { throw new Error( 'This JS animated node type cannot be used as native animated node', ); } toJSON(): any { return this.__getValue(); } } module.exports = AnimatedNode; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedProps.js ================================================ /** * 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 AnimatedProps * @flow * @format */ 'use strict'; const {AnimatedEvent} = require('../AnimatedEvent'); const AnimatedNode = require('./AnimatedNode'); const AnimatedStyle = require('./AnimatedStyle'); const NativeAnimatedHelper = require('../NativeAnimatedHelper'); const ReactNative = require('ReactNative'); const invariant = require('fbjs/lib/invariant'); class AnimatedProps extends AnimatedNode { _props: Object; _animatedView: any; _callback: () => void; constructor(props: Object, callback: () => void) { super(); if (props.style) { props = { ...props, style: new AnimatedStyle(props.style), }; } this._props = props; this._callback = callback; this.__attach(); } __getValue(): Object { const props = {}; for (const key in this._props) { const value = this._props[key]; if (value instanceof AnimatedNode) { if (!value.__isNative || value instanceof AnimatedStyle) { // We cannot use value of natively driven nodes this way as the value we have access from // JS may not be up to date. props[key] = value.__getValue(); } } else if (value instanceof AnimatedEvent) { props[key] = value.__getHandler(); } else { props[key] = value; } } return props; } __getAnimatedValue(): Object { const props = {}; for (const key in this._props) { const value = this._props[key]; if (value instanceof AnimatedNode) { props[key] = value.__getAnimatedValue(); } } return props; } __attach(): void { for (const key in this._props) { const value = this._props[key]; if (value instanceof AnimatedNode) { value.__addChild(this); } } } __detach(): void { if (this.__isNative && this._animatedView) { this.__disconnectAnimatedView(); } for (const key in this._props) { const value = this._props[key]; if (value instanceof AnimatedNode) { value.__removeChild(this); } } super.__detach(); } update(): void { this._callback(); } __makeNative(): void { if (!this.__isNative) { this.__isNative = true; for (const key in this._props) { const value = this._props[key]; if (value instanceof AnimatedNode) { value.__makeNative(); } } if (this._animatedView) { this.__connectAnimatedView(); } } } setNativeView(animatedView: any): void { if (this._animatedView === animatedView) { return; } this._animatedView = animatedView; if (this.__isNative) { this.__connectAnimatedView(); } } __connectAnimatedView(): void { invariant(this.__isNative, 'Expected node to be marked as "native"'); const nativeViewTag: ?number = ReactNative.findNodeHandle( this._animatedView, ); invariant( nativeViewTag != null, 'Unable to locate attached view in the native tree', ); NativeAnimatedHelper.API.connectAnimatedNodeToView( this.__getNativeTag(), nativeViewTag, ); } __disconnectAnimatedView(): void { invariant(this.__isNative, 'Expected node to be marked as "native"'); const nativeViewTag: ?number = ReactNative.findNodeHandle( this._animatedView, ); invariant( nativeViewTag != null, 'Unable to locate attached view in the native tree', ); NativeAnimatedHelper.API.disconnectAnimatedNodeFromView( this.__getNativeTag(), nativeViewTag, ); } __getNativeConfig(): Object { const propsConfig = {}; for (const propKey in this._props) { const value = this._props[propKey]; if (value instanceof AnimatedNode) { propsConfig[propKey] = value.__getNativeTag(); } } return { type: 'props', props: propsConfig, }; } } module.exports = AnimatedProps; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedStyle.js ================================================ /** * 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 AnimatedStyle * @flow * @format */ 'use strict'; const AnimatedNode = require('./AnimatedNode'); const AnimatedTransform = require('./AnimatedTransform'); const AnimatedWithChildren = require('./AnimatedWithChildren'); const NativeAnimatedHelper = require('../NativeAnimatedHelper'); const flattenStyle = require('flattenStyle'); class AnimatedStyle extends AnimatedWithChildren { _style: Object; constructor(style: any) { super(); style = flattenStyle(style) || {}; if (style.transform) { style = { ...style, transform: new AnimatedTransform(style.transform), }; } this._style = style; } // Recursively get values for nested styles (like iOS's shadowOffset) _walkStyleAndGetValues(style) { const updatedStyle = {}; for (const key in style) { const value = style[key]; if (value instanceof AnimatedNode) { if (!value.__isNative) { // We cannot use value of natively driven nodes this way as the value we have access from // JS may not be up to date. updatedStyle[key] = value.__getValue(); } } else if (value && !Array.isArray(value) && typeof value === 'object') { // Support animating nested values (for example: shadowOffset.height) updatedStyle[key] = this._walkStyleAndGetValues(value); } else { updatedStyle[key] = value; } } return updatedStyle; } __getValue(): Object { return this._walkStyleAndGetValues(this._style); } // Recursively get animated values for nested styles (like iOS's shadowOffset) _walkStyleAndGetAnimatedValues(style) { const updatedStyle = {}; for (const key in style) { const value = style[key]; if (value instanceof AnimatedNode) { updatedStyle[key] = value.__getAnimatedValue(); } else if (value && !Array.isArray(value) && typeof value === 'object') { // Support animating nested values (for example: shadowOffset.height) updatedStyle[key] = this._walkStyleAndGetAnimatedValues(value); } } return updatedStyle; } __getAnimatedValue(): Object { return this._walkStyleAndGetAnimatedValues(this._style); } __attach(): void { for (const key in this._style) { const value = this._style[key]; if (value instanceof AnimatedNode) { value.__addChild(this); } } } __detach(): void { for (const key in this._style) { const value = this._style[key]; if (value instanceof AnimatedNode) { value.__removeChild(this); } } super.__detach(); } __makeNative() { super.__makeNative(); for (const key in this._style) { const value = this._style[key]; if (value instanceof AnimatedNode) { value.__makeNative(); } } } __getNativeConfig(): Object { const styleConfig = {}; for (const styleKey in this._style) { if (this._style[styleKey] instanceof AnimatedNode) { styleConfig[styleKey] = this._style[styleKey].__getNativeTag(); } // Non-animated styles are set using `setNativeProps`, no need // to pass those as a part of the node config } NativeAnimatedHelper.validateStyles(styleConfig); return { type: 'style', style: styleConfig, }; } } module.exports = AnimatedStyle; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedTracking.js ================================================ /** * 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 AnimatedTracking * @flow * @format */ 'use strict'; const AnimatedValue = require('./AnimatedValue'); const AnimatedNode = require('./AnimatedNode'); import type {EndCallback} from '../animations/Animation'; class AnimatedTracking extends AnimatedNode { _value: AnimatedValue; _parent: AnimatedNode; _callback: ?EndCallback; _animationConfig: Object; _animationClass: any; constructor( value: AnimatedValue, parent: AnimatedNode, animationClass: any, animationConfig: Object, callback?: ?EndCallback, ) { super(); this._value = value; this._parent = parent; this._animationClass = animationClass; this._animationConfig = animationConfig; this._callback = callback; this.__attach(); } __getValue(): Object { return this._parent.__getValue(); } __attach(): void { this._parent.__addChild(this); } __detach(): void { this._parent.__removeChild(this); super.__detach(); } update(): void { this._value.animate( new this._animationClass({ ...this._animationConfig, toValue: (this._animationConfig.toValue: any).__getValue(), }), this._callback, ); } } module.exports = AnimatedTracking; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedTransform.js ================================================ /** * 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 AnimatedTransform * @flow * @format */ 'use strict'; const AnimatedNode = require('./AnimatedNode'); const AnimatedWithChildren = require('./AnimatedWithChildren'); const NativeAnimatedHelper = require('../NativeAnimatedHelper'); class AnimatedTransform extends AnimatedWithChildren { _transforms: Array; constructor(transforms: Array) { super(); this._transforms = transforms; } __makeNative() { super.__makeNative(); this._transforms.forEach(transform => { for (const key in transform) { const value = transform[key]; if (value instanceof AnimatedNode) { value.__makeNative(); } } }); } __getValue(): Array { return this._transforms.map(transform => { const result = {}; for (const key in transform) { const value = transform[key]; if (value instanceof AnimatedNode) { result[key] = value.__getValue(); } else { result[key] = value; } } return result; }); } __getAnimatedValue(): Array { return this._transforms.map(transform => { const result = {}; for (const key in transform) { const value = transform[key]; if (value instanceof AnimatedNode) { result[key] = value.__getAnimatedValue(); } else { // All transform components needed to recompose matrix result[key] = value; } } return result; }); } __attach(): void { this._transforms.forEach(transform => { for (const key in transform) { const value = transform[key]; if (value instanceof AnimatedNode) { value.__addChild(this); } } }); } __detach(): void { this._transforms.forEach(transform => { for (const key in transform) { const value = transform[key]; if (value instanceof AnimatedNode) { value.__removeChild(this); } } }); super.__detach(); } __getNativeConfig(): any { const transConfigs = []; this._transforms.forEach(transform => { for (const key in transform) { const value = transform[key]; if (value instanceof AnimatedNode) { transConfigs.push({ type: 'animated', property: key, nodeTag: value.__getNativeTag(), }); } else { transConfigs.push({ type: 'static', property: key, value, }); } } }); NativeAnimatedHelper.validateTransform(transConfigs); return { type: 'transform', transforms: transConfigs, }; } } module.exports = AnimatedTransform; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedValue.js ================================================ /** * 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 AnimatedValue * @flow * @format */ 'use strict'; const AnimatedInterpolation = require('./AnimatedInterpolation'); const AnimatedNode = require('./AnimatedNode'); const AnimatedWithChildren = require('./AnimatedWithChildren'); const InteractionManager = require('InteractionManager'); const NativeAnimatedHelper = require('../NativeAnimatedHelper'); import type Animation, {EndCallback} from '../animations/Animation'; import type {InterpolationConfigType} from './AnimatedInterpolation'; const NativeAnimatedAPI = NativeAnimatedHelper.API; type ValueListenerCallback = (state: {value: number}) => void; let _uniqueId = 1; /** * Animated works by building a directed acyclic graph of dependencies * transparently when you render your Animated components. * * new Animated.Value(0) * .interpolate() .interpolate() new Animated.Value(1) * opacity translateY scale * style transform * View#234 style * View#123 * * A) Top Down phase * When an Animated.Value is updated, we recursively go down through this * graph in order to find leaf nodes: the views that we flag as needing * an update. * * B) Bottom Up phase * When a view is flagged as needing an update, we recursively go back up * in order to build the new value that it needs. The reason why we need * this two-phases process is to deal with composite props such as * transform which can receive values from multiple parents. */ function _flush(rootNode: AnimatedValue): void { const animatedStyles = new Set(); function findAnimatedStyles(node) { if (typeof node.update === 'function') { animatedStyles.add(node); } else { node.__getChildren().forEach(findAnimatedStyles); } } findAnimatedStyles(rootNode); /* $FlowFixMe */ animatedStyles.forEach(animatedStyle => animatedStyle.update()); } /** * Standard value for driving animations. One `Animated.Value` can drive * multiple properties in a synchronized fashion, but can only be driven by one * mechanism at a time. Using a new mechanism (e.g. starting a new animation, * or calling `setValue`) will stop any previous ones. * * See http://facebook.github.io/react-native/docs/animatedvalue.html */ class AnimatedValue extends AnimatedWithChildren { _value: number; _startingValue: number; _offset: number; _animation: ?Animation; _tracking: ?AnimatedNode; _listeners: {[key: string]: ValueListenerCallback}; __nativeAnimatedValueListener: ?any; constructor(value: number) { super(); this._startingValue = this._value = value; this._offset = 0; this._animation = null; this._listeners = {}; } __detach() { this.stopAnimation(); super.__detach(); } __getValue(): number { return this._value + this._offset; } __makeNative() { super.__makeNative(); if (Object.keys(this._listeners).length) { this._startListeningToNativeValueUpdates(); } } /** * Directly set the value. This will stop any animations running on the value * and update all the bound properties. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#setvalue */ setValue(value: number): void { if (this._animation) { this._animation.stop(); this._animation = null; } this._updateValue( value, !this.__isNative /* don't perform a flush for natively driven values */, ); if (this.__isNative) { NativeAnimatedAPI.setAnimatedNodeValue(this.__getNativeTag(), value); } } /** * Sets an offset that is applied on top of whatever value is set, whether via * `setValue`, an animation, or `Animated.event`. Useful for compensating * things like the start of a pan gesture. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#setoffset */ setOffset(offset: number): void { this._offset = offset; if (this.__isNative) { NativeAnimatedAPI.setAnimatedNodeOffset(this.__getNativeTag(), offset); } } /** * Merges the offset value into the base value and resets the offset to zero. * The final output of the value is unchanged. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#flattenoffset */ flattenOffset(): void { this._value += this._offset; this._offset = 0; if (this.__isNative) { NativeAnimatedAPI.flattenAnimatedNodeOffset(this.__getNativeTag()); } } /** * Sets the offset value to the base value, and resets the base value to zero. * The final output of the value is unchanged. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#extractoffset */ extractOffset(): void { this._offset += this._value; this._value = 0; if (this.__isNative) { NativeAnimatedAPI.extractAnimatedNodeOffset(this.__getNativeTag()); } } /** * Adds an asynchronous listener to the value so you can observe updates from * animations. This is useful because there is no way to * synchronously read the value because it might be driven natively. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#addlistener */ addListener(callback: ValueListenerCallback): string { const id = String(_uniqueId++); this._listeners[id] = callback; if (this.__isNative) { this._startListeningToNativeValueUpdates(); } return id; } /** * Unregister a listener. The `id` param shall match the identifier * previously returned by `addListener()`. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#removelistener */ removeListener(id: string): void { delete this._listeners[id]; if (this.__isNative && Object.keys(this._listeners).length === 0) { this._stopListeningForNativeValueUpdates(); } } /** * Remove all registered listeners. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#removealllisteners */ removeAllListeners(): void { this._listeners = {}; if (this.__isNative) { this._stopListeningForNativeValueUpdates(); } } _startListeningToNativeValueUpdates() { if (this.__nativeAnimatedValueListener) { return; } NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag()); this.__nativeAnimatedValueListener = NativeAnimatedHelper.nativeEventEmitter.addListener( 'onAnimatedValueUpdate', data => { if (data.tag !== this.__getNativeTag()) { return; } this._updateValue(data.value, false /* flush */); }, ); } _stopListeningForNativeValueUpdates() { if (!this.__nativeAnimatedValueListener) { return; } this.__nativeAnimatedValueListener.remove(); this.__nativeAnimatedValueListener = null; NativeAnimatedAPI.stopListeningToAnimatedNodeValue(this.__getNativeTag()); } /** * Stops any running animation or tracking. `callback` is invoked with the * final value after stopping the animation, which is useful for updating * state to match the animation position with layout. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#stopanimation */ stopAnimation(callback?: ?(value: number) => void): void { this.stopTracking(); this._animation && this._animation.stop(); this._animation = null; callback && callback(this.__getValue()); } /** * Stops any animation and resets the value to its original. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#resetanimation */ resetAnimation(callback?: ?(value: number) => void): void { this.stopAnimation(callback); this._value = this._startingValue; } /** * Interpolates the value before updating the property, e.g. mapping 0-1 to * 0-10. */ interpolate(config: InterpolationConfigType): AnimatedInterpolation { return new AnimatedInterpolation(this, config); } /** * Typically only used internally, but could be used by a custom Animation * class. * * See http://facebook.github.io/react-native/docs/animatedvalue.html#animate */ animate(animation: Animation, callback: ?EndCallback): void { let handle = null; if (animation.__isInteraction) { handle = InteractionManager.createInteractionHandle(); } const previousAnimation = this._animation; this._animation && this._animation.stop(); this._animation = animation; animation.start( this._value, value => { // Natively driven animations will never call into that callback, therefore we can always // pass flush = true to allow the updated value to propagate to native with setNativeProps this._updateValue(value, true /* flush */); }, result => { this._animation = null; if (handle !== null) { InteractionManager.clearInteractionHandle(handle); } callback && callback(result); }, previousAnimation, this, ); } /** * Typically only used internally. */ stopTracking(): void { this._tracking && this._tracking.__detach(); this._tracking = null; } /** * Typically only used internally. */ track(tracking: AnimatedNode): void { this.stopTracking(); this._tracking = tracking; } _updateValue(value: number, flush: boolean): void { this._value = value; if (flush) { _flush(this); } for (const key in this._listeners) { this._listeners[key]({value: this.__getValue()}); } } __getNativeConfig(): Object { return { type: 'value', value: this._value, offset: this._offset, }; } } module.exports = AnimatedValue; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedValueXY.js ================================================ /** * 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 AnimatedValueXY * @flow * @format */ 'use strict'; const AnimatedValue = require('./AnimatedValue'); const AnimatedWithChildren = require('./AnimatedWithChildren'); const invariant = require('fbjs/lib/invariant'); type ValueXYListenerCallback = (value: {x: number, y: number}) => void; let _uniqueId = 1; /** * 2D Value for driving 2D animations, such as pan gestures. Almost identical * API to normal `Animated.Value`, but multiplexed. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html */ class AnimatedValueXY extends AnimatedWithChildren { x: AnimatedValue; y: AnimatedValue; _listeners: {[key: string]: {x: string, y: string}}; constructor( valueIn?: ?{x: number | AnimatedValue, y: number | AnimatedValue}, ) { super(); const value: any = valueIn || {x: 0, y: 0}; // @flowfixme: shouldn't need `: any` if (typeof value.x === 'number' && typeof value.y === 'number') { this.x = new AnimatedValue(value.x); this.y = new AnimatedValue(value.y); } else { invariant( value.x instanceof AnimatedValue && value.y instanceof AnimatedValue, 'AnimatedValueXY must be initalized with an object of numbers or ' + 'AnimatedValues.', ); this.x = value.x; this.y = value.y; } this._listeners = {}; } /** * Directly set the value. This will stop any animations running on the value * and update all the bound properties. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#setvalue */ setValue(value: {x: number, y: number}) { this.x.setValue(value.x); this.y.setValue(value.y); } /** * Sets an offset that is applied on top of whatever value is set, whether * via `setValue`, an animation, or `Animated.event`. Useful for compensating * things like the start of a pan gesture. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#setoffset */ setOffset(offset: {x: number, y: number}) { this.x.setOffset(offset.x); this.y.setOffset(offset.y); } /** * Merges the offset value into the base value and resets the offset to zero. * The final output of the value is unchanged. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#flattenoffset */ flattenOffset(): void { this.x.flattenOffset(); this.y.flattenOffset(); } /** * Sets the offset value to the base value, and resets the base value to * zero. The final output of the value is unchanged. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#extractoffset */ extractOffset(): void { this.x.extractOffset(); this.y.extractOffset(); } __getValue(): {x: number, y: number} { return { x: this.x.__getValue(), y: this.y.__getValue(), }; } /** * Stops any animation and resets the value to its original. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#resetanimation */ resetAnimation(callback?: (value: {x: number, y: number}) => void): void { this.x.resetAnimation(); this.y.resetAnimation(); callback && callback(this.__getValue()); } /** * Stops any running animation or tracking. `callback` is invoked with the * final value after stopping the animation, which is useful for updating * state to match the animation position with layout. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#stopanimation */ stopAnimation(callback?: (value: {x: number, y: number}) => void): void { this.x.stopAnimation(); this.y.stopAnimation(); callback && callback(this.__getValue()); } /** * Adds an asynchronous listener to the value so you can observe updates from * animations. This is useful because there is no way to synchronously read * the value because it might be driven natively. * * Returns a string that serves as an identifier for the listener. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#addlistener */ addListener(callback: ValueXYListenerCallback): string { const id = String(_uniqueId++); const jointCallback = ({value: number}) => { callback(this.__getValue()); }; this._listeners[id] = { x: this.x.addListener(jointCallback), y: this.y.addListener(jointCallback), }; return id; } /** * Unregister a listener. The `id` param shall match the identifier * previously returned by `addListener()`. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#removelistener */ removeListener(id: string): void { this.x.removeListener(this._listeners[id].x); this.y.removeListener(this._listeners[id].y); delete this._listeners[id]; } /** * Remove all registered listeners. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#removealllisteners */ removeAllListeners(): void { this.x.removeAllListeners(); this.y.removeAllListeners(); this._listeners = {}; } /** * Converts `{x, y}` into `{left, top}` for use in style. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#getlayout */ getLayout(): {[key: string]: AnimatedValue} { return { left: this.x, top: this.y, }; } /** * Converts `{x, y}` into a useable translation transform. * * See http://facebook.github.io/react-native/docs/animatedvaluexy.html#gettranslatetransform */ getTranslateTransform(): Array<{[key: string]: AnimatedValue}> { return [{translateX: this.x}, {translateY: this.y}]; } } module.exports = AnimatedValueXY; ================================================ FILE: Libraries/Animated/src/nodes/AnimatedWithChildren.js ================================================ /** * 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 AnimatedWithChildren * @flow * @format */ 'use strict'; const AnimatedNode = require('./AnimatedNode'); const NativeAnimatedHelper = require('../NativeAnimatedHelper'); class AnimatedWithChildren extends AnimatedNode { _children: Array; constructor() { super(); this._children = []; } __makeNative() { if (!this.__isNative) { this.__isNative = true; for (const child of this._children) { child.__makeNative(); NativeAnimatedHelper.API.connectAnimatedNodes( this.__getNativeTag(), child.__getNativeTag(), ); } } } __addChild(child: AnimatedNode): void { if (this._children.length === 0) { this.__attach(); } this._children.push(child); if (this.__isNative) { // Only accept "native" animated nodes as children child.__makeNative(); NativeAnimatedHelper.API.connectAnimatedNodes( this.__getNativeTag(), child.__getNativeTag(), ); } } __removeChild(child: AnimatedNode): void { const index = this._children.indexOf(child); if (index === -1) { console.warn("Trying to remove a child that doesn't exist"); return; } if (this.__isNative && child.__isNative) { NativeAnimatedHelper.API.disconnectAnimatedNodes( this.__getNativeTag(), child.__getNativeTag(), ); } this._children.splice(index, 1); if (this._children.length === 0) { this.__detach(); } } __getChildren(): Array { return this._children; } } module.exports = AnimatedWithChildren; ================================================ FILE: Libraries/Animated/src/polyfills/InteractionManager.js ================================================ /** * 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'; module.exports = { createInteractionHandle: function() {}, clearInteractionHandle: function() {} }; ================================================ FILE: Libraries/Animated/src/polyfills/Set.js ================================================ /** * 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'; function SetPolyfill() { this._cache = []; } SetPolyfill.prototype.add = function(e) { if (this._cache.indexOf(e) === -1) { this._cache.push(e); } }; SetPolyfill.prototype.forEach = function(cb) { this._cache.forEach(cb); }; module.exports = SetPolyfill; ================================================ FILE: Libraries/Animated/src/polyfills/flattenStyle.js ================================================ /** * 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'; module.exports = function(style) { return style; }; ================================================ FILE: Libraries/AppState/AppState.js ================================================ /** * 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 AppState * @flow */ 'use strict'; const MissingNativeEventEmitterShim = require('MissingNativeEventEmitterShim'); const NativeEventEmitter = require('NativeEventEmitter'); const NativeModules = require('NativeModules'); const RCTAppState = NativeModules.AppState; const logError = require('logError'); const invariant = require('fbjs/lib/invariant'); /** * `AppState` can tell you if the app is in the foreground or background, * and notify you when the state changes. * * See http://facebook.github.io/react-native/docs/appstate.html */ class AppState extends NativeEventEmitter { _eventHandlers: Object; currentState: ?string; isAvailable: boolean = true; constructor() { super(RCTAppState); this.isAvailable = true; this._eventHandlers = { change: new Map(), memoryWarning: new Map(), }; // TODO: Remove the 'active' fallback after `initialAppState` is exported by // the Android implementation. this.currentState = RCTAppState.initialAppState || 'active'; let eventUpdated = false; // TODO: this is a terrible solution - in order to ensure `currentState` // prop is up to date, we have to register an observer that updates it // whenever the state changes, even if nobody cares. We should just // deprecate the `currentState` property and get rid of this. this.addListener( 'appStateDidChange', (appStateData) => { eventUpdated = true; this.currentState = appStateData.app_state; } ); // TODO: see above - this request just populates the value of `currentState` // when the module is first initialized. Would be better to get rid of the // prop and expose `getCurrentAppState` method directly. RCTAppState.getCurrentAppState( (appStateData) => { if (!eventUpdated) { this.currentState = appStateData.app_state; } }, logError ); } // TODO: now that AppState is a subclass of NativeEventEmitter, we could // deprecate `addEventListener` and `removeEventListener` and just use // addListener` and `listener.remove()` directly. That will be a breaking // change though, as both the method and event names are different // (addListener events are currently required to be globally unique). /** * Add a handler to AppState changes by listening to the `change` event type * and providing the handler. * * See http://facebook.github.io/react-native/docs/appstate.html#addeventlistener */ addEventListener( type: string, handler: Function ) { invariant( ['change', 'memoryWarning'].indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type ); if (type === 'change') { this._eventHandlers[type].set(handler, this.addListener( 'appStateDidChange', (appStateData) => { handler(appStateData.app_state); } )); } else if (type === 'memoryWarning') { this._eventHandlers[type].set(handler, this.addListener( 'memoryWarning', handler )); } } /** * Remove a handler by passing the `change` event type and the handler. * * See http://facebook.github.io/react-native/docs/appstate.html#removeeventlistener */ removeEventListener( type: string, handler: Function ) { invariant( ['change', 'memoryWarning'].indexOf(type) !== -1, 'Trying to remove listener for unknown event: "%s"', type ); if (!this._eventHandlers[type].has(handler)) { return; } this._eventHandlers[type].get(handler).remove(); this._eventHandlers[type].delete(handler); } } if (__DEV__ && !RCTAppState) { class MissingNativeAppStateShim extends MissingNativeEventEmitterShim { constructor() { super('RCTAppState', 'AppState'); } get currentState(): ?string { this.throwMissingNativeModule(); } addEventListener(...args: Array) { this.throwMissingNativeModule(); } removeEventListener(...args: Array) { this.throwMissingNativeModule(); } } // This module depends on the native `RCTAppState` module. If you don't // include it, `AppState.isAvailable` will return `false`, and any method // calls will throw. We reassign the class variable to keep the autodoc // generator happy. AppState = new MissingNativeAppStateShim(); } else { AppState = new AppState(); } module.exports = AppState; ================================================ FILE: Libraries/BatchedBridge/BatchedBridge.js ================================================ /** * 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 BatchedBridge * @flow */ 'use strict'; const MessageQueue = require('MessageQueue'); // MessageQueue can install a global handler to catch all exceptions where JS users can register their own behavior // This handler makes all exceptions to be handled inside MessageQueue rather than by the VM at its origin // This makes stacktraces to be placed at MessageQueue rather than at where they were launched // The parameter __fbUninstallRNGlobalErrorHandler is passed to MessageQueue to prevent the handler from being installed // // __fbUninstallRNGlobalErrorHandler is conditionally set by the Inspector while the VM is paused for intialization // If the Inspector isn't present it defaults to undefined and the global error handler is installed // The Inspector can still call MessageQueue#uninstallGlobalErrorHandler to uninstalled on attach const BatchedBridge = new MessageQueue( // $FlowFixMe typeof __fbUninstallRNGlobalErrorHandler !== 'undefined' && __fbUninstallRNGlobalErrorHandler === true, // eslint-disable-line no-undef ); // Wire up the batched bridge on the global object so that we can call into it. // Ideally, this would be the inverse relationship. I.e. the native environment // provides this global directly with its script embedded. Then this module // would export it. A possible fix would be to trim the dependencies in // MessageQueue to its minimal features and embed that in the native runtime. Object.defineProperty(global, '__fbBatchedBridge', { configurable: true, value: BatchedBridge, }); module.exports = BatchedBridge; ================================================ FILE: Libraries/BatchedBridge/MessageQueue.js ================================================ /** * 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 MessageQueue * @flow * @format */ 'use strict'; const ErrorUtils = require('ErrorUtils'); const Systrace = require('Systrace'); const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev'); const invariant = require('fbjs/lib/invariant'); const stringifySafe = require('stringifySafe'); export type SpyData = { type: number, module: ?string, method: string | number, args: any[], }; const TO_JS = 0; const TO_NATIVE = 1; const MODULE_IDS = 0; const METHOD_IDS = 1; const PARAMS = 2; const MIN_TIME_BETWEEN_FLUSHES_MS = 5; // eslint-disable-next-line no-bitwise const TRACE_TAG_REACT_APPS = 1 << 17; const DEBUG_INFO_LIMIT = 32; // Work around an initialization order issue let JSTimers = null; class MessageQueue { _lazyCallableModules: {[key: string]: (void) => Object}; _queue: [number[], number[], any[], number]; _successCallbacks: (?Function)[]; _failureCallbacks: (?Function)[]; _callID: number; _inCall: number; _lastFlush: number; _eventLoopStartTime: number; _debugInfo: {[number]: [number, number]}; _remoteModuleTable: {[number]: string}; _remoteMethodTable: {[number]: string[]}; __spy: ?(data: SpyData) => void; __guard: (() => void) => void; constructor(shouldUninstallGlobalErrorHandler: boolean = false) { this._lazyCallableModules = {}; this._queue = [[], [], [], 0]; this._successCallbacks = []; this._failureCallbacks = []; this._callID = 0; this._lastFlush = 0; this._eventLoopStartTime = new Date().getTime(); if (shouldUninstallGlobalErrorHandler) { this.uninstallGlobalErrorHandler(); } else { this.installGlobalErrorHandler(); } if (__DEV__) { this._debugInfo = {}; this._remoteModuleTable = {}; this._remoteMethodTable = {}; } (this: any).callFunctionReturnFlushedQueue = this.callFunctionReturnFlushedQueue.bind( this, ); (this: any).callFunctionReturnResultAndFlushedQueue = this.callFunctionReturnResultAndFlushedQueue.bind( this, ); (this: any).flushedQueue = this.flushedQueue.bind(this); (this: any).invokeCallbackAndReturnFlushedQueue = this.invokeCallbackAndReturnFlushedQueue.bind( this, ); } /** * Public APIs */ static spy(spyOrToggle: boolean | ((data: SpyData) => void)) { if (spyOrToggle === true) { MessageQueue.prototype.__spy = info => { console.log( `${info.type === TO_JS ? 'N->JS' : 'JS->N'} : ` + `${info.module ? info.module + '.' : ''}${info.method}` + `(${JSON.stringify(info.args)})`, ); }; } else if (spyOrToggle === false) { MessageQueue.prototype.__spy = null; } else { MessageQueue.prototype.__spy = spyOrToggle; } } callFunctionReturnFlushedQueue(module: string, method: string, args: any[]) { this.__guard(() => { this.__callFunction(module, method, args); }); return this.flushedQueue(); } callFunctionReturnResultAndFlushedQueue( module: string, method: string, args: any[], ) { let result; this.__guard(() => { result = this.__callFunction(module, method, args); }); return [result, this.flushedQueue()]; } invokeCallbackAndReturnFlushedQueue(cbID: number, args: any[]) { this.__guard(() => { this.__invokeCallback(cbID, args); }); return this.flushedQueue(); } flushedQueue() { this.__guard(() => { this.__callImmediates(); }); const queue = this._queue; this._queue = [[], [], [], this._callID]; return queue[0].length ? queue : null; } getEventLoopRunningTime() { return new Date().getTime() - this._eventLoopStartTime; } registerCallableModule(name: string, module: Object) { this._lazyCallableModules[name] = () => module; } registerLazyCallableModule(name: string, factory: void => Object) { let module: Object; let getValue: ?(void) => Object = factory; this._lazyCallableModules[name] = () => { if (getValue) { module = getValue(); getValue = null; } return module; }; } getCallableModule(name: string) { const getValue = this._lazyCallableModules[name]; return getValue ? getValue() : null; } enqueueNativeCall( moduleID: number, methodID: number, params: any[], onFail: ?Function, onSucc: ?Function, ) { if (onFail || onSucc) { if (__DEV__) { this._debugInfo[this._callID] = [moduleID, methodID]; if (this._callID > DEBUG_INFO_LIMIT) { delete this._debugInfo[this._callID - DEBUG_INFO_LIMIT]; } } // Encode callIDs into pairs of callback identifiers by shifting left and using the rightmost bit // to indicate fail (0) or success (1) // eslint-disable-next-line no-bitwise onFail && params.push(this._callID << 1); // eslint-disable-next-line no-bitwise onSucc && params.push((this._callID << 1) | 1); this._successCallbacks[this._callID] = onSucc; this._failureCallbacks[this._callID] = onFail; } if (__DEV__) { global.nativeTraceBeginAsyncFlow && global.nativeTraceBeginAsyncFlow( TRACE_TAG_REACT_APPS, 'native', this._callID, ); } this._callID++; this._queue[MODULE_IDS].push(moduleID); this._queue[METHOD_IDS].push(methodID); if (__DEV__) { // Validate that parameters passed over the bridge are // folly-convertible. As a special case, if a prop value is a // function it is permitted here, and special-cased in the // conversion. const seen = []; const path = []; const validate = (val, key) => { if (val == null) { return true; } if (key != null) { path.push(key); } let valid = true; let error = ''; switch (typeof val) { case 'boolean': case 'string': break; // No error. case 'number': if (!isFinite(val)) { error = 'Cannot serialize an infinite number'; } break; case 'bigint': case 'function': case 'symbol': error = 'Cannot serialize a ' + typeof val; break; case 'object': const seenIndex = seen.indexOf(val); if (seenIndex >= 0) { error = 'Cannot serialize the same object twice'; break; } seen.push(val); valid = Array.isArray(val) ? val.every(validate) : Object.keys(val).every(k => validate(val[k], k)); break; } if (error) { console.warn( error + '\nFound at this path: ', path.slice(), '\nin this object: ', params, ); } if (key != null) { path.pop(); } return valid && !error; }; if (!validate(params)) { console.warn( 'Native method call has arguments which cannot be serialized: %O', params, ); } // The params object should not be mutated after being queued deepFreezeAndThrowOnMutationInDev((params: any)); } this._queue[PARAMS].push(params); const now = new Date().getTime(); if ( global.nativeFlushQueueImmediate && (now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS || this._inCall === 0) ) { var queue = this._queue; this._queue = [[], [], [], this._callID]; this._lastFlush = now; global.nativeFlushQueueImmediate(queue); } Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length); if (__DEV__ && this.__spy && isFinite(moduleID)) { this.__spy({ type: TO_NATIVE, module: this._remoteModuleTable[moduleID], method: this._remoteMethodTable[moduleID][methodID], args: params, }); } else if (this.__spy) { this.__spy({ type: TO_NATIVE, module: moduleID + '', method: methodID, args: params, }); } } createDebugLookup(moduleID: number, name: string, methods: string[]) { if (__DEV__) { this._remoteModuleTable[moduleID] = name; this._remoteMethodTable[moduleID] = methods; } } uninstallGlobalErrorHandler() { this.__guard = this.__guardUnsafe; } installGlobalErrorHandler() { this.__guard = this.__guardSafe; } /** * Private methods */ // Lets exceptions propagate to be handled by the VM at the origin __guardUnsafe(fn: () => void) { this._inCall++; fn(); this._inCall--; } __guardSafe(fn: () => void) { this._inCall++; try { fn(); } catch (error) { ErrorUtils.reportFatalError(error); } finally { this._inCall--; } } __callImmediates() { Systrace.beginEvent('JSTimers.callImmediates()'); if (!JSTimers) { JSTimers = require('JSTimers'); } JSTimers.callImmediates(); Systrace.endEvent(); } __callFunction(module: string, method: string, args: any[]): any { this._lastFlush = new Date().getTime(); this._eventLoopStartTime = this._lastFlush; Systrace.beginEvent(`${module}.${method}()`); if (this.__spy) { this.__spy({type: TO_JS, module, method, args}); } const moduleMethods = this.getCallableModule(module); invariant( !!moduleMethods, 'Module %s is not a registered callable module (calling %s)', module, method, ); invariant( !!moduleMethods[method], 'Method %s does not exist on module %s', method, module, ); const result = moduleMethods[method].apply(moduleMethods, args); Systrace.endEvent(); return result; } __invokeCallback(cbID: number, args: any[]) { this._lastFlush = new Date().getTime(); this._eventLoopStartTime = this._lastFlush; // The rightmost bit of cbID indicates fail (0) or success (1), the other bits are the callID shifted left. // eslint-disable-next-line no-bitwise const callID = cbID >>> 1; // eslint-disable-next-line no-bitwise const isSuccess = cbID & 1; const callback = isSuccess ? this._successCallbacks[callID] : this._failureCallbacks[callID]; if (__DEV__) { const debug = this._debugInfo[callID]; const module = debug && this._remoteModuleTable[debug[0]]; const method = debug && this._remoteMethodTable[debug[0]][debug[1]]; if (!callback) { let errorMessage = `Callback with id ${cbID}: ${module}.${method}() not found`; if (method) { errorMessage = `The callback ${method}() exists in module ${module}, ` + 'but only one callback may be registered to a function in a native module.'; } invariant(callback, errorMessage); } const profileName = debug ? '' : cbID; if (callback && this.__spy) { this.__spy({type: TO_JS, module: null, method: profileName, args}); } Systrace.beginEvent( `MessageQueue.invokeCallback(${profileName}, ${stringifySafe(args)})`, ); } if (!callback) { return; } this._successCallbacks[callID] = this._failureCallbacks[callID] = null; callback(...args); if (__DEV__) { Systrace.endEvent(); } } } module.exports = MessageQueue; ================================================ FILE: Libraries/BatchedBridge/NativeModules.js ================================================ /** * 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 NativeModules * @flow */ 'use strict'; const BatchedBridge = require('BatchedBridge'); const ExceptionsManager = require('ExceptionsManager'); const invariant = require('fbjs/lib/invariant'); import type {ExtendedError} from 'parseErrorStack'; type ModuleConfig = [ string, /* name */ ?Object, /* constants */ Array, /* functions */ Array, /* promise method IDs */ Array, /* sync method IDs */ ]; export type MethodType = 'async' | 'promise' | 'sync'; function genModule(config: ?ModuleConfig, moduleID: number): ?{name: string, module?: Object} { if (!config) { return null; } const [moduleName, constants, methods, promiseMethods, syncMethods] = config; invariant(!moduleName.startsWith('RCT') && !moduleName.startsWith('RK'), 'Module name prefixes should\'ve been stripped by the native side ' + 'but wasn\'t for ' + moduleName); if (!constants && !methods) { // Module contents will be filled in lazily later return { name: moduleName }; } const module = {}; methods && methods.forEach((methodName, methodID) => { const isPromise = promiseMethods && arrayContains(promiseMethods, methodID); const isSync = syncMethods && arrayContains(syncMethods, methodID); invariant(!isPromise || !isSync, 'Cannot have a method that is both async and a sync hook'); const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async'; module[methodName] = genMethod(moduleID, methodID, methodType); }); Object.assign(module, constants); if (__DEV__) { BatchedBridge.createDebugLookup(moduleID, moduleName, methods); } return { name: moduleName, module }; } // export this method as a global so we can call it from native global.__fbGenNativeModule = genModule; function loadModule(name: string, moduleID: number): ?Object { invariant(global.nativeRequireModuleConfig, 'Can\'t lazily create module without nativeRequireModuleConfig'); const config = global.nativeRequireModuleConfig(name); const info = genModule(config, moduleID); return info && info.module; } function genMethod(moduleID: number, methodID: number, type: MethodType) { let fn = null; if (type === 'promise') { fn = function(...args: Array) { return new Promise((resolve, reject) => { BatchedBridge.enqueueNativeCall(moduleID, methodID, args, (data) => resolve(data), (errorData) => reject(createErrorFromErrorData(errorData))); }); }; } else if (type === 'sync') { fn = function(...args: Array) { if (__DEV__) { invariant(global.nativeCallSyncHook, 'Calling synchronous methods on native ' + 'modules is not supported in Chrome.\n\n Consider providing alternative ' + 'methods to expose this method in debug mode, e.g. by exposing constants ' + 'ahead-of-time.'); } return global.nativeCallSyncHook(moduleID, methodID, args); }; } else { fn = function(...args: Array) { const lastArg = args.length > 0 ? args[args.length - 1] : null; const secondLastArg = args.length > 1 ? args[args.length - 2] : null; const hasSuccessCallback = typeof lastArg === 'function'; const hasErrorCallback = typeof secondLastArg === 'function'; hasErrorCallback && invariant( hasSuccessCallback, 'Cannot have a non-function arg after a function arg.' ); const onSuccess = hasSuccessCallback ? lastArg : null; const onFail = hasErrorCallback ? secondLastArg : null; const callbackCount = hasSuccessCallback + hasErrorCallback; args = args.slice(0, args.length - callbackCount); try { BatchedBridge.enqueueNativeCall(moduleID, methodID, args, onFail, onSuccess); } catch(e) { ExceptionsManager.handleException(e, true); } }; } fn.type = type; return fn; } function arrayContains(array: Array, value: T): boolean { return array.indexOf(value) !== -1; } function createErrorFromErrorData(errorData: {message: string}): ExtendedError { const { message, ...extraErrorInfo } = errorData || {}; const error : ExtendedError = new Error(message); error.framesToPop = 1; return Object.assign(error, extraErrorInfo); } let NativeModules : {[moduleName: string]: Object} = {}; if (global.nativeModuleProxy) { NativeModules = global.nativeModuleProxy; } else { const bridgeConfig = global.__fbBatchedBridgeConfig; invariant(bridgeConfig, '__fbBatchedBridgeConfig is not set, cannot invoke native modules'); const defineLazyObjectProperty = require('defineLazyObjectProperty'); (bridgeConfig.remoteModuleConfig || []).forEach((config: ModuleConfig, moduleID: number) => { // Initially this config will only contain the module name when running in JSC. The actual // configuration of the module will be lazily loaded. const info = genModule(config, moduleID); if (!info) { return; } if (info.module) { NativeModules[info.name] = info.module; } // If there's no module config, define a lazy getter else { defineLazyObjectProperty(NativeModules, info.name, { get: () => loadModule(info.name, moduleID) }); } }); } module.exports = NativeModules; ================================================ FILE: Libraries/BatchedBridge/__mocks__/MessageQueueTestConfig.js ================================================ /** * Copyright (c) 2013-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. * * These don't actually exist anywhere in the code. */ 'use strict'; var remoteModulesConfig = [ ['RemoteModule1',null,['remoteMethod','promiseMethod'],[]], ['RemoteModule2',null,['remoteMethod','promiseMethod'],[]], ]; var MessageQueueTestConfig = { remoteModuleConfig: remoteModulesConfig, }; module.exports = MessageQueueTestConfig; ================================================ FILE: Libraries/BatchedBridge/__mocks__/MessageQueueTestModule.js ================================================ /** * Copyright (c) 2013-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'; /** * Dummy module that only exists for the sake of proving that the message queue * correctly dispatches to commonJS modules. The `testHook` is overriden by test * cases. */ var MessageQueueTestModule = { testHook1: function() { }, testHook2: function() { } }; module.exports = MessageQueueTestModule; ================================================ FILE: Libraries/BatchedBridge/__tests__/MessageQueue-test.js ================================================ /** * Copyright (c) 2013-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. * * @emails oncall+react_native * @format */ 'use strict'; let MessageQueue; let MessageQueueTestModule; let queue; const MODULE_IDS = 0; const METHOD_IDS = 1; const PARAMS = 2; const assertQueue = (flushedQueue, index, moduleID, methodID, params) => { expect(flushedQueue[MODULE_IDS][index]).toEqual(moduleID); expect(flushedQueue[METHOD_IDS][index]).toEqual(methodID); expect(flushedQueue[PARAMS][index]).toEqual(params); }; // Important things to test: // // [x] Local modules can be invoked through the queue. // // [ ] Local modules that throw exceptions are gracefully caught. In that case // local callbacks stored by IDs are cleaned up. describe('MessageQueue', function() { beforeEach(function() { jest.resetModules(); MessageQueue = require('MessageQueue'); MessageQueueTestModule = require('MessageQueueTestModule'); queue = new MessageQueue(); queue.registerCallableModule( 'MessageQueueTestModule', MessageQueueTestModule, ); queue.createDebugLookup(0, 'MessageQueueTestModule', [ 'testHook1', 'testHook2', ]); }); it('should enqueue native calls', () => { queue.enqueueNativeCall(0, 1, [2]); const flushedQueue = queue.flushedQueue(); assertQueue(flushedQueue, 0, 0, 1, [2]); }); it('should call a local function with the function name', () => { MessageQueueTestModule.testHook2 = jest.fn(); expect(MessageQueueTestModule.testHook2.mock.calls.length).toEqual(0); queue.__callFunction('MessageQueueTestModule', 'testHook2', [2]); expect(MessageQueueTestModule.testHook2.mock.calls.length).toEqual(1); }); it('should store callbacks', () => { queue.enqueueNativeCall(0, 1, ['foo'], null, null); const flushedQueue = queue.flushedQueue(); assertQueue(flushedQueue, 0, 0, 1, ['foo']); }); it('should call the stored callback', () => { let done = false; queue.enqueueNativeCall( 0, 1, [], () => {}, () => { done = true; }, ); queue.__invokeCallback(1, []); expect(done).toEqual(true); }); it('should throw when calling the same callback twice', () => { queue.enqueueNativeCall(0, 1, [], () => {}, () => {}); queue.__invokeCallback(1, []); expect(() => queue.__invokeCallback(1, [])).toThrow(); }); it('should throw when calling both success and failure callback', () => { queue.enqueueNativeCall(0, 1, [], () => {}, () => {}); queue.__invokeCallback(1, []); expect(() => queue.__invokeCallback(0, [])).toThrow(); }); it('should throw when calling with unknown module', () => { const unknownModule = 'UnknownModule', unknownMethod = 'UnknownMethod'; expect(() => queue.__callFunction(unknownModule, unknownMethod)).toThrow( `Module ${unknownModule} is not a registered callable module (calling ${unknownMethod})`, ); }); it('should return lazily registered module', () => { const dummyModule = {}, name = 'modulesName'; queue.registerLazyCallableModule(name, () => dummyModule); expect(queue.getCallableModule(name)).toEqual(dummyModule); }); it('should not initialize lazily registered module before it was used for the first time', () => { const dummyModule = {}, name = 'modulesName'; const factory = jest.fn(() => dummyModule); queue.registerLazyCallableModule(name, factory); expect(factory).not.toHaveBeenCalled(); }); it('should initialize lazily registered module only once', () => { const dummyModule = {}, name = 'modulesName'; const factory = jest.fn(() => dummyModule); queue.registerLazyCallableModule(name, factory); queue.getCallableModule(name); queue.getCallableModule(name); expect(factory).toHaveBeenCalledTimes(1); }); it('should catch all exceptions if the global error handler is installed', () => { const errorMessage = 'intentional error'; const errorModule = { explode: function() { throw new Error(errorMessage); }, }; const name = 'errorModuleName'; const factory = jest.fn(() => errorModule); queue.__guardSafe = jest.fn(() => {}); queue.__guardUnsafe = jest.fn(() => {}); queue.installGlobalErrorHandler(); queue.registerLazyCallableModule(name, factory); queue.callFunctionReturnFlushedQueue(name, 'explode', []); expect(queue.__guardUnsafe).toHaveBeenCalledTimes(0); expect(queue.__guardSafe).toHaveBeenCalledTimes(2); }); it('should propagate exceptions if the global error handler is uninstalled', () => { queue.uninstallGlobalErrorHandler(); const errorMessage = 'intentional error'; const errorModule = { explode: function() { throw new Error(errorMessage); }, }; const name = 'errorModuleName'; const factory = jest.fn(() => errorModule); queue.__guardUnsafe = jest.fn(() => {}); queue.__guardSafe = jest.fn(() => {}); queue.registerLazyCallableModule(name, factory); queue.uninstallGlobalErrorHandler(); queue.callFunctionReturnFlushedQueue(name, 'explode'); expect(queue.__guardUnsafe).toHaveBeenCalledTimes(2); expect(queue.__guardSafe).toHaveBeenCalledTimes(0); }); it('should catch all exceptions if the global error handler is re-installed', () => { const errorMessage = 'intentional error'; const errorModule = { explode: function() { throw new Error(errorMessage); }, }; const name = 'errorModuleName'; const factory = jest.fn(() => errorModule); queue.__guardUnsafe = jest.fn(() => {}); queue.__guardSafe = jest.fn(() => {}); queue.registerLazyCallableModule(name, factory); queue.uninstallGlobalErrorHandler(); queue.installGlobalErrorHandler(); queue.callFunctionReturnFlushedQueue(name, 'explode'); expect(queue.__guardUnsafe).toHaveBeenCalledTimes(0); expect(queue.__guardSafe).toHaveBeenCalledTimes(2); }); }); ================================================ FILE: Libraries/BatchedBridge/__tests__/NativeModules-test.js ================================================ /** * Copyright (c) 2013-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. * * @emails oncall+react_native */ 'use strict'; jest .enableAutomock() .unmock('BatchedBridge') .unmock('defineLazyObjectProperty') .unmock('MessageQueue') .unmock('NativeModules'); let BatchedBridge; let NativeModules; const MODULE_IDS = 0; const METHOD_IDS = 1; const PARAMS = 2; const CALL_ID = 3; const assertQueue = (flushedQueue, index, moduleID, methodID, params) => { expect(flushedQueue[MODULE_IDS][index]).toEqual(moduleID); expect(flushedQueue[METHOD_IDS][index]).toEqual(methodID); expect(flushedQueue[PARAMS][index]).toEqual(params); }; // Important things to test: // // [x] Calling remote method actually queues it up on the BatchedBridge // // [x] Both error and success callbacks are invoked. // // [x] When simulating an error callback from remote method, both error and // success callbacks are cleaned up. // // [ ] Remote invocation throws if not supplying an error callback. describe('MessageQueue', function() { beforeEach(function() { jest.resetModules(); global.__fbBatchedBridgeConfig = require('MessageQueueTestConfig'); BatchedBridge = require('BatchedBridge'); NativeModules = require('NativeModules'); }); it('should generate native modules', () => { NativeModules.RemoteModule1.remoteMethod('foo'); const flushedQueue = BatchedBridge.flushedQueue(); assertQueue(flushedQueue, 0, 0, 0, ['foo']); }); it('should make round trip and clear memory', function() { const onFail = jest.fn(); const onSucc = jest.fn(); // Perform communication NativeModules.RemoteModule1.promiseMethod('paloAlto', 'menloPark', onFail, onSucc); NativeModules.RemoteModule2.promiseMethod('mac', 'windows', onFail, onSucc); const resultingRemoteInvocations = BatchedBridge.flushedQueue(); // As always, the message queue has four fields expect(resultingRemoteInvocations.length).toBe(4); expect(resultingRemoteInvocations[MODULE_IDS].length).toBe(2); expect(resultingRemoteInvocations[METHOD_IDS].length).toBe(2); expect(resultingRemoteInvocations[PARAMS].length).toBe(2); expect(typeof resultingRemoteInvocations[CALL_ID]).toEqual('number'); expect(resultingRemoteInvocations[0][0]).toBe(0); // `RemoteModule1` expect(resultingRemoteInvocations[1][0]).toBe(1); // `promiseMethod` expect([ // the arguments resultingRemoteInvocations[2][0][0], resultingRemoteInvocations[2][0][1] ]).toEqual(['paloAlto', 'menloPark']); // Callbacks ids are tacked onto the end of the remote arguments. const firstFailCBID = resultingRemoteInvocations[2][0][2]; const firstSuccCBID = resultingRemoteInvocations[2][0][3]; expect(resultingRemoteInvocations[0][1]).toBe(1); // `RemoteModule2` expect(resultingRemoteInvocations[1][1]).toBe(1); // `promiseMethod` expect([ // the arguments resultingRemoteInvocations[2][1][0], resultingRemoteInvocations[2][1][1] ]).toEqual(['mac', 'windows']); const secondFailCBID = resultingRemoteInvocations[2][1][2]; const secondSuccCBID = resultingRemoteInvocations[2][1][3]; // Handle the first remote invocation by signaling failure. BatchedBridge.__invokeCallback(firstFailCBID, ['firstFailure']); // The failure callback was already invoked, the success is no longer valid expect(function() { BatchedBridge.__invokeCallback(firstSuccCBID, ['firstSucc']); }).toThrow(); expect(onFail.mock.calls.length).toBe(1); expect(onSucc.mock.calls.length).toBe(0); // Handle the second remote invocation by signaling success. BatchedBridge.__invokeCallback(secondSuccCBID, ['secondSucc']); // The success callback was already invoked, the fail cb is no longer valid expect(function() { BatchedBridge.__invokeCallback(secondFailCBID, ['secondFail']); }).toThrow(); expect(onFail.mock.calls.length).toBe(1); expect(onSucc.mock.calls.length).toBe(1); }); }); ================================================ FILE: Libraries/Blob/Blob.js ================================================ /** * Copyright (c) 2013-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 Blob * @flow */ 'use strict'; const invariant = require('fbjs/lib/invariant'); /* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error * found when Flow v0.54 was deployed. To see the error delete this comment and * run Flow. */ const uuid = require('uuid'); const { BlobModule } = require('NativeModules'); import type { BlobProps } from 'BlobTypes'; /** * Opaque JS representation of some binary data in native. * * The API is modeled after the W3C Blob API, with one caveat * regarding explicit deallocation. Refer to the `close()` * method for further details. * * Example usage in a React component: * * class WebSocketImage extends React.Component { * state = {blob: null}; * componentDidMount() { * let ws = this.ws = new WebSocket(...); * ws.binaryType = 'blob'; * ws.onmessage = (event) => { * if (this.state.blob) { * this.state.blob.close(); * } * this.setState({blob: event.data}); * }; * } * componentUnmount() { * if (this.state.blob) { * this.state.blob.close(); * } * this.ws.close(); * } * render() { * if (!this.state.blob) { * return ; * } * return ; * } * } * * Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob */ class Blob { /** * Size of the data contained in the Blob object, in bytes. */ size: number; /* * String indicating the MIME type of the data contained in the Blob. * If the type is unknown, this string is empty. */ type: string; /* * Unique id to identify the blob on native side (non-standard) */ blobId: string; /* * Offset to indicate part of blob, used when sliced (non-standard) */ offset: number; /** * Construct blob instance from blob data from native. * Used internally by modules like XHR, WebSocket, etc. */ static create(props: BlobProps): Blob { return Object.assign(Object.create(Blob.prototype), props); } /** * Constructor for JS consumers. * Currently we only support creating Blobs from other Blobs. * Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob */ constructor(parts: Array, options: any) { const blobId = uuid(); let size = 0; parts.forEach((part) => { invariant(part instanceof Blob, 'Can currently only create a Blob from other Blobs'); size += part.size; }); BlobModule.createFromParts(parts, blobId); return Blob.create({ blobId, offset: 0, size, }); } /* * This method is used to create a new Blob object containing * the data in the specified range of bytes of the source Blob. * Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice */ slice(start?: number, end?: number): Blob { let offset = this.offset; let size = this.size; if (typeof start === 'number') { if (start > size) { start = size; } offset += start; size -= start; if (typeof end === 'number') { if (end < 0) { end = this.size + end; } size = end - start; } } return Blob.create({ blobId: this.blobId, offset, size, }); } /** * This method is in the standard, but not actually implemented by * any browsers at this point. It's important for how Blobs work in * React Native, however, since we cannot de-allocate resources automatically, * so consumers need to explicitly de-allocate them. * * Note that the semantics around Blobs created via `blob.slice()` * and `new Blob([blob])` are different. `blob.slice()` creates a * new *view* onto the same binary data, so calling `close()` on any * of those views is enough to deallocate the data, whereas * `new Blob([blob, ...])` actually copies the data in memory. */ close() { BlobModule.release(this.blobId); } } module.exports = Blob; ================================================ FILE: Libraries/Blob/BlobTypes.js ================================================ /** * Copyright (c) 2013-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 BlobTypes * @flow */ 'use strict'; export type BlobProps = { blobId: string, offset: number, size: number, type?: string, }; export type FileProps = BlobProps & { name: string, lastModified: number, }; ================================================ FILE: Libraries/Blob/RCTBlob.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ AD0871131E215B28007D136D /* RCTBlobManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; AD0871161E215EC9007D136D /* RCTBlobManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; AD0871181E215ED1007D136D /* RCTBlobManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; AD08711A1E2162C8007D136D /* RCTBlobManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; AD9A43C31DFC7126008DC588 /* RCTBlobManager.m in Sources */ = {isa = PBXBuildFile; fileRef = AD9A43C21DFC7126008DC588 /* RCTBlobManager.m */; }; ADD01A711E09404A00F6D226 /* RCTBlobManager.m in Sources */ = {isa = PBXBuildFile; fileRef = AD9A43C21DFC7126008DC588 /* RCTBlobManager.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 358F4ED51D1E81A9004DF814 /* Copy Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = include/RCTBlob; dstSubfolderSpec = 16; files = ( AD08711A1E2162C8007D136D /* RCTBlobManager.h in Copy Headers */, ); name = "Copy Headers"; runOnlyForDeploymentPostprocessing = 0; }; AD0871121E215B16007D136D /* Copy Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = include/RCTBlob; dstSubfolderSpec = 16; files = ( AD0871131E215B28007D136D /* RCTBlobManager.h in Copy Headers */, ); name = "Copy Headers"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 358F4ED71D1E81A9004DF814 /* libRCTBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTBlob.a; sourceTree = BUILT_PRODUCTS_DIR; }; AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBlobManager.h; sourceTree = ""; }; AD9A43C21DFC7126008DC588 /* RCTBlobManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBlobManager.m; sourceTree = ""; }; ADD01A681E09402E00F6D226 /* libRCTBlob-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTBlob-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ 358F4ECE1D1E81A9004DF814 = { isa = PBXGroup; children = ( AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */, AD9A43C21DFC7126008DC588 /* RCTBlobManager.m */, 358F4ED81D1E81A9004DF814 /* Products */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 358F4ED81D1E81A9004DF814 /* Products */ = { isa = PBXGroup; children = ( 358F4ED71D1E81A9004DF814 /* libRCTBlob.a */, ADD01A681E09402E00F6D226 /* libRCTBlob-tvOS.a */, ); name = Products; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ AD0871151E215EB7007D136D /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( AD0871161E215EC9007D136D /* RCTBlobManager.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; AD0871171E215ECC007D136D /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( AD0871181E215ED1007D136D /* RCTBlobManager.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 358F4ED61D1E81A9004DF814 /* RCTBlob */ = { isa = PBXNativeTarget; buildConfigurationList = 358F4EE01D1E81A9004DF814 /* Build configuration list for PBXNativeTarget "RCTBlob" */; buildPhases = ( AD0871151E215EB7007D136D /* Headers */, 358F4ED51D1E81A9004DF814 /* Copy Headers */, 358F4ED31D1E81A9004DF814 /* Sources */, ); buildRules = ( ); dependencies = ( ); name = RCTBlob; productName = SLKBlobs; productReference = 358F4ED71D1E81A9004DF814 /* libRCTBlob.a */; productType = "com.apple.product-type.library.static"; }; ADD01A671E09402E00F6D226 /* RCTBlob-tvOS */ = { isa = PBXNativeTarget; buildConfigurationList = ADD01A6E1E09402E00F6D226 /* Build configuration list for PBXNativeTarget "RCTBlob-tvOS" */; buildPhases = ( AD0871171E215ECC007D136D /* Headers */, AD0871121E215B16007D136D /* Copy Headers */, ADD01A641E09402E00F6D226 /* Sources */, ); buildRules = ( ); dependencies = ( ); name = "RCTBlob-tvOS"; productName = "RCTBlob-tvOS"; productReference = ADD01A681E09402E00F6D226 /* libRCTBlob-tvOS.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 358F4ECF1D1E81A9004DF814 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0730; ORGANIZATIONNAME = "Silk Labs"; TargetAttributes = { 358F4ED61D1E81A9004DF814 = { CreatedOnToolsVersion = 7.3; }; ADD01A671E09402E00F6D226 = { CreatedOnToolsVersion = 8.2; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 358F4ED21D1E81A9004DF814 /* Build configuration list for PBXProject "RCTBlob" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 358F4ECE1D1E81A9004DF814; productRefGroup = 358F4ED81D1E81A9004DF814 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 358F4ED61D1E81A9004DF814 /* RCTBlob */, ADD01A671E09402E00F6D226 /* RCTBlob-tvOS */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 358F4ED31D1E81A9004DF814 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( AD9A43C31DFC7126008DC588 /* RCTBlobManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; ADD01A641E09402E00F6D226 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( ADD01A711E09404A00F6D226 /* RCTBlobManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 358F4EDE1D1E81A9004DF814 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SKIP_INSTALL = YES; }; name = Debug; }; 358F4EDF1D1E81A9004DF814 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; VALIDATE_PRODUCT = YES; }; name = Release; }; 358F4EE11D1E81A9004DF814 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Debug; }; 358F4EE21D1E81A9004DF814 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Release; }; ADD01A6F1E09402E00F6D226 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; TVOS_DEPLOYMENT_TARGET = 10.1; }; name = Debug; }; ADD01A701E09402E00F6D226 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; TVOS_DEPLOYMENT_TARGET = 10.1; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 358F4ED21D1E81A9004DF814 /* Build configuration list for PBXProject "RCTBlob" */ = { isa = XCConfigurationList; buildConfigurations = ( 358F4EDE1D1E81A9004DF814 /* Debug */, 358F4EDF1D1E81A9004DF814 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 358F4EE01D1E81A9004DF814 /* Build configuration list for PBXNativeTarget "RCTBlob" */ = { isa = XCConfigurationList; buildConfigurations = ( 358F4EE11D1E81A9004DF814 /* Debug */, 358F4EE21D1E81A9004DF814 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; ADD01A6E1E09402E00F6D226 /* Build configuration list for PBXNativeTarget "RCTBlob-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( ADD01A6F1E09402E00F6D226 /* Debug */, ADD01A701E09402E00F6D226 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 358F4ECF1D1E81A9004DF814 /* Project object */; } ================================================ FILE: Libraries/Blob/RCTBlobManager.h ================================================ /** * 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. */ #import #import #import @interface RCTBlobManager : NSObject @end ================================================ FILE: Libraries/Blob/RCTBlobManager.m ================================================ /** * 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. */ #import "RCTBlobManager.h" #import #import static NSString *const kBlobUriScheme = @"blob"; @interface _RCTBlobContentHandler : NSObject - (instancetype)initWithBlobManager:(RCTBlobManager *)blobManager; @end @implementation RCTBlobManager { NSMutableDictionary *_blobs; _RCTBlobContentHandler *_contentHandler; NSOperationQueue *_queue; } RCT_EXPORT_MODULE(BlobModule) @synthesize bridge = _bridge; + (BOOL)requiresMainQueueSetup { return NO; } - (NSDictionary *)constantsToExport { return @{ @"BLOB_URI_SCHEME": kBlobUriScheme, @"BLOB_URI_HOST": [NSNull null], }; } - (dispatch_queue_t)methodQueue { return [[_bridge webSocketModule] methodQueue]; } - (NSString *)store:(NSData *)data { NSString *blobId = [NSUUID UUID].UUIDString; [self store:data withId:blobId]; return blobId; } - (void)store:(NSData *)data withId:(NSString *)blobId { if (!_blobs) { _blobs = [NSMutableDictionary new]; } _blobs[blobId] = data; } - (NSData *)resolve:(NSDictionary *)blob { NSString *blobId = [RCTConvert NSString:blob[@"blobId"]]; NSNumber *offset = [RCTConvert NSNumber:blob[@"offset"]]; NSNumber *size = [RCTConvert NSNumber:blob[@"size"]]; return [self resolve:blobId offset:offset ? [offset integerValue] : 0 size:size ? [size integerValue] : -1]; } - (NSData *)resolve:(NSString *)blobId offset:(NSInteger)offset size:(NSInteger)size { NSData *data = _blobs[blobId]; if (!data) { return nil; } if (offset != 0 || (size != -1 && size != data.length)) { data = [data subdataWithRange:NSMakeRange(offset, size)]; } return data; } RCT_EXPORT_METHOD(enableBlobSupport:(nonnull NSNumber *)socketID) { if (!_contentHandler) { _contentHandler = [[_RCTBlobContentHandler alloc] initWithBlobManager:self]; } [[_bridge webSocketModule] setContentHandler:_contentHandler forSocketID:socketID]; } RCT_EXPORT_METHOD(disableBlobSupport:(nonnull NSNumber *)socketID) { [[_bridge webSocketModule] setContentHandler:nil forSocketID:socketID]; } RCT_EXPORT_METHOD(sendBlob:(NSDictionary *)blob socketID:(nonnull NSNumber *)socketID) { [[_bridge webSocketModule] sendData:[self resolve:blob] forSocketID:socketID]; } RCT_EXPORT_METHOD(createFromParts:(NSArray *> *)parts withId:(NSString *)blobId) { NSMutableData *data = [NSMutableData new]; for (NSDictionary *part in parts) { NSData *partData = [self resolve:part]; [data appendData:partData]; } [self store:data withId:blobId]; } RCT_EXPORT_METHOD(release:(NSString *)blobId) { [_blobs removeObjectForKey:blobId]; } #pragma mark - RCTURLRequestHandler methods - (BOOL)canHandleRequest:(NSURLRequest *)request { return [request.URL.scheme caseInsensitiveCompare:kBlobUriScheme] == NSOrderedSame; } - (id)sendRequest:(NSURLRequest *)request withDelegate:(id)delegate { // Lazy setup if (!_queue) { _queue = [NSOperationQueue new]; _queue.maxConcurrentOperationCount = 2; } __weak __block NSBlockOperation *weakOp; __block NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:nil expectedContentLength:-1 textEncodingName:nil]; [delegate URLRequest:weakOp didReceiveResponse:response]; NSURLComponents *components = [[NSURLComponents alloc] initWithURL:request.URL resolvingAgainstBaseURL:NO]; NSString *blobId = components.path; NSInteger offset = 0; NSInteger size = -1; if (components.queryItems) { for (NSURLQueryItem *queryItem in components.queryItems) { if ([queryItem.name isEqualToString:@"offset"]) { offset = [queryItem.value integerValue]; } if ([queryItem.name isEqualToString:@"size"]) { size = [queryItem.value integerValue]; } } } NSData *data; if (blobId) { data = [self resolve:blobId offset:offset size:size]; } NSError *error; if (data) { [delegate URLRequest:weakOp didReceiveData:data]; } else { error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil]; } [delegate URLRequest:weakOp didCompleteWithError:error]; }]; weakOp = op; [_queue addOperation:op]; return op; } - (void)cancelRequest:(NSOperation *)op { [op cancel]; } @end @implementation _RCTBlobContentHandler { __weak RCTBlobManager *_blobManager; } - (instancetype)initWithBlobManager:(RCTBlobManager *)blobManager { if (self = [super init]) { _blobManager = blobManager; } return self; } - (id)processMessage:(id)message forSocketID:(NSNumber *)socketID withType:(NSString *__autoreleasing _Nonnull *)type { if (![message isKindOfClass:[NSData class]]) { *type = @"text"; return message; } *type = @"blob"; return @{ @"blobId": [_blobManager store:message], @"offset": @0, @"size": @(((NSData *)message).length), }; } @end ================================================ FILE: Libraries/Blob/URL.js ================================================ /** * Copyright (c) 2013-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 URL * @format * @flow */ 'use strict'; const Blob = require('Blob'); const {BlobModule} = require('NativeModules'); let BLOB_URL_PREFIX = null; if (BlobModule && typeof BlobModule.BLOB_URI_SCHEME === 'string') { BLOB_URL_PREFIX = BlobModule.BLOB_URI_SCHEME + ':'; if (typeof BlobModule.BLOB_URI_HOST === 'string') { BLOB_URL_PREFIX += `//${BlobModule.BLOB_URI_HOST}/`; } } /** * To allow Blobs be accessed via `content://` URIs, * you need to register `BlobProvider` as a ContentProvider in your app's `AndroidManifest.xml`: * * ```xml * * * * * * ``` * And then define the `blob_provider_authority` string in `res/values/strings.xml`. * Use a dotted name that's entirely unique to your app: * * ```xml * * your.app.package.blobs * * ``` */ class URL { constructor() { throw new Error('Creating BlobURL objects is not supported yet.'); } static createObjectURL(blob: Blob) { if (BLOB_URL_PREFIX === null) { throw new Error('Cannot create URL for blob!'); } return `${BLOB_URL_PREFIX}${blob.blobId}?offset=${blob.offset}&size=${ blob.size }`; } static revokeObjectURL(url: string) { // Do nothing. } } module.exports = URL; ================================================ FILE: Libraries/BugReporting/BugReporting.js ================================================ /** * Copyright (c) 2013-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 BugReporting * @flow */ 'use strict'; const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); const Map = require('Map'); const infoLog = require('infoLog'); import type EmitterSubscription from 'EmitterSubscription'; type ExtraData = { [key: string]: string }; type SourceCallback = () => string; type DebugData = { extras: ExtraData, files: ExtraData }; function defaultExtras() { BugReporting.addFileSource('react_hierarchy.txt', () => require('dumpReactTree')()); } /** * A simple class for collecting bug report data. Components can add sources that will be queried when a bug report * is created via `collectExtraData`. For example, a list component might add a source that provides the list of rows * that are currently visible on screen. Components should also remember to call `remove()` on the object that is * returned by `addSource` when they are unmounted. */ class BugReporting { static _extraSources: Map = new Map(); static _fileSources: Map = new Map(); static _subscription: ?EmitterSubscription = null; static _redboxSubscription: ?EmitterSubscription = null; static _maybeInit() { if (!BugReporting._subscription) { BugReporting._subscription = RCTDeviceEventEmitter .addListener('collectBugExtraData', BugReporting.collectExtraData, null); defaultExtras(); } if (!BugReporting._redboxSubscription) { BugReporting._redboxSubscription = RCTDeviceEventEmitter .addListener('collectRedBoxExtraData', BugReporting.collectExtraData, null); } } /** * Maps a string key to a simple callback that should return a string payload to be attached * to a bug report. Source callbacks are called when `collectExtraData` is called. * * Returns an object to remove the source when the component unmounts. * * Conflicts trample with a warning. */ static addSource(key: string, callback: SourceCallback): {remove: () => void} { return this._addSource(key, callback, BugReporting._extraSources); } /** * Maps a string key to a simple callback that should return a string payload to be attached * to a bug report. Source callbacks are called when `collectExtraData` is called. * * Returns an object to remove the source when the component unmounts. * * Conflicts trample with a warning. */ static addFileSource(key: string, callback: SourceCallback): {remove: () => void} { return this._addSource(key, callback, BugReporting._fileSources); } static _addSource(key: string, callback: SourceCallback, source: Map): {remove: () => void} { BugReporting._maybeInit(); if (source.has(key)) { console.warn(`BugReporting.add* called multiple times for same key '${key}'`); } source.set(key, callback); return {remove: () => { source.delete(key); }}; } /** * This can be called from a native bug reporting flow, or from JS code. * * If available, this will call `NativeModules.BugReporting.setExtraData(extraData)` * after collecting `extraData`. */ static collectExtraData(): DebugData { const extraData: ExtraData = {}; for (const [key, callback] of BugReporting._extraSources) { extraData[key] = callback(); } const fileData: ExtraData = {}; for (const [key, callback] of BugReporting._fileSources) { fileData[key] = callback(); } infoLog('BugReporting extraData:', extraData); const BugReportingNativeModule = require('NativeModules').BugReporting; BugReportingNativeModule && BugReportingNativeModule.setExtraData && BugReportingNativeModule.setExtraData(extraData, fileData); const RedBoxNativeModule = require('NativeModules').RedBox; RedBoxNativeModule && RedBoxNativeModule.setExtraData && RedBoxNativeModule.setExtraData(extraData, 'From BugReporting.js'); return { extras: extraData, files: fileData }; } } module.exports = BugReporting; ================================================ FILE: Libraries/BugReporting/dumpReactTree.js ================================================ /** * Copyright (c) 2013-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 dumpReactTree * @flow */ 'use strict'; /* const getReactData = require('getReactData'); const INDENTATION_SIZE = 2; const MAX_DEPTH = 2; const MAX_STRING_LENGTH = 50; */ /** * Dump all React Native root views and their content. This function tries * it best to get the content but ultimately relies on implementation details * of React and will fail in future versions. */ function dumpReactTree() { try { return getReactTree(); } catch (e) { return 'Failed to dump react tree: ' + e; } } function getReactTree() { // TODO(sema): Reenable tree dumps using the Fiber tree structure. #15945684 return ( 'React tree dumps have been temporarily disabled while React is ' + 'upgraded to Fiber.' ); /* let output = ''; const rootIds = Object.getOwnPropertyNames(ReactNativeMount._instancesByContainerID); for (const rootId of rootIds) { const instance = ReactNativeMount._instancesByContainerID[rootId]; output += `============ Root ID: ${rootId} ============\n`; output += dumpNode(instance, 0); output += `============ End root ID: ${rootId} ============\n`; } return output; */ } /* function dumpNode(node: Object, identation: number) { const data = getReactData(node); if (data.nodeType === 'Text') { return indent(identation) + data.text + '\n'; } else if (data.nodeType === 'Empty') { return ''; } let output = indent(identation) + `<${data.name}`; if (data.nodeType === 'Composite') { for (const propName of Object.getOwnPropertyNames(data.props || {})) { if (isNormalProp(propName)) { try { const value = convertValue(data.props[propName]); if (value) { output += ` ${propName}=${value}`; } } catch (e) { const message = `[Failed to get property: ${e}]`; output += ` ${propName}=${message}`; } } } } let childOutput = ''; for (const child of data.children || []) { childOutput += dumpNode(child, identation + 1); } if (childOutput) { output += '>\n' + childOutput + indent(identation) + `\n`; } else { output += ' />\n'; } return output; } function isNormalProp(name: string): boolean { switch (name) { case 'children': case 'key': case 'ref': return false; default: return true; } } function convertObject(object: Object, depth: number) { if (depth >= MAX_DEPTH) { return '[...omitted]'; } let output = '{'; let first = true; for (const key of Object.getOwnPropertyNames(object)) { if (!first) { output += ', '; } output += `${key}: ${convertValue(object[key], depth + 1)}`; first = false; } return output + '}'; } function convertValue(value, depth = 0): ?string { if (!value) { return null; } switch (typeof value) { case 'string': return JSON.stringify(possiblyEllipsis(value).replace('\n', '\\n')); case 'boolean': case 'number': return JSON.stringify(value); case 'function': return '[function]'; case 'object': return convertObject(value, depth); default: return null; } } function possiblyEllipsis(value: string) { if (value.length > MAX_STRING_LENGTH) { return value.slice(0, MAX_STRING_LENGTH) + '...'; } else { return value; } } function indent(size: number) { return ' '.repeat(size * INDENTATION_SIZE); } */ module.exports = dumpReactTree; ================================================ FILE: Libraries/BugReporting/getReactData.js ================================================ /** * 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 getReactData * @flow */ 'use strict'; /** * Convert a react internal instance to a sanitized data object. * * This is shamelessly stolen from react-devtools: * https://github.com/facebook/react-devtools/blob/master/backend/getData.js */ function getData(element: Object): Object { var children = null; var props = null; var state = null; var context = null; var updater = null; var name = null; var type = null; var text = null; var publicInstance = null; var nodeType = 'Native'; // If the parent is a native node without rendered children, but with // multiple string children, then the `element` that gets passed in here is // a plain value -- a string or number. if (typeof element !== 'object') { nodeType = 'Text'; text = element + ''; } else if (element._currentElement === null || element._currentElement === false) { nodeType = 'Empty'; } else if (element._renderedComponent) { nodeType = 'NativeWrapper'; children = [element._renderedComponent]; props = element._instance.props; state = element._instance.state; context = element._instance.context; if (context && Object.keys(context).length === 0) { context = null; } } else if (element._renderedChildren) { children = childrenList(element._renderedChildren); } else if (element._currentElement && element._currentElement.props) { // This is a native node without rendered children -- meaning the children // prop is just a string or (in the case of the