diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 00000000..3ad02974
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,50 @@
+{
+ "extends": [
+ "airbnb",
+ "prettier",
+ "prettier/react"
+ ],
+ "parser": "babel-eslint",
+ "plugins": [
+ "prettier"
+ ],
+ "env": {
+ "jasmine": true
+ },
+ "globals": {
+ "__DEV__": true,
+ "ReactClass": true,
+ "fetch": true
+ },
+ "rules": {
+ "prettier/prettier": ["error", {
+ "trailingComma": "all",
+ "singleQuote": true
+ }],
+
+ "no-underscore-dangle": 0,
+ "no-use-before-define": 0,
+ "no-unused-expressions": 0,
+ "new-cap": 0,
+ "no-plusplus": 0,
+ "no-class-assign": 0,
+ "no-duplicate-imports": 0,
+ "no-console": 0,
+
+ "import/extensions": 0,
+ "import/no-extraneous-dependencies": 0,
+ "import/no-unresolved": 0,
+
+ "react/jsx-filename-extension": [
+ 0, { "extensions": [".js", ".jsx"] }
+ ],
+
+ "react/sort-comp": 0,
+ "react/prefer-stateless-function": 0,
+
+ "react/forbid-prop-types": 1,
+ "react/prop-types": 0,
+ "react/require-default-props": 0,
+ "react/no-unused-prop-types": 0
+ }
+}
diff --git a/.gitignore b/.gitignore
index 00cbbdf5..619395b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,3 +57,27 @@ typings/
# dotenv environment variables file
.env
+
+
+#### Xcode ignore ####
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## User settings
+xcuserdata/
+
+## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
+*.xcscmblueprint
+*.xccheckout
+
+## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
+build/
+DerivedData/
+*.moved-aside
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..e5af7848
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "ios/PocketSVG"]
+ path = ios/PocketSVG
+ url = https://github.com/pocketsvg/PocketSVG
diff --git a/ARKit.js b/ARKit.js
new file mode 100644
index 00000000..f52b3698
--- /dev/null
+++ b/ARKit.js
@@ -0,0 +1,248 @@
+//
+// index.js
+//
+// Created by HippoAR on 7/9/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import {
+ StyleSheet,
+ View,
+ Text,
+ NativeModules,
+ requireNativeComponent,
+} from 'react-native';
+import { keyBy, mapValues, isBoolean } from 'lodash';
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+
+import {
+ deprecated,
+ detectionImages,
+ planeDetection,
+ position,
+ transition,
+} from './components/lib/propTypes';
+import { pickColors, pickColorsFromFile } from './lib/pickColors';
+import generateId from './components/lib/generateId';
+
+const ARKitManager = NativeModules.ARKitManager;
+
+const TRACKING_STATES = ['NOT_AVAILABLE', 'LIMITED', 'NORMAL'];
+
+const TRACKING_REASONS = [
+ 'NONE',
+ 'INITIALIZING',
+ 'EXCESSIVE_MOTION',
+ 'INSUFFICIENT_FEATURES',
+];
+const TRACKING_STATES_COLOR = ['red', 'orange', 'green'];
+
+class ARKit extends Component {
+ state = {
+ state: 0,
+ reason: 0,
+ floor: null,
+ };
+
+ componentDidMount() {
+ ARKitManager.resume();
+ }
+
+ componentWillUnmount() {
+ ARKitManager.pause();
+ }
+
+ getCallbackProps() {
+ return mapValues(
+ keyBy([
+ 'onTapOnPlaneUsingExtent',
+ 'onTapOnPlaneNoExtent',
+ 'onPlaneDetected',
+ 'onPlaneRemoved',
+ 'onPlaneUpdated',
+ 'onAnchorDetected',
+ 'onAnchorUpdated',
+ 'onAnchorRemoved',
+ 'onTrackingState',
+ 'onARKitError',
+ ]),
+ name => this.callback(name),
+ );
+ }
+
+ render(AR = RCTARKit) {
+ let state = null;
+ if (this.props.debug) {
+ state = (
+
+
+
+ {TRACKING_REASONS[this.state.reason] || this.state.reason}
+
+
+ );
+ }
+ return (
+
+
+ {state}
+
+ );
+ }
+
+ _onTrackingState = ({
+ state = this.state.state,
+ reason = this.state.reason,
+ }) => {
+ if (this.props.onTrackingState) {
+ this.props.onTrackingState({
+ state: TRACKING_STATES[state] || state,
+ reason: TRACKING_REASONS[reason] || reason,
+ });
+ }
+ // TODO: check if we can remove this
+ if (this.props.debug) {
+ this.setState({
+ state,
+ reason,
+ });
+ }
+ };
+
+ _onEvent = event => {
+ let eventName = event.nativeEvent.event;
+ if (!eventName) {
+ return;
+ }
+ eventName = eventName.charAt(0).toUpperCase() + eventName.slice(1);
+ const eventListener = this.props[`on${eventName}`];
+ if (eventListener) {
+ eventListener(event.nativeEvent);
+ }
+ };
+
+ // handle deprecated alias
+ _onPlaneUpdated = nativeEvent => {
+ if (this.props.onPlaneUpdate) {
+ this.props.onPlaneUpdate(nativeEvent);
+ }
+ if (this.props.onPlaneUpdated) {
+ this.props.onPlaneUpdated(nativeEvent);
+ }
+ };
+
+ callback(name) {
+ return event => {
+ if (this[`_${name}`]) {
+ this[`_${name}`](event.nativeEvent);
+ return;
+ }
+ if (!this.props[name]) {
+ return;
+ }
+ this.props[name](event.nativeEvent);
+ };
+ }
+}
+
+const styles = StyleSheet.create({
+ statePanel: {
+ position: 'absolute',
+ top: 30,
+ left: 10,
+ height: 20,
+ borderRadius: 10,
+ padding: 4,
+ backgroundColor: 'black',
+ flexDirection: 'row',
+ },
+ stateIcon: {
+ width: 12,
+ height: 12,
+ borderRadius: 6,
+ marginRight: 4,
+ },
+ stateText: {
+ color: 'white',
+ fontSize: 10,
+ height: 12,
+ },
+});
+
+// copy all ARKitManager properties to ARKit
+Object.keys(ARKitManager).forEach(key => {
+ ARKit[key] = ARKitManager[key];
+});
+
+const addDefaultsToSnapShotFunc = funcName => (
+ { target = 'cameraRoll', format = 'png' } = {},
+) => ARKitManager[funcName]({ target, format });
+
+ARKit.snapshot = addDefaultsToSnapShotFunc('snapshot');
+ARKit.snapshotCamera = addDefaultsToSnapShotFunc('snapshotCamera');
+
+ARKit.exportModel = presetId => {
+ const id = presetId || generateId();
+ const property = { id };
+ return ARKitManager.exportModel(property).then(result => ({ ...result, id }));
+};
+
+ARKit.pickColors = pickColors;
+ARKit.pickColorsFromFile = pickColorsFromFile;
+ARKit.propTypes = {
+ debug: PropTypes.bool,
+ planeDetection,
+ origin: PropTypes.shape({
+ position,
+ transition,
+ }),
+ lightEstimationEnabled: PropTypes.bool,
+ autoenablesDefaultLighting: PropTypes.bool,
+ worldAlignment: PropTypes.number,
+ detectionImages,
+ onARKitError: PropTypes.func,
+
+ onFeaturesDetected: PropTypes.func,
+ // onLightEstimation is called rapidly, better poll with
+ // ARKit.getCurrentLightEstimation()
+ onLightEstimation: PropTypes.func,
+
+ onPlaneDetected: PropTypes.func,
+ onPlaneRemoved: PropTypes.func,
+ onPlaneUpdated: PropTypes.func,
+ onPlaneUpdate: deprecated(PropTypes.func, 'Use `onPlaneUpdated` instead'),
+
+ onAnchorDetected: PropTypes.func,
+ onAnchorRemoved: PropTypes.func,
+ onAnchorUpdated: PropTypes.func,
+
+ onTrackingState: PropTypes.func,
+ onTapOnPlaneUsingExtent: PropTypes.func,
+ onTapOnPlaneNoExtent: PropTypes.func,
+ onEvent: PropTypes.func,
+ isMounted: PropTypes.func,
+ isInitialized: PropTypes.func,
+};
+
+const RCTARKit = requireNativeComponent('RCTARKit', ARKit);
+
+export default ARKit;
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..71e34db5
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+
+
+see Releases
diff --git a/DeviceMotion.js b/DeviceMotion.js
new file mode 100644
index 00000000..aa8041fd
--- /dev/null
+++ b/DeviceMotion.js
@@ -0,0 +1,19 @@
+import { NativeEventEmitter, NativeModules } from 'react-native';
+
+const deviceMotionEmitter = new NativeEventEmitter(NativeModules.DeviceMotion);
+let subscription;
+
+const DeviceMotion = {
+ start(callback, interval) {
+ NativeModules.DeviceMotion.setUpdateInterval(interval);
+ subscription = deviceMotionEmitter.addListener('MotionData', callback);
+ NativeModules.DeviceMotion.startUpdates();
+ },
+
+ stop() {
+ subscription.remove();
+ NativeModules.DeviceMotion.stopUpdates();
+ },
+};
+
+export default DeviceMotion;
diff --git a/README.md b/README.md
index 77a87e07..f2f07969 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,41 @@
-# react-native-arkit
+⚠️ **LOOKING FOR MAINTAINERS - This Repo is currently not maintained. Give https://github.com/ViroCommunity/viro/ a try which has been open sourced and also supports android (ARCORE)** ⚠️
+
+# react-native-arkit
+
+
[](https://www.npmjs.com/package/react-native-arkit)
[](https://www.npmjs.com/package/react-native-arkit)
React Native binding for iOS ARKit.
-**Note**: ARKit is only supported by devices with A9 or later processors (iPhone 6s/7/SE, iPad 2017/Pro) on [iOS 11 beta](https://developer.apple.com/download/). You also need [Xcode 9 beta](https://developer.apple.com/download/) to build the project.
+**Made with React Native Arkit**:
+
+- Homestory: An AI powered interior design assistant ([App store](https://itunes.apple.com/us/app/homestory-augmented-reality/id1292552232?ls=1&mt=8))
+
+**Tutorial**: [How to make an ARKit app in 5 minutes using React Native](https://medium.com/@HippoAR/how-to-make-your-own-arkit-app-in-5-minutes-using-react-native-9d7ce109a4c2)
+
+**Sample Project**: https://github.com/HippoAR/ReactNativeARKit
+
+**Note**: ARKit is only supported by devices with A9 or later processors (iPhone 6s/7/SE/8/X, iPad 2017/Pro) on iOS 11. You also need Xcode 9 to build the project.
+
+There is a Slack group that anyone can join for help / support / general questions.
+
+[**Join Slack**](https://join.slack.com/t/react-native-ar/shared_invite/enQtMjUzMzg3MjM0MTQ5LWU3Nzg2YjI4MGRjMTM1ZDBlNmIwYTE4YmM0M2U0NmY2YjBiYzQ4YzlkODExMTA0NDkwMzFhYWY4ZDE2M2Q4NGY)
## Getting started
-`$ npm install react-native-arkit --save`
+`$ yarn add react-native-arkit`
+
+make sure to use the latest version of yarn (>=1.x.x)
+
+(npm does not work properly at the moment. See https://github.com/HippoAR/react-native-arkit/issues/103)
+
### Mostly automatic installation
+⚠️ **Currently automatic installation does not work as PocketSVG is missing. Follow the manual installation.**
+
`$ react-native link react-native-arkit`
### Manual installation
@@ -21,18 +44,37 @@ React Native binding for iOS ARKit.
#### iOS
1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]`
-2. Go to `node_modules` ➜ `react-native-arkit` and add `RCTARKit.xcodeproj`
-3. In XCode, in the project navigator, select your project. Add `libRCTARKit.a` to your project's `Build Phases` ➜ `Link Binary With Libraries`
-4. Run your project (`Cmd+R`)<
+2. Go to `node_modules` ➜ add `react-native-arkit/ios/RCTARKit.xcodeproj` and `react-native-arkit/ios/PocketSVG/PocketSVG.xcodeproj`
+3. In XCode, in the project navigator, select your project. Add `libRCTARKit.a` `and PocketSVG.framework` to your project's `Build Phases` ➜ `Link Binary With Libraries`
+4. In Tab `General` ➜ `Embedded Binaries` ➜ `+` ➜ Add `PocketSVG.framework ios`
+5. Run your project (`Cmd+R`)<
+
+
+##### iOS Project configuration
+
+These steps are mandatory regardless of doing a manual or automatic installation:
+
+1. Give permissions for camera usage. In `Info.plist` add the following:
+
+```
+NSCameraUsageDescription
+Your message to user when the camera is accessed for the first time
+```
+2. ARKit only runs on arm64-ready devices so the default build architecture should be set to arm64: go to `Build settings` ➜ `Build Active Architecture Only` and change the value to `Yes`.
+
+
## Usage
-Sample React Native ARKit App
+A simple sample React Native ARKit App
+
```javascript
+// index.ios.js
+
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
-import ARKit from 'react-native-arkit';
+import { ARKit } from 'react-native-arkit';
export default class ReactNativeARKit extends Component {
render() {
@@ -41,11 +83,120 @@ export default class ReactNativeARKit extends Component {
+ // enable plane detection (defaults to Horizontal)
+ planeDetection={ARKit.ARPlaneDetection.Horizontal}
+
+ // enable light estimation (defaults to true)
+ lightEstimationEnabled
+ // get the current lightEstimation (if enabled)
+ // it fires rapidly, so better poll it from outside with
+ // ARKit.getCurrentLightEstimation()
+ onLightEstimation={e => console.log(e.nativeEvent)}
+
+ // event listener for (horizontal) plane detection
+ onPlaneDetected={anchor => console.log(anchor)}
+
+ // event listener for plane update
+ onPlaneUpdated={anchor => console.log(anchor)}
+
+ // arkit sometimes removes detected planes
+ onPlaneRemoved={anchor => console.log(anchor)}
+
+ // event listeners for all anchors, see [Planes and Anchors](#planes-and-anchors)
+ onAnchorDetected={anchor => console.log(anchor)}
+ onAnchorUpdated={anchor => console.log(anchor)}
+ onAnchorRemoved={anchor => console.log(anchor)}
+
+ // you can detect images and will get an anchor for these images
+ detectionImages={[{ resourceGroupName: 'DetectionImages' }]}
+
+
+ onARKitError={console.log} // if arkit could not be initialized (e.g. missing permissions), you will get notified here
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ pathFlatness: 0.1,
+ // it's also possible to specify a chamfer profile:
+ chamferRadius: 5,
+ chamferProfilePathSvg: `
+
+ `,
+ extrusion: 10,
+ }}
+ />
+
);
}
@@ -55,6 +206,503 @@ AppRegistry.registerComponent('ReactNativeARKit', () => ReactNativeARKit);
```
+
+
+
+
+### ``-Component
+
+#### Props
+
+| Prop | Type | Note |
+|---|---|---|
+| `debug` | `Boolean` | Debug mode will show the 3D axis and feature points detected.
+| `planeDetection` | `ARKit.ARPlaneDetection.{ Horizontal \| Vertical \| HorizontalVertical \| None }` | ARKit plane detection. Defaults to `Horizontal`. `Vertical` is available with IOS 11.3
+| `lightEstimationEnabled` | `Boolean` | ARKit light estimation (defaults to false).
+| `worldAlignment` | `ARKit.ARWorldAlignment.{ Gravity \| GravityAndHeading \| Camera }` | **ARWorldAlignmentGravity**
The coordinate system's y-axis is parallel to gravity, and its origin is the initial position of the device. **ARWorldAlignmentGravityAndHeading**
The coordinate system's y-axis is parallel to gravity, its x- and z-axes are oriented to compass heading, and its origin is the initial position of the device. **ARWorldAlignmentCamera**
The scene coordinate system is locked to match the orientation of the camera. Defaults to `ARKit.ARWorldAlignment.Gravity`. [See](https://developer.apple.com/documentation/arkit/arworldalignment)|
+| `origin` | `{position, transition}` | Usually `{0,0,0}` is where you launched the app. If you want to have a different origin, you can set it here. E.g. if you set `origin={{position: {0,-1, 0}, transition: {duration: 1}}}` the new origin will be one meter below. If you have any objects already placed, they will get moved down using the given transition. All hit-test functions or similar will report coordinates relative to that new origin as `position`. You can get the original coordinates with `positionAbsolute` in these functions |
+| `detectionImages` | `Array` | An Array of `DetectionImage` (see below), only available on IOS 11.3 |
+
+##### `DetectionImage`
+
+An `DetectionImage` is an image or image resource group that should be detected by ARKit.
+
+See https://developer.apple.com/documentation/arkit/arreferenceimage?language=objc how to add these images.
+
+You will then receive theses images in `onAnchorDetected/onAnchorUpdated`. See also [Planes and Anchors](#planes-and-anchors) for more details.
+
+`DetectionImage` has these properties
+
+| Prop | Type | Notes
+|---|---|---|
+| `resourceGroupName` | `String` | The name of the resource group |
+
+We probably will add the option to load images from other sources as well (PRs encouraged).
+
+#### Events
+
+| Event Name | Returns | Notes
+|---|---|---|
+| `onARKitError` | `ARKiterror` | will report whether an error occured while initializing ARKit. A common error is when the user has not allowed camera access. Another error is, if you use `worldAlignment=GravityAndHeading` and location service is turned off |
+| `onLightEstimation` | `{ ambientColorTemperature, ambientIntensity }` | Light estimation on every frame. Called rapidly, better use polling. See `ARKit.getCurrentLightEstimation()`
+| `onFeaturesDetected` | `{ featurePoints}` | Detected Features on every frame (currently also not throttled). Usefull to display custom dots for detected features. You can also poll this information with `ARKit.getCurrentDetectedFeaturePoints()`
+| `onAnchorDetected` | `Anchor` | When an anchor (plane or image) is first detected.
+| `onAnchorUpdated` | `Anchor` | When an anchor is updated
+| `onAnchorRemoved` | `Anchor` | When an anchor is removed
+| `onPlaneDetected` | `Anchor` | When a plane anchor is first detected.
+| `onPlaneUpdated` | `Anchor` | When a detected plane is updated
+| `onPlaneRemoved` | `Anchor` | When a detected plane is removed
+
+See [Planes and Anchors](#planes-and-anchors) for Details about anchors
+
+
+
+#### Planes and Anchors
+
+ARKit can detect different anchors in the real world:
+
+- `plane` horizontal and vertical planes
+- `image`, image-anchors [See DetectionImage](#DetectionImage)
+- face with iphone X or similar (not implemented yet)
+
+You then will receive anchor objects in the `onAnchorDetected`, `onAnchorUpdated`, `onAnchorRemoved` callbacks on your ``-component.
+
+You can use `onPlaneDetected`, `onPlaneUpdated`, `onPlaneRemoved` to only receive plane-anchors (may be deprecated later).
+
+The `Anchor` object has the following properties:
+
+| Property | Type | Description
+|---|---|---|
+| `id` | `String` | a unique id identifying the anchor |
+| `type` | `String` | The type of the anchor (plane, image) |
+| `position` | `{ x, y, z }` | the position of the anchor (relative to the origin) |
+| `positionAbsolute` | `{ x, y, z }` | the absolute position of the anchor |
+| `eulerAngles` | `{ x, y, z }` | the rotation of the plane |
+
+If its a `plane`-anchor, it will have these additional properties:
+
+| Property | Description
+|---|---|
+| `alignment` | `ARKit.ARPlaneAnchorAlignment.Horizontal` or `ARKit.ARPlaneAnchorAlignment.Vertical`
so you can check whether it was a horizontal or vertical plane |
+| `extent` | see https://developer.apple.com/documentation/arkit/arplaneanchor?language=objc |
+| `center` | see https://developer.apple.com/documentation/arkit/arplaneanchor?language=objc |
+
+`image`-Anchor:
+
+| Property | type | Description
+|---|---|---|
+| `image` | `{name}` | an object with the name of the image.
+
+
+
+### Static methods
+
+Static Methods can directly be used on the `ARKit`-export:
+
+```
+import { ARKit } from 'react-native-arkit'
+
+//...
+const result = await ARKit.hitTestSceneObjects(point);
+
+```
+
+All methods return a *promise* with the result.
+
+| Method Name | Arguments | Notes
+|---|---|---|
+| `snapshot` | | | Take a screenshot (will save to Photo Library) |
+| `snapshotCamera` | | Take a screenshot without 3d models (will save to Photo Library) |
+| `getCameraPosition` | | Get the current position of the `ARCamera` |
+| `getCamera` | | Get all properties of the `ARCamera` |
+| `getCurrentLightEstimation` | | Get current light estimation `{ ambientColorTemperature, ambientIntensity}` |
+| `getCurrentDetectedFeaturePoints` | | Get current detected feature points (in last current frame) (array) |
+| `focusScene` | | Sets the scene's position/rotation to where it was when first rendered (but now relative to your device's current position/rotation) |
+| `hitTestPlanes` | point, type | check if a plane has ben hit by point (`{x,y}`) with detection type (any of `ARKit.ARHitTestResultType`). See https://developer.apple.com/documentation/arkit/arhittestresulttype?language=objc for further information |
+| `hitTestSceneObjects` | point | check if a scene object has ben hit by point (`{x,y}`) |
+| `isInitialized` | boolean | check whether arkit has been initialized (e.g. by mounting). See https://github.com/HippoAR/react-native-arkit/pull/152 for details |
+| `isMounted` | boolean | check whether arkit has been mounted. See https://github.com/HippoAR/react-native-arkit/pull/152 for details |
+
+### 3D-Components
+
+This project allows you to work with 3d elements like with usual react-components.
+We provide some primitive shapes like cubes, spheres, etc. as well as
+a component to load model-files.
+
+You can also nest components to create new Components. Child-elements will
+be relative to the parent:
+
+```
+const BigExclamationMark = ({ position, eulerAngles, color = '#ff0000' }) => (
+
+
+
+
+)
+
+// somewhere else
+
+
+
+```
+
+
+#### General props
+
+Most 3d object have these common properties
+
+| Prop | Type | Description |
+|---|---|---|
+| `position` | `{ x, y, z }` | The object's position (y is up) |
+| `scale` | Number | The scale of the object. Defaults to 1 |
+| `eulerAngles` | `{ x, y, z }` | The rotation in eulerAngles |
+| `id` | String | a unique identifier. Only provide one, if you need to find the node later in hit-testing. |
+| `shape` | depends on object | the shape of the object (will probably renamed to geometry in future versions)
+| `material` | `{ diffuse, metalness, roughness, lightingModel, shaders }` | the material of the object |
+| `transition` | `{duration: 1}` | Some property changes can be animated like in css transitions. Currently you can specify the duration (in seconds). |
+
+Advanced properties:
+
+| Prop | Type | Description |
+|---|---|---|
+| `rotation` | TODO | see scenkit documentation |
+| `orientation` | TODO | see scenkit documentation |
+| `renderingOrder` | Number | Order in which object is rendered. Usefull to place elements "behind" others, although they are nearer. |
+| `categoryBitMask` | Number / bitmask | control which lights affect this object |
+| `castsShadow` | `boolean` | whether this object casts shadows |
+| `constraint` | `ARKit.Constraint.{ BillboardAxisAll \| BillboardAxisX \| BillboardAxisY \| BillboardAxisZ \| None }` | Constrains the node to always point to the camera |
+
+*New experimental feature:*
+
+You can switch properties on mount or onmount by specifying `propsOnMount` and `propsOnUnmount`.
+E.g. you can scale an object on unmount:
+
+```
+
+```
+
+#### Material
+
+Most objects take a material property with these sub-props:
+
+| Prop | Type | Description |
+|---|---|---|
+| `diffuse` | `{ ...mapProperties }` (see below) | [diffuse](https://developer.apple.com/documentation/scenekit/scnmaterial/1462589-diffuse?language=objc)
+| `specular` | `{ ...mapProperties }` (see below) | [specular](https://developer.apple.com/documentation/scenekit/scnmaterial/1462516-specular?language=objc)
+| `displacement` | `{ ...mapProperties }` (see below) | [displacement](https://developer.apple.com/documentation/scenekit/scnmaterial/2867516-displacement?language=objc)
+| `normal` | `{ ...mapProperties }` (see below) | [normal](https://developer.apple.com/documentation/scenekit/scnmaterial/1462542-normal)
+| `metalness` | number | metalness of the object |
+| `roughness` | number | roughness of the object |
+| `doubleSided` | boolean | render both sides, default is `true` |
+| `litPerPixel` | boolean | calculate lighting per-pixel or vertex [litPerPixel](https://developer.apple.com/documentation/scenekit/scnmaterial/1462580-litperpixel) |
+| `lightingModel` | `ARKit.LightingModel.*` | [LightingModel](https://developer.apple.com/documentation/scenekit/scnmaterial.lightingmodel) |
+| `blendMode` | `ARKit.BlendMode.*` | [BlendMode](https://developer.apple.com/documentation/scenekit/scnmaterial/1462585-blendmode) |
+| `transparencyMode` | `ARKit.TransparencyMode.*` | [TransparencyMode](https://developer.apple.com/documentation/scenekit/scnmaterial/1462549-transparencymode?language=objc) |
+| `fillMode` | `ARKit.FillMode.*` | [FillMode](https://developer.apple.com/documentation/scenekit/scnmaterial/2867442-fillmode)
+| `shaders` | Object with keys from `ARKit.ShaderModifierEntryPoint.*` and shader strings as values | [Shader modifiers](https://developer.apple.com/documentation/scenekit/scnshadable) |
+| `colorBufferWriteMask` | `ARKit.ColorMask.*` | [color mask](https://developer.apple.com/documentation/scenekit/scncolormask). Set to ARKit.ColorMask.None so that an object is transparent, but receives deferred shadows. |
+
+Map Properties:
+
+| Prop | Type | Description |
+|---|---|---|
+| `path` | string | Currently `require` is not supported, so this is an absolute link to a local resource placed for example in .xcassets |
+| `color` | string | Color string, only used if path is not provided |
+| `wrapS` | `ARKit.WrapMode.{ Clamp \| Repeat \| Mirror }` | [wrapS](https://developer.apple.com/documentation/scenekit/scnmaterialproperty/1395384-wraps?language=objc) |
+| `wrapT` | `ARKit.WrapMode.{ Clamp \| Repeat \| Mirror }` | [wrapT](https://developer.apple.com/documentation/scenekit/scnmaterialproperty/1395382-wrapt?language=objc) |
+| `wrap` | `ARKit.WrapMode.{ Clamp \| Repeat \| Mirror }` | Shorthand for setting both wrapS & wrapT |
+| `translation` | `{ x, y, z }` | Translate the UVs, equivalent to applying a translation matrix to SceneKit's `transformContents` |
+| `rotation` | `{ angle, x, y, z }` | Rotate the UVs, equivalent to applying a rotation matrix to SceneKit's `transformContents` |
+| `scale` | `{ x, y, z }` | Scale the UVs, equivalent to applying a scale matrix to SceneKit's `transformContents` |
+
+
+#### ``
+
+This Object has no geometry, but is simply a wrapper for other components.
+It receives all common properties like position, eulerAngles, scale, opacity, etc.
+but no shape or material.
+
+
+
+#### [``](https://developer.apple.com/documentation/scenekit/scnbox)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ width, height, length, chamfer }` |
+
+And any common object property (position, material, etc.)
+
+#### [``](https://developer.apple.com/documentation/scenekit/scnsphere)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ radius }` |
+
+
+
+#### [``](https://developer.apple.com/documentation/scenekit/scncylinder)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ radius, height }` |
+
+#### [``](https://developer.apple.com/documentation/scenekit/scncone)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ topR, bottomR, height }` |
+
+#### [``](https://developer.apple.com/documentation/scenekit/scnpyramid)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ width, height, length }` |
+
+#### [``](https://developer.apple.com/documentation/scenekit/scntube)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ innerR, outerR, height }` |
+
+#### [``](https://developer.apple.com/documentation/scenekit/scntorus)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ ringR, pipeR }` |
+
+#### [``](https://developer.apple.com/documentation/scenekit/scncapsule)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ capR, height }` |
+
+#### [``](https://developer.apple.com/documentation/scenekit/scnplane)
+
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ width, height }` |
+
+Notice: planes are veritcally aligned. If you want a horizontal plane, rotate it around the x-axis.
+
+*Example*:
+
+This is a horizontal plane that only receives shadows, but is invisible otherwise:
+
+```
+
+```
+
+
+#### [``](https://developer.apple.com/documentation/scenekit/scntext)
+
+| Prop | Type |
+|---|---|
+| `text` | `String` |
+| `font` | `{ name, size, depth, chamfer }` |
+
+
+
+#### ``
+
+SceneKit only supports `.scn` and `.dae` formats.
+
+
+| Prop | Type |
+|---|---|
+| `model` | `{ file, node, scale, alpha }` |
+
+Objects currently don't take material property.
+
+#### ``
+
+Creates a extruded shape by an svg path.
+See https://github.com/HippoAR/react-native-arkit/pull/89 for details
+
+| Prop | Type |
+|---|---|
+| `shape` | `{ pathSvg, extrusion, pathFlatness, chamferRadius, chamferProfilePathSvg, chamferProfilePathFlatness }` |
+
+
+
+#### [``](https://developer.apple.com/documentation/scenekit/scnlight)
+
+Place lights on the scene!
+
+You might set `autoenablesDefaultLighting={false}` on The `` component to disable default lighting. You can use `lightEstimationEnabled` and `ARKit.getCurrentLightEstimation()` to find values for intensity and temperature. This produces much nicer results then `autoenablesDefaultLighting`.
+
+
+
+| Prop | Type | Description |
+|---|---|---|
+| `position` | `{ x, y, z }` | |
+| `eulerAngles` | `{ x, y, z }` | |
+| `type` | any of `ARKit.LightType` | see [here for details](https://developer.apple.com/documentation/scenekit/scnlight.lighttype) |
+| `color` | `string` | the color of the light |
+| `temperature` | `Number` | The color temperature of the light |
+| `intensity` | `Number` | The light intensity |
+| `lightCategoryBitMask` | `Number`/`bitmask` | control which objects are lit by this light |
+| `castsShadow` | `boolean` | whether to cast shadows on object |
+| `shadowMode`| `ARKit.ShadowMode.* | Define the shadowmode. Set to `ARKit.ShadowMode.Deferred` to cast shadows on invisible objects (like an invisible floor plane) |
+
+
+
+Most properties described here are also supported: https://developer.apple.com/documentation/scenekit/scnlight
+
+This feature is new. If you experience any problem, please report an issue!
+
+
+### HOCs (higher order components)
+
+#### withProjectedPosition()
+
+this hoc allows you to create 3D components where the position is always relative to the same point on the screen/camera, but sticks to a plane or object.
+
+Think about a 3D cursor that can be moved across your table or a 3D cursor on a wall.
+
+You can use the hoc like this:
+
+```
+const Cursor3D = withProjectedPosition()(({positionProjected, projectionResult}) => {
+ if(!projectionResult) {
+ // nothing has been hit, don't render it
+ return null;
+ }
+ return (
+
+ )
+})
+
+```
+
+It's recommended that you specify a transition duration (0.1s works nice), as the position gets updated rapidly, but slightly throttled.
+
+Now you can use your 3D cursor like this:
+
+##### Attach to a given detected horizontal plane
+
+Given you have detected a plane with onPlaneDetected, you can make the cursor stick to that plane:
+
+```
+
+
+```
+
+If you don't have the id, but want to place the cursor on a certain plane (e.g. the first or last one), pass a function for plane. This function will get all hit-results and you can return the one you need:
+
+```
+ results.length > 0 ? results[0] : null
+ }}
+/>
+
+```
+
+You can also add a property `onProjectedPosition` to your cursor which will be called with the hit result on every frame
+
+It uses https://developer.apple.com/documentation/arkit/arframe/2875718-hittest with some default options. Please file an issue or send a PR if you need more control over the options here!
+
+##### Attach to a given 3D object
+
+You can attach the cursor on a 3D object, e.g. a non-horizontal-plane or similar:
+
+Given there is some 3D object on your scene with `id="my-nodeId"`
+
+```
+
+```
+
+Like with planes, you can select the node with a function.
+
+E.gl you have several "walls" with ids "wall_1", "wall_2", etc.
+
+```
+ results.find(r => r.id.startsWith('wall_')),
+ }}
+/>
+```
+
+
+It uses https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522929-hittest with some default options. Please file an issue or send a PR if you need more control over the options here!
+
+
+## FAQ:
+
+#### Which permissions does this use?
+
+- **camera access** (see section iOS Project configuration above). The user is asked for permission, as soon as you mount an `` component or use any of its API. If user denies access, you will get an error in `onARKitError`
+- **location service**: only needed if you use `ARKit.ARWorldAlignment.GravityAndHeading`.
+
+#### Is there an Android / ARCore version?
+
+Not yet, but there has been a proof-of-concept: https://github.com/HippoAR/react-native-arkit/issues/14. We are looking for contributors to help backporting this proof-of-conept to react-native-arkit.
+
+#### I have another question...
+
+[**Join Slack!**](https://join.slack.com/t/react-native-ar/shared_invite/enQtMjUzMzg3MjM0MTQ5LWU3Nzg2YjI4MGRjMTM1ZDBlNmIwYTE4YmM0M2U0NmY2YjBiYzQ4YzlkODExMTA0NDkwMzFhYWY4ZDE2M2Q4NGY)
+
+
## Contributing
-If you find a bug or would like to request a new feature, just [open an issue](https://github.com/HippoAR/react-native-arkit/issues/new). Your contributions are always welcome! Submit a pull request and see `contribution.md` for guidelines.
+If you find a bug or would like to request a new feature, just [open an issue](https://github.com/HippoAR/react-native-arkit/issues/new). Your contributions are always welcome! Submit a pull request and see [`CONTRIBUTING.md`](CONTRIBUTING.md) for guidelines.
diff --git a/components/ARBox.js b/components/ARBox.js
new file mode 100644
index 00000000..b53350ce
--- /dev/null
+++ b/components/ARBox.js
@@ -0,0 +1,23 @@
+//
+// ARBox.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARBox = createArComponent('addBox', {
+ shape: PropTypes.shape({
+ width: PropTypes.number,
+ height: PropTypes.number,
+ length: PropTypes.number,
+ chamfer: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARBox;
diff --git a/components/ARCapsule.js b/components/ARCapsule.js
new file mode 100644
index 00000000..b74df957
--- /dev/null
+++ b/components/ARCapsule.js
@@ -0,0 +1,21 @@
+//
+// ARCapsule.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARCapsule = createArComponent('addCapsule', {
+ shape: PropTypes.shape({
+ capR: PropTypes.number,
+ height: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARCapsule;
diff --git a/components/ARCone.js b/components/ARCone.js
new file mode 100644
index 00000000..01eef2b0
--- /dev/null
+++ b/components/ARCone.js
@@ -0,0 +1,22 @@
+//
+// ARCone.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARCone = createArComponent('addCone', {
+ shape: PropTypes.shape({
+ topR: PropTypes.number,
+ bottomR: PropTypes.number,
+ height: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARCone;
diff --git a/components/ARCylinder.js b/components/ARCylinder.js
new file mode 100644
index 00000000..ccbd1887
--- /dev/null
+++ b/components/ARCylinder.js
@@ -0,0 +1,21 @@
+//
+// ARCylinder.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARCylinder = createArComponent('addCylinder', {
+ shape: PropTypes.shape({
+ radius: PropTypes.number,
+ height: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARCylinder;
diff --git a/components/ARGroup.js b/components/ARGroup.js
new file mode 100644
index 00000000..87fbba16
--- /dev/null
+++ b/components/ARGroup.js
@@ -0,0 +1,12 @@
+//
+// ARBox.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import createArComponent from './lib/createArComponent';
+
+const ARBox = createArComponent('addGroup', {});
+
+export default ARBox;
diff --git a/components/ARLight.js b/components/ARLight.js
new file mode 100644
index 00000000..0328e0e9
--- /dev/null
+++ b/components/ARLight.js
@@ -0,0 +1,28 @@
+import PropTypes from 'prop-types';
+
+import { categoryBitMask, color, lightType, shadowMode } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARLight = createArComponent('addLight', {
+ type: lightType,
+ color,
+ temperature: PropTypes.number,
+ intensity: PropTypes.number,
+ attenuationStartDistance: PropTypes.number,
+ attenuationEndDistance: PropTypes.number,
+ spotInnerAngle: PropTypes.number,
+ spotOuterAngle: PropTypes.number,
+ castsShadow: PropTypes.bool,
+ shadowRadius: PropTypes.number,
+ shadowColor: color,
+ // shadowMapSize: PropTypes.number,
+ shadowSampleCount: PropTypes.number,
+ shadowMode,
+ shadowBias: PropTypes.number,
+ orthographicScale: PropTypes.number,
+ zFar: PropTypes.number,
+ zNear: PropTypes.number,
+ lightCategoryBitMask: categoryBitMask,
+});
+
+export default ARLight;
diff --git a/components/ARModel.js b/components/ARModel.js
new file mode 100644
index 00000000..2103cc74
--- /dev/null
+++ b/components/ARModel.js
@@ -0,0 +1,31 @@
+//
+// ARModel.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { NativeModules } from 'react-native';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARModel = createArComponent(
+ {
+ mount: NativeModules.ARModelManager.mount,
+ },
+ {
+ model: PropTypes.shape({
+ file: PropTypes.string,
+ node: PropTypes.string,
+ scale: PropTypes.number,
+ alpha: PropTypes.number,
+ }),
+ material,
+ },
+ ['model'],
+);
+
+export default ARModel;
diff --git a/components/ARPlane.js b/components/ARPlane.js
new file mode 100644
index 00000000..10cbe90e
--- /dev/null
+++ b/components/ARPlane.js
@@ -0,0 +1,25 @@
+//
+// ARPlane.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARPlane = createArComponent('addPlane', {
+ shape: PropTypes.shape({
+ width: PropTypes.number,
+ height: PropTypes.number,
+ cornerRadius: PropTypes.number,
+ cornerSegmentCount: PropTypes.number,
+ widthSegmentCount: PropTypes.number,
+ heightSegmentCount: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARPlane;
diff --git a/components/ARPyramid.js b/components/ARPyramid.js
new file mode 100644
index 00000000..90f47acc
--- /dev/null
+++ b/components/ARPyramid.js
@@ -0,0 +1,22 @@
+//
+// ARPyramid.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARPyramid = createArComponent('addPyramid', {
+ shape: PropTypes.shape({
+ width: PropTypes.number,
+ length: PropTypes.number,
+ height: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARPyramid;
diff --git a/components/ARShape.js b/components/ARShape.js
new file mode 100644
index 00000000..91e951f9
--- /dev/null
+++ b/components/ARShape.js
@@ -0,0 +1,19 @@
+import PropTypes from 'prop-types';
+
+import { chamferMode, material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARShape = createArComponent('addShape', {
+ shape: PropTypes.shape({
+ extrusion: PropTypes.number,
+ pathSvg: PropTypes.string,
+ pathFlatness: PropTypes.number,
+ chamferMode,
+ chamferRadius: PropTypes.number,
+ chamferProfilePathSvg: PropTypes.string,
+ chamferProfilePathFlatness: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARShape;
diff --git a/components/ARSphere.js b/components/ARSphere.js
new file mode 100644
index 00000000..31e31269
--- /dev/null
+++ b/components/ARSphere.js
@@ -0,0 +1,20 @@
+//
+// ARSphere.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARSphere = createArComponent('addSphere', {
+ shape: PropTypes.shape({
+ radius: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARSphere;
diff --git a/components/ARSprite.js b/components/ARSprite.js
new file mode 100644
index 00000000..03e1029a
--- /dev/null
+++ b/components/ARSprite.js
@@ -0,0 +1,72 @@
+import {
+ requireNativeComponent,
+ NativeModules,
+ findNodeHandle,
+} from 'react-native';
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+
+import { position, transition } from './lib/propTypes';
+
+const { ARKitSpriteViewManager } = NativeModules;
+
+const DEFAULT_TRANSITION_DURATION = 0.05;
+const ARSprite = class extends Component {
+ startAnimation() {
+ if (findNodeHandle(this.nativeRef)) {
+ ARKitSpriteViewManager.startAnimation(findNodeHandle(this.nativeRef));
+ }
+ }
+ stopAnimation() {
+ if (findNodeHandle(this.nativeRef)) {
+ ARKitSpriteViewManager.stopAnimation(findNodeHandle(this.nativeRef));
+ }
+ }
+ componentDidUpdate(oldProps) {
+ if (oldProps.disablePositionUpdate !== this.props.disablePositionUpdate)
+ this.handleAnimation();
+ }
+ componentWillUnmount() {
+ this.stopAnimation();
+ }
+ handleAnimation() {
+ if (this.props.disablePositionUpdate) {
+ this.stopAnimation();
+ } else {
+ this.startAnimation();
+ }
+ }
+ render() {
+ const { position, transition, ...props } = this.props;
+
+ return (
+ {
+ if (ref && !this.nativeRef) {
+ this.nativeRef = ref;
+ this.handleAnimation();
+ }
+ }}
+ {...props}
+ position3D={position}
+ transitionDuration={
+ transition ? transition.duration : DEFAULT_TRANSITION_DURATION
+ }
+ style={{ position: 'absolute', ...this.props.style }}
+ />
+ );
+ }
+};
+
+const RCTARKitARSprite = requireNativeComponent('RCTARKitSpriteView', null);
+RCTARKitARSprite.propTypes = {
+ position3D: position,
+ transitionDuration: PropTypes.number,
+};
+ARSprite.propTypes = {
+ position,
+ transition,
+ disablePositionUpdate: PropTypes.bool,
+};
+
+export default ARSprite;
diff --git a/components/ARText.js b/components/ARText.js
new file mode 100644
index 00000000..207d0ab8
--- /dev/null
+++ b/components/ARText.js
@@ -0,0 +1,31 @@
+//
+// ARText.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { NativeModules } from 'react-native';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARText = createArComponent(
+ { mount: NativeModules.ARTextManager.mount, pick: ['id', 'text', 'font'] },
+ {
+ text: PropTypes.string,
+ font: PropTypes.shape({
+ name: PropTypes.string,
+ // weight: PropTypes.string,
+ size: PropTypes.number,
+ depth: PropTypes.number,
+ chamfer: PropTypes.number
+ }),
+ material
+ },
+ ['text', 'font']
+);
+
+export default ARText;
diff --git a/components/ARTorus.js b/components/ARTorus.js
new file mode 100644
index 00000000..07c926eb
--- /dev/null
+++ b/components/ARTorus.js
@@ -0,0 +1,21 @@
+//
+// ARTorus.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARTorus = createArComponent('addTorus', {
+ shape: PropTypes.shape({
+ ringR: PropTypes.number,
+ pipeR: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARTorus;
diff --git a/components/ARTube.js b/components/ARTube.js
new file mode 100644
index 00000000..ba203bb4
--- /dev/null
+++ b/components/ARTube.js
@@ -0,0 +1,22 @@
+//
+// ARTube.js
+//
+// Created by HippoAR on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+import PropTypes from 'prop-types';
+
+import { material } from './lib/propTypes';
+import createArComponent from './lib/createArComponent';
+
+const ARTube = createArComponent('addTube', {
+ shape: PropTypes.shape({
+ innerR: PropTypes.number,
+ outerR: PropTypes.number,
+ height: PropTypes.number,
+ }),
+ material,
+});
+
+export default ARTube;
diff --git a/components/lib/addAnimatedSupport.js b/components/lib/addAnimatedSupport.js
new file mode 100644
index 00000000..2dbf4c65
--- /dev/null
+++ b/components/lib/addAnimatedSupport.js
@@ -0,0 +1,44 @@
+import { Animated } from 'react-native';
+import React from 'react';
+import get from 'lodash/get';
+import unset from 'lodash/unset';
+
+export default ARComponent => {
+ // there is a strange issue: animatedvalues can't be child objects,
+ // so we have to flatten them
+ // we would need to have an Animated.ValueXYZ
+ const ANIMATEABLE3D = ['position', 'eulerAngles'];
+
+ const ARComponentAnimatedInner = Animated.createAnimatedComponent(
+ class extends React.PureComponent {
+ render() {
+ // unflatten
+ const unflattened = {};
+ const props = { ...this.props };
+ ANIMATEABLE3D.forEach(key => {
+ unflattened[key] = {
+ x: get(props, `_flattened_${key}_x`),
+ y: get(props, `_flattened_${key}_y`),
+ z: get(props, `_flattened_${key}_z`),
+ };
+ unset(props, `_flattened_${key}_x`);
+ unset(props, `_flattened_${key}_y`);
+ unset(props, `_flattened_${key}_z`);
+ });
+ return ;
+ }
+ },
+ );
+
+ return props => {
+ const flattenedProps = {};
+
+ ANIMATEABLE3D.forEach(key => {
+ flattenedProps[`_flattened_${key}_x`] = get(props, [key, 'x']);
+ flattenedProps[`_flattened_${key}_y`] = get(props, [key, 'y']);
+ flattenedProps[`_flattened_${key}_z`] = get(props, [key, 'z']);
+ });
+
+ return ;
+ };
+};
diff --git a/components/lib/createArComponent.js b/components/lib/createArComponent.js
new file mode 100644
index 00000000..21552962
--- /dev/null
+++ b/components/lib/createArComponent.js
@@ -0,0 +1,256 @@
+import { NativeModules, processColor } from 'react-native';
+import PropTypes from 'prop-types';
+import React, { PureComponent, Fragment } from 'react';
+import filter from 'lodash/filter';
+import isDeepEqual from 'fast-deep-equal';
+import isEmpty from 'lodash/isEmpty';
+import keys from 'lodash/keys';
+import pick from 'lodash/pick';
+import some from 'lodash/some';
+
+import {
+ castsShadow,
+ categoryBitMask,
+ eulerAngles,
+ opacity,
+ orientation,
+ position,
+ renderingOrder,
+ rotation,
+ scale,
+ constraint,
+ transition,
+} from './propTypes';
+import addAnimatedSupport from './addAnimatedSupport';
+import generateId from './generateId';
+import processMaterial from './processMaterial';
+
+const DEBUG = false;
+
+const { ARGeosManager } = NativeModules;
+
+const PROP_TYPES_IMMUTABLE = {
+ id: PropTypes.string,
+ frame: PropTypes.string,
+};
+const MOUNT_UNMOUNT_ANIMATION_PROPS = {
+ propsOnMount: PropTypes.any,
+ propsOnUnMount: PropTypes.any,
+};
+const PROP_TYPES_NODE = {
+ position,
+ transition,
+ orientation,
+ eulerAngles,
+ rotation,
+ scale,
+ categoryBitMask,
+ castsShadow,
+ renderingOrder,
+ opacity,
+ constraint,
+};
+
+const NODE_PROPS = keys(PROP_TYPES_NODE);
+const IMMUTABLE_PROPS = keys(PROP_TYPES_IMMUTABLE);
+
+const TIMERS = {};
+
+/**
+mountConfig,
+propTypes,
+nonUpdateablePropKeys: if a prop key is in this list,
+the property will be updated on scenekit, instead of beeing remounted.
+
+this excludes at the moment: model, font, text, (???)
+* */
+export default (mountConfig, propTypes = {}, nonUpdateablePropKeys = []) => {
+ const allPropTypes = {
+ ...MOUNT_UNMOUNT_ANIMATION_PROPS,
+ ...PROP_TYPES_IMMUTABLE,
+ ...PROP_TYPES_NODE,
+ ...propTypes,
+ };
+ // any custom props (material, shape, ...)
+ const nonNodePropKeys = keys(propTypes);
+
+ const parseMaterials = props => ({
+ ...props,
+ ...(props.shadowColor
+ ? { shadowColor: processColor(props.shadowColor) }
+ : {}),
+ ...(props.color ? { color: processColor(props.color) } : {}),
+ ...(props.material ? { material: processMaterial(props.material) } : {}),
+ });
+
+ const getNonNodeProps = props => parseMaterials(pick(props, nonNodePropKeys));
+
+ const mountFunc =
+ typeof mountConfig === 'string'
+ ? ARGeosManager[mountConfig]
+ : mountConfig.mount;
+
+ const mount = (id, props, parentId) => {
+ if (DEBUG) console.log(`[${id}] [${new Date().getTime()}] mount`, props);
+ return mountFunc(
+ getNonNodeProps(props),
+ {
+ id,
+ ...pick(props, NODE_PROPS),
+ },
+ props.frame,
+ parentId,
+ );
+ };
+
+ const update = (id, props) => {
+ if (DEBUG) console.log(`[${id}] [${new Date().getTime()}] update`, props);
+ return ARGeosManager.updateNode(id, props);
+ };
+
+ const unmount = id => {
+ if (DEBUG) console.log(`[${id}] [${new Date().getTime()}] unmount`);
+ return ARGeosManager.unmount(id);
+ };
+
+ const ARComponent = class extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.identifier = props.id || generateId();
+ }
+ identifier = null;
+ componentDidMount() {
+ const { propsOnMount, ...props } = this.props;
+ if (propsOnMount) {
+ const fullPropsOnMount = { ...props, ...propsOnMount };
+ const {
+ transition: transitionOnMount = { duration: 0 },
+ } = fullPropsOnMount;
+
+ this.doPendingTimers();
+ this.mountWithProps(fullPropsOnMount).then(() => {
+ this.props = propsOnMount;
+ this.componentWillUpdate({ ...props, transition: transitionOnMount });
+ });
+ } else {
+ this.mountWithProps(props);
+ }
+ }
+
+ async mountWithProps(props) {
+ return mount(this.identifier, props, this.context.arkitParentId);
+ }
+
+ componentWillUpdate(props) {
+ const changedKeys = filter(
+ keys(this.props),
+ key => key !== 'children' && !isDeepEqual(props[key], this.props[key]),
+ );
+ if (isEmpty(changedKeys)) {
+ return;
+ }
+
+ if (__DEV__) {
+ const nonAllowedUpdates = filter(changedKeys, k =>
+ IMMUTABLE_PROPS.includes(k),
+ );
+ if (nonAllowedUpdates.length > 0) {
+ throw new Error(
+ `[${this
+ .identifier}] prop can't be updated: '${nonAllowedUpdates.join(
+ ', ',
+ )}'`,
+ );
+ }
+ }
+
+ if (some(changedKeys, k => nonUpdateablePropKeys.includes(k))) {
+ if (DEBUG)
+ console.log(
+ `[${this.identifier}] need to remount node because of `,
+ changedKeys,
+ );
+ this.mountWithProps({ ...this.props, ...props });
+ } else {
+ // every property is updateable
+ // send only these changed property to the native part
+
+ const propsToupdate = {
+ // always inclue transition
+ transition: {
+ ...this.props.transition,
+ ...props.transition,
+ },
+ ...parseMaterials(pick(props, changedKeys)),
+ };
+ update(this.identifier, propsToupdate).catch(() => {
+ // sometimes calls are out of order, so this node has been unmounted
+ // we therefore mount again
+
+ this.mountWithProps({ ...this.props, ...props });
+ });
+ }
+ }
+
+ componentWillUnmount() {
+ const { propsOnUnmount, ...props } = this.props;
+ if (propsOnUnmount) {
+ const fullProps = { ...props, ...propsOnUnmount };
+ const { transition: { duration = 0 } = {} } = fullProps;
+
+ this.componentWillUpdate(fullProps);
+ this.delayed(() => {
+ unmount(this.identifier);
+ }, duration * 1000);
+ } else {
+ this.doPendingTimers();
+ unmount(this.identifier);
+ }
+ }
+ /**
+ do something delayed, but keep order of events per id
+ * */
+ delayed(callback, duration) {
+ this.doPendingTimers();
+ TIMERS[this.identifier] = {
+ handle: global.setTimeout(() => {
+ callback.call(this);
+ delete TIMERS[this.identifier];
+ }, duration),
+ callback,
+ };
+ }
+
+ doPendingTimers() {
+ if (TIMERS[this.identifier]) {
+ // timer is running, do it now, otherwise we might change order
+ // e.g. it could be that an unmount happens after a remount
+ global.clearTimeout(TIMERS[this.identifier].handle);
+ TIMERS[this.identifier].callback.call(this);
+ delete TIMERS[this.identifier];
+ }
+ }
+ getChildContext() {
+ return {
+ arkitParentId: this.identifier,
+ };
+ }
+
+ render() {
+ return this.props.children ? (
+ {this.props.children}
+ ) : null;
+ }
+ };
+ ARComponent.childContextTypes = {
+ arkitParentId: PropTypes.string,
+ };
+ ARComponent.contextTypes = {
+ arkitParentId: PropTypes.string,
+ };
+
+ const ARComponentAnimated = addAnimatedSupport(ARComponent);
+ ARComponentAnimated.propTypes = allPropTypes;
+
+ return ARComponentAnimated;
+};
diff --git a/components/lib/generateId.js b/components/lib/generateId.js
new file mode 100644
index 00000000..50956e66
--- /dev/null
+++ b/components/lib/generateId.js
@@ -0,0 +1,71 @@
+// Unique id generator
+
+const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+function toSex(num, base) {
+ /* eslint no-param-reassign:0 */
+ if (base) {
+ num = parseInt(num, base);
+ }
+ let [integer, decimal] = num.toString().split('.');
+
+ integer = parseInt(integer, 10);
+ const sex = [];
+ do {
+ sex.push(digits[integer % 60]);
+ integer = Math.floor(integer / 60);
+ } while (integer > 0);
+
+ let result = sex.reverse().join('');
+
+ if (decimal) {
+ decimal = parseFloat(`.${decimal}`);
+ const rem = [];
+ let precision = 4;
+ do {
+ decimal *= 60;
+ const x = Math.floor(decimal);
+ rem.push(digits[x]);
+ decimal -= x;
+ precision--;
+ } while (precision);
+
+ result += `.${rem.join('')}`;
+ }
+
+ return result;
+}
+
+function toDigits(str, n) {
+ if (str.length === n) {
+ return str;
+ }
+
+ if (str.length > n) {
+ return str.substr(0, n);
+ }
+
+ do {
+ str = `0${str}`;
+ } while (str.length < n);
+
+ return str;
+}
+
+let random = Math.floor(Math.random() * 60 * 60 * 60 * 60);
+
+// export default function() {
+// const firstEightDigits = Math.random().toString(36).substr(2, 8);
+// const secondEightDigits = Math.random().toString(36).substr(2, 8);
+// return `${firstEightDigits}${secondEightDigits}`;
+// }
+
+export default function(idfv) {
+ random++;
+ const time = toSex(Math.floor(Date.now() / 1000));
+ const device = idfv ? toSex(idfv.split('-')[4], 16) : '';
+ return `${toDigits(time, 6)}${device && toDigits(device, 10)}${toDigits(
+ toSex(random),
+ 4,
+ )}`;
+}
diff --git a/components/lib/processMaterial.js b/components/lib/processMaterial.js
new file mode 100644
index 00000000..0790c7dd
--- /dev/null
+++ b/components/lib/processMaterial.js
@@ -0,0 +1,27 @@
+import { processColor } from 'react-native';
+import { isObject, isString, mapValues, set } from 'lodash';
+
+// https://developer.apple.com/documentation/scenekit/scnmaterial
+const propsWithMaps = ['normal', 'diffuse', 'displacement', 'specular'];
+
+export default function processMaterial(materialOrg) {
+ const material = { ...materialOrg };
+ // previously it was possible to set { material: { color:'colorstring'}}... translate this to { material: { diffuse: { color: 'colorstring'}}}
+ if (material.color) {
+ set(material, 'diffuse.color', material.color);
+ }
+
+ return mapValues(
+ material,
+ (prop, key) =>
+ propsWithMaps.includes(key)
+ ? {
+ ...(isObject(prop) ? prop : {}),
+ color: processColor(
+ // allow for setting a diffuse colorstring { diffuse: 'colorstring'}
+ key === 'diffuse' && isString(prop) ? prop : prop.color,
+ ),
+ }
+ : prop,
+ );
+}
diff --git a/components/lib/propTypes.js b/components/lib/propTypes.js
new file mode 100644
index 00000000..b16127de
--- /dev/null
+++ b/components/lib/propTypes.js
@@ -0,0 +1,152 @@
+import { NativeModules } from 'react-native';
+import { values } from 'lodash';
+import PropTypes from 'prop-types';
+
+const ARKitManager = NativeModules.ARKitManager;
+
+const animatableNumber = PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.object,
+]);
+
+export const deprecated = (propType, hint = null) => (
+ props,
+ propName,
+ componentName,
+) => {
+ if (props[propName]) {
+ console.warn(
+ `Prop \`${propName}\` supplied to` +
+ ` \`${componentName}\` is deprecated. ${hint}`,
+ );
+ }
+ return PropTypes.checkPropTypes(
+ { [propName]: propType },
+ props,
+ propName,
+ componentName,
+ );
+};
+export const position = PropTypes.shape({
+ x: animatableNumber,
+ y: animatableNumber,
+ z: animatableNumber,
+});
+
+export const scale = animatableNumber;
+export const categoryBitMask = PropTypes.number;
+export const transition = PropTypes.shape({
+ duration: PropTypes.number,
+});
+
+export const planeDetection = PropTypes.oneOf(
+ values(ARKitManager.ARPlaneDetection),
+);
+export const eulerAngles = PropTypes.shape({
+ x: animatableNumber,
+ y: animatableNumber,
+ z: animatableNumber,
+});
+
+export const rotation = PropTypes.shape({
+ x: animatableNumber,
+ y: animatableNumber,
+ z: animatableNumber,
+ w: animatableNumber,
+});
+
+export const orientation = PropTypes.shape({
+ x: animatableNumber,
+ y: animatableNumber,
+ z: animatableNumber,
+ w: animatableNumber,
+});
+
+export const textureTranslation = PropTypes.shape({
+ x: PropTypes.number,
+ y: PropTypes.number,
+ z: PropTypes.number,
+});
+
+export const textureRotation = PropTypes.shape({
+ angle: PropTypes.number,
+ x: PropTypes.number,
+ y: PropTypes.number,
+ z: PropTypes.number,
+});
+
+export const textureScale = PropTypes.shape({
+ x: PropTypes.number,
+ y: PropTypes.number,
+ z: PropTypes.number,
+});
+
+export const shaders = PropTypes.shape({
+ [ARKitManager.ShaderModifierEntryPoint.Geometry]: PropTypes.string,
+ [ARKitManager.ShaderModifierEntryPoint.Surface]: PropTypes.string,
+ [ARKitManager.ShaderModifierEntryPoint.LightingModel]: PropTypes.string,
+ [ARKitManager.ShaderModifierEntryPoint.Fragment]: PropTypes.string,
+});
+
+export const lightingModel = PropTypes.oneOf(
+ values(ARKitManager.LightingModel),
+);
+
+export const castsShadow = PropTypes.bool;
+export const renderingOrder = PropTypes.number;
+export const blendMode = PropTypes.oneOf(values(ARKitManager.BlendMode));
+export const transparencyMode = PropTypes.oneOf(
+ values(ARKitManager.TransparencyMode),
+);
+export const chamferMode = PropTypes.oneOf(values(ARKitManager.ChamferMode));
+export const color = PropTypes.string;
+export const fillMode = PropTypes.oneOf(values(ARKitManager.FillMode));
+
+export const lightType = PropTypes.oneOf(values(ARKitManager.LightType));
+export const shadowMode = PropTypes.oneOf(values(ARKitManager.ShadowMode));
+export const colorBufferWriteMask = PropTypes.oneOf(
+ values(ARKitManager.ColorMask),
+);
+
+export const opacity = animatableNumber;
+
+export const constraint = PropTypes.oneOf(values(ARKitManager.Constraint));
+
+export const wrapMode = PropTypes.oneOf(values(ARKitManager.WrapMode));
+
+export const materialProperty = PropTypes.shape({
+ path: PropTypes.string,
+ color: PropTypes.string,
+ intensity: PropTypes.number,
+ wrapS: wrapMode,
+ wrapT: wrapMode,
+ wrap: wrapMode,
+ translation: textureTranslation,
+ scale: textureScale,
+ rotation: textureRotation,
+});
+
+export const material = PropTypes.shape({
+ color,
+ normal: materialProperty,
+ specular: materialProperty,
+ displacement: materialProperty,
+ diffuse: PropTypes.oneOfType([PropTypes.string, materialProperty]),
+ metalness: PropTypes.number,
+ roughness: PropTypes.number,
+ blendMode,
+ transparencyMode,
+ lightingModel,
+ shaders,
+ writesToDepthBuffer: PropTypes.bool,
+ colorBufferWriteMask,
+ doubleSided: PropTypes.bool,
+ litPerPixel: PropTypes.bool,
+ transparency: PropTypes.number,
+ fillMode,
+});
+
+const detectionImage = PropTypes.shape({
+ resourceGroupName: PropTypes.string,
+});
+export const detectionImages = PropTypes.arrayOf(detectionImage);
diff --git a/examples/animation.md b/examples/animation.md
new file mode 100644
index 00000000..a500204c
--- /dev/null
+++ b/examples/animation.md
@@ -0,0 +1,6 @@
+# DRAFT Animations
+
+- react-native Animated library
+- propsOnMount / propsOnUnmount
+- scenekit transitions
+- animated models
diff --git a/examples/measure-distance.md b/examples/measure-distance.md
new file mode 100644
index 00000000..c97ce57c
--- /dev/null
+++ b/examples/measure-distance.md
@@ -0,0 +1,6 @@
+# DRAFT Measure distance
+
+- geometry, pythagoras, vectors
+- hitTestPlanes with features
+- hitTestPlanes with planes
+- hitTestSceneObjects
diff --git a/examples/models.md b/examples/models.md
new file mode 100644
index 00000000..5f3d36db
--- /dev/null
+++ b/examples/models.md
@@ -0,0 +1,4 @@
+# DRAFT loading models
+
+- loading from network
+- convertion with xcode
diff --git a/hocs/withProjectedPosition.js b/hocs/withProjectedPosition.js
new file mode 100644
index 00000000..cc7938c3
--- /dev/null
+++ b/hocs/withProjectedPosition.js
@@ -0,0 +1,105 @@
+import React, { Component } from 'react';
+import withAnimationFrame from '@panter/react-animation-frame';
+import { round, isFunction } from 'lodash';
+import { NativeModules } from 'react-native';
+
+const ARKitManager = NativeModules.ARKitManager;
+
+const rountPosition = ({ x, y, z }, precision) => ({
+ x: round(x, precision),
+ y: round(y, precision),
+ z: round(z, precision),
+});
+
+export default ({ throttleMs = 33, overwritePosition = {} } = {}) => C =>
+ withAnimationFrame(
+ class extends Component {
+ projectionRunning = true;
+ _isMounted = false;
+ constructor(props) {
+ super(props);
+ this.state = {
+ positionProjected: props.position || { x: 0, y: 0, z: 0 },
+ projectionResult: null,
+ };
+ this.handleAnimation(props);
+ }
+ componentWillMount() {
+ this._isMounted = true;
+ }
+ handleAnimation({ projectionEnabled }) {
+ if (projectionEnabled) {
+ if (!this.projectionRunning) {
+ this.projectionRunning = true;
+ this.props.startAnimation();
+ }
+ } else if (this.projectionRunning) {
+ this.projectionRunning = false;
+ this.props.endAnimation();
+ }
+ }
+ componentWillUpdate({ projectionEnabled = true }) {
+ this.handleAnimation({ projectionEnabled });
+ }
+
+ componentWillUnmount() {
+ this._isMounted = false;
+ }
+
+ onResult(result) {
+ if (this._isMounted) {
+ if (result) {
+ this.setState({
+ positionProjected: rountPosition(result.position, 3),
+ projectionResult: result,
+ });
+ } else {
+ this.setState({
+ positionProjected: null,
+ projectionResult: null,
+ });
+ }
+ if (this.props.onProjectedPosition) {
+ this.props.onProjectedPosition(result);
+ }
+ }
+ }
+
+ onAnimationFrame() {
+ const { x, y, plane, node } = this.props.projectPosition || {};
+
+ if (plane) {
+ ARKitManager.hitTestPlanes(
+ { x, y },
+ ARKitManager.ARHitTestResultType.ExistingPlane,
+ ).then(({ results }) => {
+ const result = isFunction(plane)
+ ? plane(results)
+ : results.find(r => r.id === plane);
+ this.onResult(result);
+ });
+ } else if (node) {
+ ARKitManager.hitTestSceneObjects({ x, y }).then(({ results }) => {
+ const result = isFunction(node)
+ ? node(results)
+ : results.find(r => r.id === node);
+ this.onResult(result);
+ });
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ },
+ throttleMs,
+ );
diff --git a/index.js b/index.js
index a7188e15..0bdb4b4d 100644
--- a/index.js
+++ b/index.js
@@ -5,43 +5,63 @@
// Copyright © 2017 HippoAR. All rights reserved.
//
-import PropTypes from 'prop-types';
-import React from 'react';
-import { NativeModules, requireNativeComponent } from 'react-native';
+import ARBox from './components/ARBox';
+import ARCapsule from './components/ARCapsule';
+import ARCone from './components/ARCone';
+import ARCylinder from './components/ARCylinder';
+import ARGroup from './components/ARGroup';
+import ARKit from './ARKit';
+import ARLight from './components/ARLight';
+import ARModel from './components/ARModel';
+import ARPlane from './components/ARPlane';
+import ARPyramid from './components/ARPyramid';
+import ARShape from './components/ARShape';
+import ARSphere from './components/ARSphere';
+import ARSprite from './components/ARSprite';
+import ARText from './components/ARText';
+import ARTorus from './components/ARTorus';
+import ARTube from './components/ARTube';
+import DeviceMotion from './DeviceMotion';
+import startup from './startup';
+import withProjectedPosition from './hocs/withProjectedPosition';
-const ARKitManager = NativeModules.ARKitManager;
+import * as colorUtils from './lib/colorUtils';
-class ARKit extends React.Component {
- render() {
- return ;
- }
+ARKit.Box = ARBox;
+ARKit.Sphere = ARSphere;
+ARKit.Cylinder = ARCylinder;
+ARKit.Cone = ARCone;
+ARKit.Pyramid = ARPyramid;
+ARKit.Tube = ARTube;
+ARKit.Torus = ARTorus;
+ARKit.Capsule = ARCapsule;
+ARKit.Plane = ARPlane;
+ARKit.Text = ARText;
+ARKit.Model = ARModel;
+ARKit.Sprite = ARSprite;
+ARKit.Group = ARGroup;
+ARKit.Shape = ARShape;
+ARKit.Light = ARLight;
- getCameraPosition() {
- return ARKitManager.getCameraPosition();
- }
+startup();
- callback(name) {
- return event => {
- if (!this.props[name]) {
- return;
- }
- this.props[name](event.nativeEvent);
- };
- }
-}
-
-ARKit.propTypes = {
- debug: PropTypes.bool,
- planeDetection: PropTypes.bool,
- lightEstimation: PropTypes.bool,
- onPlaneDetected: PropTypes.func,
- onPlaneUpdate: PropTypes.func,
+export {
+ colorUtils,
+ ARKit,
+ DeviceMotion,
+ ARBox,
+ ARSphere,
+ ARSprite,
+ ARCylinder,
+ ARCone,
+ ARPyramid,
+ ARTube,
+ ARTorus,
+ ARCapsule,
+ ARPlane,
+ ARText,
+ ARModel,
+ ARLight,
+ ARGroup,
+ withProjectedPosition,
};
-
-const RCTARKit = requireNativeComponent('RCTARKit', ARKit);
-
-module.exports = ARKit;
diff --git a/ios/DeviceMotion.h b/ios/DeviceMotion.h
new file mode 100644
index 00000000..8951ecd6
--- /dev/null
+++ b/ios/DeviceMotion.h
@@ -0,0 +1,23 @@
+//
+// DeviceMotion.h
+// RCTARKit
+//
+// Created by Zehao Li on 7/29/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+#import
+#import
+
+@interface DeviceMotion : RCTEventEmitter
+
+@property (nonatomic) CMMotionManager *motionManager;
+
+- (void)setUpdateInterval:(double) interval;
+- (void)getUpdateInterval:(RCTResponseSenderBlock) cb;
+- (void)getData:(RCTResponseSenderBlock) cb;
+- (void)startUpdates;
+- (void)stopUpdates;
+
+@end
diff --git a/ios/DeviceMotion.m b/ios/DeviceMotion.m
new file mode 100644
index 00000000..4b49b398
--- /dev/null
+++ b/ios/DeviceMotion.m
@@ -0,0 +1,103 @@
+//
+// DeviceMotion.m
+// RCTARKit
+//
+// Created by Zehao Li on 7/29/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+#import
+#import "DeviceMotion.h"
+
+@implementation DeviceMotion
+
+@synthesize bridge = _bridge;
+
+RCT_EXPORT_MODULE();
+
+- (id)init {
+ self = [super init];
+ return self;
+}
+
+
++ (BOOL)requiresMainQueueSetup
+{
+ return YES;
+}
+
+- (CMMotionManager *)motionManager {
+ if (_motionManager) {
+ return _motionManager;
+ }
+
+ _motionManager = [[CMMotionManager alloc] init];
+ if (![_motionManager isDeviceMotionAvailable]) {
+ NSLog(@"DeviceMotion not Available!");
+ return _motionManager;
+ }
+
+ NSLog(@"DeviceMotion available");
+ if ([_motionManager isDeviceMotionActive] == NO) {
+ NSLog(@"DeviceMotion active");
+ } else {
+ NSLog(@"DeviceMotion not active");
+ }
+ return _motionManager;
+}
+
+- (NSArray *)supportedEvents {
+ return @[@"MotionData"];
+}
+
+RCT_EXPORT_METHOD(setUpdateInterval:(double) interval) {
+ NSLog(@"setDeviceMotionUpdateInterval: %f", interval);
+ [self.motionManager setDeviceMotionUpdateInterval:interval];
+}
+
+RCT_EXPORT_METHOD(getUpdateInterval:(RCTResponseSenderBlock) cb) {
+ double interval = self.motionManager.deviceMotionUpdateInterval;
+ NSLog(@"getDeviceMotionUpdateInterval: %f", interval);
+ cb(@[[NSNull null], [NSNumber numberWithDouble:interval]]);
+}
+
+RCT_EXPORT_METHOD(getData:(RCTResponseSenderBlock) cb) {
+ CMAcceleration gravity = self.motionManager.deviceMotion.gravity;
+ CMRotationMatrix m = self.motionManager.deviceMotion.attitude.rotationMatrix;
+
+ cb(@[[NSNull null], @{
+ @"gravity": @{ @"x" : @(gravity.x), @"y" : @(gravity.y), @"z" : @(gravity.z) },
+ @"rotationMatrix": @{
+ @"m11" : @(m.m11), @"m12" : @(m.m12), @"m13" : @(m.m13),
+ @"m21" : @(m.m21), @"m22" : @(m.m22), @"m23" : @(m.m23),
+ @"m31" : @(m.m31), @"m32" : @(m.m32), @"m33" : @(m.m33) },
+ }]);
+}
+
+RCT_EXPORT_METHOD(startUpdates) {
+ NSLog(@"startDeviceMotionUpdates");
+ [self.motionManager startDeviceMotionUpdates];
+
+ /* Receive the DeviceMotion data on this block */
+ [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
+ withHandler:^(CMDeviceMotion *deviceMotion, NSError *error) {
+ CMAcceleration gravity = deviceMotion.gravity;
+ CMRotationMatrix m = deviceMotion.attitude.rotationMatrix;
+ [self sendEventWithName:@"MotionData"
+ body:@{
+ @"gravity": @{ @"x" : @(gravity.x), @"y" : @(gravity.y), @"z" : @(gravity.z) },
+ @"rotationMatrix": @{
+ @"m11" : @(m.m11), @"m12" : @(m.m12), @"m13" : @(m.m13),
+ @"m21" : @(m.m21), @"m22" : @(m.m22), @"m23" : @(m.m23),
+ @"m31" : @(m.m31), @"m32" : @(m.m32), @"m33" : @(m.m33) },
+ }];
+ }];
+}
+
+RCT_EXPORT_METHOD(stopUpdates) {
+ NSLog(@"stopDeviceMotionUpdates");
+ [self.motionManager stopDeviceMotionUpdates];
+}
+
+@end
diff --git a/ios/Plane.h b/ios/Plane.h
deleted file mode 100644
index 5fff9353..00000000
--- a/ios/Plane.h
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// Plane.h
-// RCTARKit
-//
-// Created by Zehao Li on 7/10/17.
-// Copyright © 2017 Facebook. All rights reserved.
-//
-
-#import
-#import
-
-@interface Plane : SCNNode
-- (instancetype)initWithAnchor:(ARPlaneAnchor *)anchor isHidden:(BOOL)hidden;
-- (void)update:(ARPlaneAnchor *)anchor;
-- (void)setTextureScale;
-- (void)hide;
-@property (nonatomic, retain) ARPlaneAnchor *anchor;
-@property (nonatomic, retain) SCNBox *planeGeometry;
-@end
diff --git a/ios/Plane.m b/ios/Plane.m
deleted file mode 100644
index b072dc92..00000000
--- a/ios/Plane.m
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-// Plane.m
-// RCTARKit
-//
-// Created by Zehao Li on 7/10/17.
-// Copyright © 2017 Facebook. All rights reserved.
-//
-
-#import "Plane.h"
-
-@implementation Plane
-
-- (instancetype)initWithAnchor:(ARPlaneAnchor *)anchor isHidden:(BOOL)hidden {
- self = [super init];
-
- self.anchor = anchor;
- float width = anchor.extent.x;
- float length = anchor.extent.z;
-
- float planeHeight = 0.01;
-
- self.planeGeometry = [SCNBox boxWithWidth:width height:planeHeight length:length chamferRadius:0];
-
- SCNMaterial *material = [SCNMaterial new];
- UIImage *img = [UIImage imageNamed:@"tron_grid.png"];
-
- material.diffuse.contents = img;
-
- SCNMaterial *transparentMaterial = [SCNMaterial new];
- transparentMaterial.diffuse.contents = [UIColor colorWithWhite:1.0 alpha:0.0];
-
- if (hidden) {
- self.planeGeometry.materials = @[transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial];
- } else {
- self.planeGeometry.materials = @[transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, material, transparentMaterial];
- }
-
- SCNNode *planeNode = [SCNNode nodeWithGeometry:self.planeGeometry];
-
- planeNode.position = SCNVector3Make(0, -planeHeight / 2, 0);
-
- planeNode.physicsBody = [SCNPhysicsBody
- bodyWithType:SCNPhysicsBodyTypeKinematic
- shape: [SCNPhysicsShape shapeWithGeometry:self.planeGeometry options:nil]];
-
- [self setTextureScale];
- [self addChildNode:planeNode];
- return self;
-}
-
-- (void)update:(ARPlaneAnchor *)anchor {
- self.planeGeometry.width = anchor.extent.x;
- self.planeGeometry.length = anchor.extent.z;
-
- self.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z);
-
- SCNNode *node = [self.childNodes firstObject];
- //self.physicsBody = nil;
- node.physicsBody = [SCNPhysicsBody
- bodyWithType:SCNPhysicsBodyTypeKinematic
- shape: [SCNPhysicsShape shapeWithGeometry:self.planeGeometry options:nil]];
- [self setTextureScale];
-}
-
-- (void)setTextureScale {
- CGFloat width = self.planeGeometry.width;
- CGFloat height = self.planeGeometry.length;
-
- SCNMaterial *material = self.planeGeometry.materials[4];
- material.diffuse.contentsTransform = SCNMatrix4MakeScale(width, height, 1);
- material.diffuse.wrapS = SCNWrapModeRepeat;
- material.diffuse.wrapT = SCNWrapModeRepeat;
-}
-
-- (void)hide {
- SCNMaterial *transparentMaterial = [SCNMaterial new];
- transparentMaterial.diffuse.contents = [UIColor colorWithWhite:1.0 alpha:0.0];
- self.planeGeometry.materials = @[transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial];
-}
-
-@end
diff --git a/ios/PocketSVG b/ios/PocketSVG
new file mode 160000
index 00000000..5405d0f5
--- /dev/null
+++ b/ios/PocketSVG
@@ -0,0 +1 @@
+Subproject commit 5405d0f5838c1242e79b490c6ddf738779e4d5c0
diff --git a/ios/RCTARKit.h b/ios/RCTARKit.h
index 6479df94..de5c7819 100644
--- a/ios/RCTARKit.h
+++ b/ios/RCTARKit.h
@@ -10,21 +10,98 @@
#import
#import
-#import
+#import "RCTARKitDelegate.h"
+#import "RCTARKitNodes.h"
-@interface RCTARKit : ARSCNView
+typedef void (^RCTBubblingEventBlock)(NSDictionary *body);
+typedef void (^RCTARKitResolve)(id result);
+typedef void (^RCTARKitReject)(NSString *code, NSString *message, NSError *error);
+
+
+@interface RCTARKit : UIView
+
++ (instancetype)sharedInstance;
++ (bool)isInitialized;
+- (instancetype)initWithARView:(ARSCNView *)arView;
+
+
+@property (nonatomic, strong) NSMutableArray> *touchDelegates;
+@property (nonatomic, strong) NSMutableArray> *rendererDelegates;
+@property (nonatomic, strong) NSMutableArray> *sessionDelegates;
+
+
+#pragma mark - Properties
+@property (nonatomic, strong) ARSCNView *arView;
+@property (nonatomic, strong) RCTARKitNodes *nodeManager;
@property (nonatomic, assign) BOOL debug;
-@property (nonatomic, assign) BOOL planeDetection;
-@property (nonatomic, assign) BOOL lightEstimation;
-@property (nonatomic, readonly) NSDictionary *cameraPosition;
+@property (nonatomic, assign) ARPlaneDetection planeDetection;
+@property (nonatomic, assign) BOOL lightEstimationEnabled;
+@property (nonatomic, assign) BOOL autoenablesDefaultLighting;
+@property (nonatomic, assign) NSDictionary* origin;
+@property (nonatomic, assign) ARWorldAlignment worldAlignment;
+@property (nonatomic, assign) NSArray* detectionImages;
@property (nonatomic, copy) RCTBubblingEventBlock onPlaneDetected;
-@property (nonatomic, copy) RCTBubblingEventBlock onPlaneUpdate;
+@property (nonatomic, copy) RCTBubblingEventBlock onPlaneRemoved;
+@property (nonatomic, copy) RCTBubblingEventBlock onPlaneUpdated;
-@property NSMutableDictionary *planes;
-@property NSMutableArray *boxes;
+@property (nonatomic, copy) RCTBubblingEventBlock onAnchorDetected;
+@property (nonatomic, copy) RCTBubblingEventBlock onAnchorRemoved;
+@property (nonatomic, copy) RCTBubblingEventBlock onAnchorUpdated;
-+ (instancetype)sharedInstance;
+
+@property (nonatomic, copy) RCTBubblingEventBlock onFeaturesDetected;
+@property (nonatomic, copy) RCTBubblingEventBlock onLightEstimation;
+
+@property (nonatomic, copy) RCTBubblingEventBlock onTrackingState;
+@property (nonatomic, copy) RCTBubblingEventBlock onTapOnPlaneUsingExtent;
+@property (nonatomic, copy) RCTBubblingEventBlock onTapOnPlaneNoExtent;
+@property (nonatomic, copy) RCTBubblingEventBlock onEvent;
+@property (nonatomic, copy) RCTBubblingEventBlock onARKitError;
+
+
+@property NSMutableDictionary *planes; // plane detected
+
+
+
+#pragma mark - Public Method
+- (void)pause;
+- (void)resume;
+- (void)reset;
+- (void)hitTestPlane:(CGPoint)tapPoint types:(ARHitTestResultType)types resolve:(RCTARKitResolve)resolve reject:(RCTARKitReject)reject;
+- (void)hitTestSceneObjects:(CGPoint)tapPoint resolve:(RCTARKitResolve) resolve reject:(RCTARKitReject)reject;
+- (SCNVector3)projectPoint:(SCNVector3)point;
+- (float)getCameraDistanceToPoint:(SCNVector3)point;
+- (UIImage *)getSnapshot:(NSDictionary*)selection;
+- (UIImage *)getSnapshotCamera:(NSDictionary*)selection;
+- (void)focusScene;
+- (void)clearScene;
+- (NSDictionary *)readCameraPosition;
+- (NSDictionary *)readCamera;
+- (NSDictionary* )getCurrentLightEstimation;
+- (NSArray * )getCurrentDetectedFeaturePoints;
+- (bool)isMounted;
+- (void)addRendererDelegates:(id)delegate;
+- (void)removeRendererDelegates:(id)delegate;
+
+#pragma mark - Delegates
+- (void)renderer:(id )renderer didRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)time;
+- (void)renderer:(id )renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor;
+- (void)renderer:(id )renderer willUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor;
+- (void)renderer:(id )renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor;
+- (void)renderer:(id )renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor;
+
+- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame;
+- (void)session:(ARSession *)session cameraDidChangeTrackingState:(ARCamera *)camera;
@end
+
+
+
+#if __has_include("RCTARKitARCL.h")
+#import "RCTARKitARCL.h"
+@compatibility_alias ARKit RCTARKitARCL;
+#else
+@compatibility_alias ARKit RCTARKit;
+#endif
diff --git a/ios/RCTARKit.m b/ios/RCTARKit.m
index 8a4e5be1..36105fe1 100644
--- a/ios/RCTARKit.m
+++ b/ios/RCTARKit.m
@@ -7,188 +7,740 @@
//
#import "RCTARKit.h"
-#import "Plane.h"
+#import "RCTConvert+ARKit.h"
-@interface RCTARKit ()
+@import CoreLocation;
-@property (nonatomic, strong) ARWorldTrackingSessionConfiguration *configuration;
+@interface RCTARKit () {
+ RCTARKitResolve _resolve;
+}
+
+@property (nonatomic, strong) ARSession* session;
+@property (nonatomic, strong) ARWorldTrackingConfiguration *configuration;
@end
+void dispatch_once_on_main_thread(dispatch_once_t *predicate,
+ dispatch_block_t block) {
+ if ([NSThread isMainThread]) {
+ dispatch_once(predicate, block);
+ } else {
+ if (DISPATCH_EXPECT(*predicate == 0L, NO)) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ dispatch_once(predicate, block);
+ });
+ }
+ }
+}
+
+
@implementation RCTARKit
+static RCTARKit *instance = nil;
+
++ (bool)isInitialized {
+ return instance !=nil;
+}
+ (instancetype)sharedInstance {
- static RCTARKit *arView = nil;
+
static dispatch_once_t onceToken;
-
- dispatch_once(&onceToken, ^{
- if (arView == nil) {
- arView = [[self alloc] init];
+
+ dispatch_once_on_main_thread(&onceToken, ^{
+ if (instance == nil) {
+ ARSCNView *arView = [[ARSCNView alloc] init];
+ instance = [[self alloc] initWithARView:arView];
}
});
+
+ return instance;
+}
- return arView;
+- (bool)isMounted {
+
+ return self.superview != nil;
}
-- (instancetype)init {
+- (instancetype)initWithARView:(ARSCNView *)arView {
if ((self = [super init])) {
- self.delegate = self;
- [self.session runWithConfiguration:self.configuration];
+ self.arView = arView;
+
+ // delegates
+ arView.delegate = self;
+ arView.session.delegate = self;
+
+ UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapFrom:)];
+ tapGestureRecognizer.numberOfTapsRequired = 1;
+ [self.arView addGestureRecognizer:tapGestureRecognizer];
+
+ self.touchDelegates = [NSMutableArray array];
+ self.rendererDelegates = [NSMutableArray array];
+ self.sessionDelegates = [NSMutableArray array];
+
+ // nodeManager
+ self.nodeManager = [RCTARKitNodes sharedInstance];
+ self.nodeManager.arView = arView;
+ [self.sessionDelegates addObject:self.nodeManager];
+
+ // configuration(s)
+ arView.autoenablesDefaultLighting = YES;
+ arView.scene.rootNode.name = @"root";
+
+ #if TARGET_IPHONE_SIMULATOR
+ // allow for basic orbit gestures if we're running in the simulator
+ arView.allowsCameraControl = YES;
+ arView.defaultCameraController.interactionMode = SCNInteractionModeOrbitTurntable;
+ arView.defaultCameraController.maximumVerticalAngle = 45;
+ arView.defaultCameraController.inertiaEnabled = YES;
+ [arView.defaultCameraController translateInCameraSpaceByX:(float) 0.0 Y:(float) 0.0 Z:(float) 3.0];
+
+ #endif
+ // start ARKit
+ [self addSubview:arView];
+ [self resume];
+ }
+ return self;
+}
+
+
+
- self.autoenablesDefaultLighting = YES;
- self.scene = [SCNScene new];
+- (void)layoutSubviews {
+ [super layoutSubviews];
+ //NSLog(@"setting view bounds %@", NSStringFromCGRect(self.bounds));
+ self.arView.frame = self.bounds;
+}
+
+- (void)pause {
+ [self.session pause];
+}
+
+- (void)resume {
+ [self.session runWithConfiguration:self.configuration];
+}
- self.planes = [NSMutableDictionary new];
+- (void)session:(ARSession *)session didFailWithError:(NSError *)error {
+ if(self.onARKitError) {
+ self.onARKitError(RCTJSErrorFromNSError(error));
+ } else {
+ NSLog(@"Initializing ARKIT failed with Error: %@ %@", error, [error userInfo]);
+
+ }
+
+}
+- (void)reset {
+ if (ARWorldTrackingConfiguration.isSupported) {
+ [self.session runWithConfiguration:self.configuration options:ARSessionRunOptionRemoveExistingAnchors | ARSessionRunOptionResetTracking];
}
- return self;
+}
+
+- (void)focusScene {
+ [self.nodeManager.localOrigin setPosition:self.nodeManager.cameraOrigin.position];
+ [self.nodeManager.localOrigin setRotation:self.nodeManager.cameraOrigin.rotation];
+}
+
+- (void)clearScene {
+ [self.nodeManager clear];
}
#pragma mark - setter-getter
+- (ARSession*)session {
+ return self.arView.session;
+}
+
- (BOOL)debug {
- return self.showsStatistics;
+ return self.arView.showsStatistics;
}
- (void)setDebug:(BOOL)debug {
if (debug) {
- self.showsStatistics = YES;
- self.debugOptions = ARSCNDebugOptionShowWorldOrigin | ARSCNDebugOptionShowFeaturePoints;
+ self.arView.showsStatistics = YES;
+ self.arView.debugOptions = ARSCNDebugOptionShowWorldOrigin | ARSCNDebugOptionShowFeaturePoints;
} else {
- self.showsStatistics = NO;
- self.debugOptions = SCNDebugOptionNone;
+ self.arView.showsStatistics = NO;
+ self.arView.debugOptions = SCNDebugOptionNone;
}
}
-- (BOOL)planeDetection {
- return self.configuration.planeDetection == ARPlaneDetectionHorizontal;
+- (ARPlaneDetection)planeDetection {
+ ARWorldTrackingConfiguration *configuration = (ARWorldTrackingConfiguration *) self.configuration;
+ return configuration.planeDetection;
}
-- (void)setPlaneDetection:(BOOL)planeDetection {
- if (planeDetection) {
- NSLog(@"detect plane");
- self.configuration.planeDetection = ARPlaneDetectionHorizontal;
+- (void)setPlaneDetection:(ARPlaneDetection)planeDetection {
+ ARWorldTrackingConfiguration *configuration = (ARWorldTrackingConfiguration *) self.configuration;
+
+ configuration.planeDetection = planeDetection;
+ [self resume];
+}
+
+-(NSDictionary*)origin {
+ return @{
+ @"position": vectorToJson(self.nodeManager.localOrigin.position)
+ };
+}
+
+-(void)setOrigin:(NSDictionary*)json {
+
+ if(json[@"transition"]) {
+ NSDictionary * transition =json[@"transition"];
+ if(transition[@"duration"]) {
+ [SCNTransaction setAnimationDuration:[transition[@"duration"] floatValue]];
+ } else {
+ [SCNTransaction setAnimationDuration:0.0];
+ }
+
} else {
- NSLog(@"do not detect plane");
- self.configuration.planeDetection = ARPlaneDetectionNone;
+ [SCNTransaction setAnimationDuration:0.0];
}
+ SCNVector3 position = [RCTConvert SCNVector3:json[@"position"]];
+ [self.nodeManager.localOrigin setPosition:position];
+}
- [self.session runWithConfiguration:self.configuration];
+- (BOOL)lightEstimationEnabled {
+ ARConfiguration *configuration = self.configuration;
+ return configuration.lightEstimationEnabled;
+}
+
+
+- (void)setLightEstimationEnabled:(BOOL)lightEstimationEnabled {
+ ARConfiguration *configuration = self.configuration;
+ configuration.lightEstimationEnabled = lightEstimationEnabled;
+ [self resume];
+}
+- (void)setAutoenablesDefaultLighting:(BOOL)autoenablesDefaultLighting {
+ self.arView.autoenablesDefaultLighting = autoenablesDefaultLighting;
}
-- (BOOL)lightEstimation {
- return self.configuration.lightEstimationEnabled;
+- (BOOL)autoenablesDefaultLighting {
+ return self.arView.autoenablesDefaultLighting;
}
-- (void)setLightEstimation:(BOOL)lightEstimation {
- self.configuration.lightEstimationEnabled = lightEstimation;
- [self.session runWithConfiguration:self.configuration];
+- (ARWorldAlignment)worldAlignment {
+ ARConfiguration *configuration = self.configuration;
+ return configuration.worldAlignment;
+}
+
+- (void)setWorldAlignment:(ARWorldAlignment)worldAlignment {
+ ARConfiguration *configuration = self.configuration;
+ if (worldAlignment == ARWorldAlignmentGravityAndHeading) {
+ configuration.worldAlignment = ARWorldAlignmentGravityAndHeading;
+ } else if (worldAlignment == ARWorldAlignmentCamera) {
+ configuration.worldAlignment = ARWorldAlignmentCamera;
+ } else {
+ configuration.worldAlignment = ARWorldAlignmentGravity;
+ }
+ [self resume];
+}
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300
+- (void)setDetectionImages:(NSArray*) detectionImages {
+
+ if (@available(iOS 11.3, *)) {
+ ARWorldTrackingConfiguration *configuration = self.configuration;
+ NSSet *detectionImagesSet = [[NSSet alloc] init];
+ for (id config in detectionImages) {
+ if(config[@"resourceGroupName"]) {
+ // TODO: allow bundle to be defined
+ detectionImagesSet = [detectionImagesSet setByAddingObjectsFromSet:[ARReferenceImage referenceImagesInGroupNamed:config[@"resourceGroupName"] bundle:nil]];
+ }
+ }
+ configuration.detectionImages = detectionImagesSet;
+ [self resume];;
+ }
+}
+#endif
+- (NSDictionary *)readCameraPosition {
+ // deprecated
+ SCNVector3 cameraPosition = self.nodeManager.cameraOrigin.position;
+ return vectorToJson(cameraPosition);
}
-- (NSDictionary *)cameraPosition {
- simd_float4 position = self.session.currentFrame.camera.transform.columns[3];
+static NSDictionary * vectorToJson(const SCNVector3 v) {
+ return @{ @"x": @(v.x), @"y": @(v.y), @"z": @(v.z) };
+}
+static NSDictionary * vector_float3ToJson(const simd_float3 v) {
+ return @{ @"x": @(v.x), @"y": @(v.y), @"z": @(v.z) };
+}
+static NSDictionary * vector4ToJson(const SCNVector4 v) {
+ return @{ @"x": @(v.x), @"y": @(v.y), @"z": @(v.z), @"w": @(v.w) };
+}
+
+
+- (NSDictionary *)readCamera {
+ SCNVector3 position = self.nodeManager.cameraOrigin.position;
+ SCNVector4 rotation = self.nodeManager.cameraOrigin.rotation;
+ SCNVector4 orientation = self.nodeManager.cameraOrigin.orientation;
+ SCNVector3 eulerAngles = self.nodeManager.cameraOrigin.eulerAngles;
+ SCNVector3 direction = self.nodeManager.cameraDirection;
return @{
- @"x": [NSNumber numberWithFloat:position.x],
- @"y": [NSNumber numberWithFloat:position.y],
- @"z": [NSNumber numberWithFloat:position.z]
+ @"position":vectorToJson(position),
+ @"rotation":vector4ToJson(rotation),
+ @"orientation":vector4ToJson(orientation),
+ @"eulerAngles":vectorToJson(eulerAngles),
+ @"direction":vectorToJson(direction),
};
}
+- (SCNVector3)projectPoint:(SCNVector3)point {
+ return [self.arView projectPoint:[self.nodeManager getAbsolutePositionToOrigin:point]];
+
+}
+
+
+
+- (float)getCameraDistanceToPoint:(SCNVector3)point {
+ return [self.nodeManager getCameraDistanceToPoint:point];
+
+}
+
+
#pragma mark - Lazy loads
--(ARWorldTrackingSessionConfiguration *)configuration {
+-(ARWorldTrackingConfiguration *)configuration {
if (_configuration) {
return _configuration;
}
-
-// if (!ARWorldTrackingSessionConfiguration.isSupported) {
-// }
-
- _configuration = [ARWorldTrackingSessionConfiguration new];
+
+ if (!ARWorldTrackingConfiguration.isSupported) {}
+
+ _configuration = [ARWorldTrackingConfiguration new];
_configuration.planeDetection = ARPlaneDetectionHorizontal;
return _configuration;
}
-#pragma mark - methods
+
+#pragma mark - snapshot methods
+
+- (void)hitTestSceneObjects:(const CGPoint)tapPoint resolve:(RCTARKitResolve)resolve reject:(RCTARKitReject)reject {
+
+ resolve([self.nodeManager getSceneObjectsHitResult:tapPoint]);
+}
+
+
+- (UIImage *)getSnapshot:(NSDictionary *)selection {
+ UIImage *image = [self.arView snapshot];
+
+
+ return [self cropImage:image toSelection:selection];
+
+}
+
+
+
+
+
+- (UIImage *)getSnapshotCamera:(NSDictionary *)selection {
+ CVPixelBufferRef pixelBuffer = self.arView.session.currentFrame.capturedImage;
+ CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
+
+ CIContext *temporaryContext = [CIContext contextWithOptions:nil];
+ CGImageRef videoImage = [temporaryContext
+ createCGImage:ciImage
+ fromRect:CGRectMake(0, 0,
+ CVPixelBufferGetWidth(pixelBuffer),
+ CVPixelBufferGetHeight(pixelBuffer))];
+
+ UIImage *image = [UIImage imageWithCGImage:videoImage scale: 1.0 orientation:UIImageOrientationRight];
+ CGImageRelease(videoImage);
+
+ UIImage *cropped = [self cropImage:image toSelection:selection];
+ return cropped;
+
+}
+
+
+
+- (UIImage *)cropImage:(UIImage *)imageToCrop toRect:(CGRect)rect
+{
+ //CGRect CropRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height+15);
+
+ CGImageRef imageRef = CGImageCreateWithImageInRect([imageToCrop CGImage], rect);
+ UIImage *cropped = [UIImage imageWithCGImage:imageRef];
+ CGImageRelease(imageRef);
+
+ return cropped;
+}
+
+static inline double radians (double degrees) {return degrees * M_PI/180;}
+UIImage* rotate(UIImage* src, UIImageOrientation orientation)
+{
+ UIGraphicsBeginImageContext(src.size);
+
+ CGContextRef context = UIGraphicsGetCurrentContext();
+ [src drawAtPoint:CGPointMake(0, 0)];
+ if (orientation == UIImageOrientationRight) {
+ CGContextRotateCTM (context, radians(90));
+ } else if (orientation == UIImageOrientationLeft) {
+ CGContextRotateCTM (context, radians(-90));
+ } else if (orientation == UIImageOrientationDown) {
+ // NOTHING
+ } else if (orientation == UIImageOrientationUp) {
+ CGContextRotateCTM (context, radians(90));
+ }
+
+
+
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return image;
+}
+- (UIImage *)cropImage:(UIImage *)imageToCrop toSelection:(NSDictionary *)selection
+{
+
+ // selection is in view-coordinate system
+ // where as the image is a camera picture with arbitary size
+ // also, the camera picture is cut of so that it "covers" the self.bounds
+ // if selection is nil, crop to the viewport
+
+ UIImage * image = rotate(imageToCrop, imageToCrop.imageOrientation);
+
+ float arViewWidth = self.bounds.size.width;
+ float arViewHeight = self.bounds.size.height;
+ float imageWidth = image.size.width;
+ float imageHeight = image.size.height;
+
+ float arViewRatio = arViewHeight/arViewWidth;
+ float imageRatio = imageHeight/imageWidth;
+ float imageToArWidth = imageWidth/arViewWidth;
+ float imageToArHeight = imageHeight/arViewHeight;
+
+ float finalHeight;
+ float finalWidth;
+
+
+ if (arViewRatio > imageRatio)
+ {
+ finalHeight = arViewHeight*imageToArHeight;
+ finalWidth = arViewHeight*imageToArHeight /arViewRatio;
+ }
+ else
+ {
+ finalWidth = arViewWidth*imageToArWidth;
+ finalHeight = arViewWidth * imageToArWidth * arViewRatio;
+ }
+
+ float topOffset = (image.size.height - finalHeight)/2;
+ float leftOffset = (image.size.width - finalWidth)/2;
+
+
+ float x = leftOffset;
+ float y = topOffset;
+ float width = finalWidth;
+ float height = finalHeight;
+ if(selection && selection != [NSNull null]) {
+ x = leftOffset+ [selection[@"x"] floatValue]*imageToArWidth;
+ y = topOffset+[selection[@"y"] floatValue]*imageToArHeight;
+ width = [selection[@"width"] floatValue]*imageToArWidth;
+ height = [selection[@"height"] floatValue]*imageToArHeight;
+ }
+ CGRect rect = CGRectMake(x, y, width, height);
+
+ UIImage *cropped = [self cropImage:image toRect:rect];
+ return cropped;
+}
+
+
+#pragma mark - plane hit detection
+
+- (void)hitTestPlane:(const CGPoint)tapPoint types:(ARHitTestResultType)types resolve:(RCTARKitResolve)resolve reject:(RCTARKitReject)reject {
+
+ resolve([self getPlaneHitResult:tapPoint types:types]);
+}
+
+
+
+static NSDictionary * getPlaneHitResult(NSMutableArray *resultsMapped, const CGPoint tapPoint) {
+ return @{
+ @"results": resultsMapped
+ };
+}
+
+
+- (NSDictionary *)getPlaneHitResult:(const CGPoint)tapPoint types:(ARHitTestResultType)types; {
+ NSArray *results = [self.arView hitTest:tapPoint types:types];
+ NSMutableArray * resultsMapped = [self.nodeManager mapHitResults:results];
+ NSDictionary *planeHitResult = getPlaneHitResult(resultsMapped, tapPoint);
+ return planeHitResult;
+}
+
+- (void)handleTapFrom: (UITapGestureRecognizer *)recognizer {
+ // Take the screen space tap coordinates and pass them to the hitTest method on the ARSCNView instance
+ CGPoint tapPoint = [recognizer locationInView:self.arView];
+ //
+ if(self.onTapOnPlaneUsingExtent) {
+ // Take the screen space tap coordinates and pass them to the hitTest method on the ARSCNView instance
+ NSDictionary * planeHitResult = [self getPlaneHitResult:tapPoint types:ARHitTestResultTypeExistingPlaneUsingExtent];
+ self.onTapOnPlaneUsingExtent(planeHitResult);
+ }
+
+ if(self.onTapOnPlaneNoExtent) {
+ // Take the screen space tap coordinates and pass them to the hitTest method on the ARSCNView instance
+ NSDictionary * planeHitResult = [self getPlaneHitResult:tapPoint types:ARHitTestResultTypeExistingPlane];
+ self.onTapOnPlaneNoExtent(planeHitResult);
+ }
+}
+
+
#pragma mark - ARSCNViewDelegate
-/**
- Called when a new node has been added.
- */
-- (void)renderer:(id )renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
- if (![anchor isKindOfClass:[ARPlaneAnchor class]]) {
- return;
+- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time {
+ for (id rendererDelegate in self.rendererDelegates) {
+ if ([rendererDelegate respondsToSelector:@selector(renderer:updateAtTime:)]) {
+ [rendererDelegate renderer:renderer updateAtTime:time];
+ }
}
+}
+
- ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor;
- if (self.onPlaneDetected) {
- self.onPlaneDetected(@{
- @"id": planeAnchor.identifier.UUIDString,
- @"alignment": @(planeAnchor.alignment),
- @"center": @{ @"x": @(planeAnchor.center.x), @"y": @(planeAnchor.center.y), @"z": @(planeAnchor.center.z) },
- @"extent": @{ @"x": @(planeAnchor.extent.x), @"y": @(planeAnchor.extent.y), @"z": @(planeAnchor.extent.z) }
- });
+- (void)renderer:(id )renderer didRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)time {
+ for (id rendererDelegate in self.rendererDelegates) {
+ if ([rendererDelegate respondsToSelector:@selector(renderer:didRenderScene:atTime:)]) {
+ [rendererDelegate renderer:renderer didRenderScene:scene atTime:time];
+ }
}
+}
+
- Plane *plane = [[Plane alloc] initWithAnchor: (ARPlaneAnchor *)anchor isHidden: NO];
- [self.planes setObject:plane forKey:anchor.identifier];
- [node addChildNode:plane];
+
+
+- (NSDictionary *)makeAnchorDetectionResult:(SCNNode *)node anchor:(ARAnchor *)anchor {
+ NSDictionary* baseProps = @{
+ @"id": anchor.identifier.UUIDString,
+ @"type": @"unkown",
+ @"eulerAngles":vectorToJson(node.eulerAngles),
+ @"position": vectorToJson([self.nodeManager getRelativePositionToOrigin:node.position]),
+ @"positionAbsolute": vectorToJson(node.position)
+ };
+ NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:baseProps];
+
+ if([anchor isKindOfClass:[ARPlaneAnchor class]]) {
+ ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor;
+ NSDictionary * planeProperties = [self makePlaneAnchorProperties:planeAnchor];
+ [dict addEntriesFromDictionary:planeProperties];
+ } else if (@available(iOS 11.3, *)) {
+ #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300
+ if([anchor isKindOfClass:[ARImageAnchor class]]) {
+ ARImageAnchor *imageAnchor = (ARImageAnchor *)anchor;
+ NSDictionary * imageProperties = [self makeImageAnchorProperties:imageAnchor];
+ [dict addEntriesFromDictionary:imageProperties];
+ }
+ #endif
+ } else {
+ // Fallback on earlier versions
+ }
+ return dict;
}
-/**
- Called when a node will be updated.
- */
+
+- (NSDictionary *)makePlaneAnchorProperties:(ARPlaneAnchor *)planeAnchor {
+ return @{
+ @"type": @"plane",
+ @"alignment": @(planeAnchor.alignment),
+ @"center": vector_float3ToJson(planeAnchor.center),
+ @"extent": vector_float3ToJson(planeAnchor.extent)
+ };
+
+}
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300
+- (NSDictionary *)makeImageAnchorProperties:(ARImageAnchor *)imageAnchor API_AVAILABLE(ios(11.3)){
+ return @{
+ @"type": @"image",
+ @"image": @{
+ @"name": imageAnchor.referenceImage.name
+ }
+
+ };
+
+}
+ #endif
+
+- (void)addRendererDelegates:(id) delegate {
+ [self.rendererDelegates addObject:delegate];
+ NSLog(@"added, number of renderer delegates %d", [self.rendererDelegates count]);
+}
+
+- (void)removeRendererDelegates:(id) delegate {
+ [self.rendererDelegates removeObject:delegate];
+ NSLog(@"removed, number of renderer delegates %d", [self.rendererDelegates count]);
+}
- (void)renderer:(id )renderer willUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
}
-/**
- Called when a node has been updated.
- */
-- (void)renderer:(id )renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
- ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor;
- if (self.onPlaneUpdate) {
- self.onPlaneUpdate(@{
- @"id": planeAnchor.identifier.UUIDString,
- @"alignment": @(planeAnchor.alignment),
- @"center": @{ @"x": @(planeAnchor.center.x), @"y": @(planeAnchor.center.y), @"z": @(planeAnchor.center.z) },
- @"extent": @{ @"x": @(planeAnchor.extent.x), @"y": @(planeAnchor.extent.y), @"z": @(planeAnchor.extent.z) }
- });
+- (void)renderer:(id )renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
+
+ NSDictionary *anchorDict = [self makeAnchorDetectionResult:node anchor:anchor];
+
+ if (self.onPlaneDetected && [anchor isKindOfClass:[ARPlaneAnchor class]]) {
+ self.onPlaneDetected(anchorDict);
+ } else if (self.onAnchorDetected) {
+ self.onAnchorDetected(anchorDict);
}
+
+}
- Plane *plane = [self.planes objectForKey:anchor.identifier];
- if (plane == nil) {
- return;
+- (void)renderer:(id )renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
+ NSDictionary *anchorDict = [self makeAnchorDetectionResult:node anchor:anchor];
+
+ if (self.onPlaneUpdated && [anchor isKindOfClass:[ARPlaneAnchor class]]) {
+ self.onPlaneUpdated(anchorDict);
+ }else if (self.onAnchorUpdated) {
+ self.onAnchorUpdated(anchorDict);
}
+
+}
- [plane update:(ARPlaneAnchor *)anchor];
+- (void)renderer:(id)renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
+ NSDictionary *anchorDict = [self makeAnchorDetectionResult:node anchor:anchor];
+
+ if (self.onPlaneRemoved && [anchor isKindOfClass:[ARPlaneAnchor class]]) {
+ self.onPlaneRemoved(anchorDict);
+ } else if (self.onAnchorRemoved) {
+ self.onAnchorRemoved(anchorDict);
+ }
}
-/**
- Called when a mapped node has been removed.
- */
-- (void)renderer:(id )renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
- [self.planes removeObjectForKey:anchor.identifier];
+
+
+
+#pragma mark - ARSessionDelegate
+
+- (ARFrame * _Nullable)currentFrame {
+ return self.arView.session.currentFrame;
}
-#pragma mark - session
+- (NSDictionary *)getCurrentLightEstimation {
+ return [self wrapLightEstimation:[self currentFrame].lightEstimate];
+}
+
+- (NSMutableArray *)getCurrentDetectedFeaturePoints {
+ NSMutableArray * featurePoints = [NSMutableArray array];
+ for (int i = 0; i < [self currentFrame].rawFeaturePoints.count; i++) {
+ vector_float3 positionV = [self currentFrame].rawFeaturePoints.points[i];
+ SCNVector3 position = [self.nodeManager getRelativePositionToOrigin:SCNVector3Make(positionV[0],positionV[1],positionV[2])];
+ NSString * pointId = [NSString stringWithFormat:@"featurepoint_%lld",[self currentFrame].rawFeaturePoints.identifiers[i]];
+
+ [featurePoints addObject:@{
+ @"x": @(position.x),
+ @"y": @(position.y),
+ @"z": @(position.z),
+ @"id":pointId,
+ }];
+
+ }
+ return featurePoints;
+}
- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame {
- // simd_float4 position = frame.camera.transform.columns[3];
+ for (id sessionDelegate in self.sessionDelegates) {
+ if ([sessionDelegate respondsToSelector:@selector(session:didUpdateFrame:)]) {
+ [sessionDelegate session:session didUpdateFrame:frame];
+ }
+ }
+ if (self.onFeaturesDetected) {
+ NSArray * featurePoints = [self getCurrentDetectedFeaturePoints];
+ dispatch_async(dispatch_get_main_queue(), ^{
+
+
+ if(self.onFeaturesDetected) {
+ self.onFeaturesDetected(@{
+ @"featurePoints":featurePoints
+ });
+ }
+ });
+ }
+
+ if (self.lightEstimationEnabled && self.onLightEstimation) {
+ /** this is called rapidly and is therefore demanding, better poll it from outside with getCurrentLightEstimation **/
+
+
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if(self.onLightEstimation) {
+ NSDictionary *estimate = [self getCurrentLightEstimation];
+ self.onLightEstimation(estimate);
+ }
+ });
+
+ }
+
}
-- (void)session:(ARSession *)session didFailWithError:(NSError *)error {
+- (NSDictionary *)wrapLightEstimation:(ARLightEstimate *)estimate {
+ if(!estimate) {
+ return nil;
+ }
+ return @{
+ @"ambientColorTemperature":@(estimate.ambientColorTemperature),
+ @"ambientIntensity":@(estimate.ambientIntensity),
+ };
+}
+
+
+
+- (void)session:(ARSession *)session cameraDidChangeTrackingState:(ARCamera *)camera {
+ if (self.onTrackingState) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.onTrackingState(@{
+ @"state": @(camera.trackingState),
+ @"reason": @(camera.trackingStateReason)
+ });
+ });
+ }
}
-- (void)sessionWasInterrupted:(ARSession *)session {
+
+
+#pragma mark - RCTARKitTouchDelegate
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+ [super touchesBegan:touches withEvent:event];
+ for (id touchDelegate in self.touchDelegates) {
+ if ([touchDelegate respondsToSelector:@selector(touches:beganWithEvent:)]) {
+ [touchDelegate touches:touches beganWithEvent:event];
+ }
+ }
}
-- (void)sessionInterruptionEnded:(ARSession *)session {
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+ [super touchesMoved:touches withEvent:event];
+ for (id touchDelegate in self.touchDelegates) {
+ if ([touchDelegate respondsToSelector:@selector(touches:movedWithEvent:)]) {
+ [touchDelegate touches:touches movedWithEvent:event];
+ }
+ }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+ [super touchesEnded:touches withEvent:event];
+ for (id touchDelegate in self.touchDelegates) {
+ if ([touchDelegate respondsToSelector:@selector(touches:endedWithEvent:)]) {
+ [touchDelegate touches:touches endedWithEvent:event];
+ }
+ }
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+ [super touchesCancelled:touches withEvent:event];
+ for (id touchDelegate in self.touchDelegates) {
+ if ([touchDelegate respondsToSelector:@selector(touches:cancelledWithEvent:)]) {
+ [touchDelegate touches:touches cancelledWithEvent:event];
+ }
+ }
+}
+
+
+
+#pragma mark - dealloc
+-(void) dealloc {
}
@end
diff --git a/ios/RCTARKit.xcodeproj/project.pbxproj b/ios/RCTARKit.xcodeproj/project.pbxproj
index 29b10b9b..78c04cc0 100644
--- a/ios/RCTARKit.xcodeproj/project.pbxproj
+++ b/ios/RCTARKit.xcodeproj/project.pbxproj
@@ -8,7 +8,16 @@
/* Begin PBXBuildFile section */
10062C831F127572009AE974 /* RCTARKitManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10062C821F127572009AE974 /* RCTARKitManager.m */; };
- 10E553291F1391350059B7EC /* Plane.m in Sources */ = {isa = PBXBuildFile; fileRef = 10E553281F1391350059B7EC /* Plane.m */; };
+ 1021FE1C1F3EDB98000E7339 /* ARModelManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1021FE181F3EDB90000E7339 /* ARModelManager.m */; };
+ 1021FE1D1F3EDB9B000E7339 /* ARTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1021FE191F3EDB90000E7339 /* ARTextManager.m */; };
+ 105F124E1F7C0719006D4BA3 /* RCTConvert+ARKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 105F124D1F7C0718006D4BA3 /* RCTConvert+ARKit.m */; };
+ 10DCBC4B1F7CE836008C89E7 /* ARGeosManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10DCBC4A1F7CE836008C89E7 /* ARGeosManager.m */; };
+ 10ED47A71F38BC01004DF043 /* DeviceMotion.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED47A61F38BC00004DF043 /* DeviceMotion.m */; };
+ 10FEF6141F774C9000EC21AE /* RCTARKitIO.m in Sources */ = {isa = PBXBuildFile; fileRef = 10FEF6101F774C8F00EC21AE /* RCTARKitIO.m */; };
+ 10FEF6151F774C9000EC21AE /* RCTARKitNodes.m in Sources */ = {isa = PBXBuildFile; fileRef = 10FEF6121F774C9000EC21AE /* RCTARKitNodes.m */; };
+ B1990B221FCEEBD60001AE2F /* color-grabber.m in Sources */ = {isa = PBXBuildFile; fileRef = B1990B211FCEEBD60001AE2F /* color-grabber.m */; };
+ B1CEA29F204C14090025C1B8 /* RCTARKitSpriteView.m in Sources */ = {isa = PBXBuildFile; fileRef = B1CEA29E204C14090025C1B8 /* RCTARKitSpriteView.m */; };
+ B1CEA2A3204C160D0025C1B8 /* RCTARKitSpriteViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B1CEA2A2204C160D0025C1B8 /* RCTARKitSpriteViewManager.m */; };
B3E7B58A1CC2AC0600A0062D /* RCTARKit.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RCTARKit.m */; };
/* End PBXBuildFile section */
@@ -22,14 +31,44 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ B14C363A1F9504D70047CB67 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
10062C811F127566009AE974 /* RCTARKitManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitManager.h; sourceTree = ""; };
10062C821F127572009AE974 /* RCTARKitManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitManager.m; sourceTree = ""; };
- 10E553271F1391350059B7EC /* Plane.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Plane.h; sourceTree = ""; };
- 10E553281F1391350059B7EC /* Plane.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Plane.m; sourceTree = ""; };
+ 1021FE181F3EDB90000E7339 /* ARModelManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARModelManager.m; sourceTree = ""; };
+ 1021FE191F3EDB90000E7339 /* ARTextManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARTextManager.m; sourceTree = ""; };
+ 1021FE1A1F3EDB90000E7339 /* ARTextManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARTextManager.h; sourceTree = ""; };
+ 1021FE1B1F3EDB90000E7339 /* ARModelManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARModelManager.h; sourceTree = ""; };
+ 105F124C1F7C0718006D4BA3 /* RCTConvert+ARKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+ARKit.h"; sourceTree = ""; };
+ 105F124D1F7C0718006D4BA3 /* RCTConvert+ARKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+ARKit.m"; sourceTree = ""; };
+ 10DCBC491F7CE836008C89E7 /* ARGeosManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARGeosManager.h; sourceTree = ""; };
+ 10DCBC4A1F7CE836008C89E7 /* ARGeosManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARGeosManager.m; sourceTree = ""; };
+ 10ED47A51F38BC00004DF043 /* DeviceMotion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeviceMotion.h; sourceTree = ""; };
+ 10ED47A61F38BC00004DF043 /* DeviceMotion.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DeviceMotion.m; sourceTree = ""; };
+ 10FEF60F1F774C8F00EC21AE /* RCTARKitNodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitNodes.h; sourceTree = ""; };
+ 10FEF6101F774C8F00EC21AE /* RCTARKitIO.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitIO.m; sourceTree = ""; };
+ 10FEF6111F774C8F00EC21AE /* RCTARKitIO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitIO.h; sourceTree = ""; };
+ 10FEF6121F774C9000EC21AE /* RCTARKitNodes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitNodes.m; sourceTree = ""; };
+ 10FEF6131F774C9000EC21AE /* RCTARKitDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitDelegate.h; sourceTree = ""; };
134814201AA4EA6300B7C361 /* libRCTARKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTARKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ B14C36631F960C500047CB67 /* PocketSVG.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PocketSVG.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ B14C36651F960C6E0047CB67 /* PocketSVG.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PocketSVG.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ B1990B201FCEEBD60001AE2F /* color-grabber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "color-grabber.h"; sourceTree = ""; };
+ B1990B211FCEEBD60001AE2F /* color-grabber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "color-grabber.m"; sourceTree = ""; };
+ B1CEA29E204C14090025C1B8 /* RCTARKitSpriteView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitSpriteView.m; sourceTree = ""; };
+ B1CEA2A0204C15500025C1B8 /* RCTARKitSpriteView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitSpriteView.h; sourceTree = ""; };
+ B1CEA2A1204C15FE0025C1B8 /* RCTARKitSpriteViewManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitSpriteViewManager.h; sourceTree = ""; };
+ B1CEA2A2204C160D0025C1B8 /* RCTARKitSpriteViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitSpriteViewManager.m; sourceTree = ""; };
B3E7B5881CC2AC0600A0062D /* RCTARKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTARKit.h; sourceTree = ""; };
B3E7B5891CC2AC0600A0062D /* RCTARKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTARKit.m; sourceTree = ""; };
/* End PBXFileReference section */
@@ -45,6 +84,23 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 106999E21F3EC2FB00032829 /* components */ = {
+ isa = PBXGroup;
+ children = (
+ B1CEA2A2204C160D0025C1B8 /* RCTARKitSpriteViewManager.m */,
+ B1CEA2A1204C15FE0025C1B8 /* RCTARKitSpriteViewManager.h */,
+ B1CEA2A0204C15500025C1B8 /* RCTARKitSpriteView.h */,
+ B1CEA29E204C14090025C1B8 /* RCTARKitSpriteView.m */,
+ 10DCBC491F7CE836008C89E7 /* ARGeosManager.h */,
+ 10DCBC4A1F7CE836008C89E7 /* ARGeosManager.m */,
+ 1021FE1A1F3EDB90000E7339 /* ARTextManager.h */,
+ 1021FE191F3EDB90000E7339 /* ARTextManager.m */,
+ 1021FE1B1F3EDB90000E7339 /* ARModelManager.h */,
+ 1021FE181F3EDB90000E7339 /* ARModelManager.m */,
+ );
+ path = components;
+ sourceTree = "";
+ };
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
@@ -56,14 +112,42 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
- 10E553271F1391350059B7EC /* Plane.h */,
- 10E553281F1391350059B7EC /* Plane.m */,
+ B1990B1F1FCEEBD60001AE2F /* color-grabber */,
+ 106999E21F3EC2FB00032829 /* components */,
+ 10ED47A51F38BC00004DF043 /* DeviceMotion.h */,
+ 10ED47A61F38BC00004DF043 /* DeviceMotion.m */,
B3E7B5881CC2AC0600A0062D /* RCTARKit.h */,
B3E7B5891CC2AC0600A0062D /* RCTARKit.m */,
+ 10FEF6131F774C9000EC21AE /* RCTARKitDelegate.h */,
+ 10FEF6111F774C8F00EC21AE /* RCTARKitIO.h */,
+ 10FEF6101F774C8F00EC21AE /* RCTARKitIO.m */,
+ 10FEF60F1F774C8F00EC21AE /* RCTARKitNodes.h */,
+ 10FEF6121F774C9000EC21AE /* RCTARKitNodes.m */,
10062C811F127566009AE974 /* RCTARKitManager.h */,
10062C821F127572009AE974 /* RCTARKitManager.m */,
+ 105F124C1F7C0718006D4BA3 /* RCTConvert+ARKit.h */,
+ 105F124D1F7C0718006D4BA3 /* RCTConvert+ARKit.m */,
134814211AA4EA7D00B7C361 /* Products */,
+ B13FF7601F94F72400A6C92B /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ B13FF7601F94F72400A6C92B /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ B14C36651F960C6E0047CB67 /* PocketSVG.framework */,
+ B14C36631F960C500047CB67 /* PocketSVG.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ B1990B1F1FCEEBD60001AE2F /* color-grabber */ = {
+ isa = PBXGroup;
+ children = (
+ B1990B201FCEEBD60001AE2F /* color-grabber.h */,
+ B1990B211FCEEBD60001AE2F /* color-grabber.m */,
);
+ path = "color-grabber";
sourceTree = "";
};
/* End PBXGroup section */
@@ -76,6 +160,7 @@
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
+ B14C363A1F9504D70047CB67 /* CopyFiles */,
);
buildRules = (
);
@@ -93,7 +178,7 @@
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
- ORGANIZATIONNAME = Facebook;
+ ORGANIZATIONNAME = HippoAR;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
@@ -122,9 +207,18 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 10E553291F1391350059B7EC /* Plane.m in Sources */,
+ 10FEF6141F774C9000EC21AE /* RCTARKitIO.m in Sources */,
+ 10DCBC4B1F7CE836008C89E7 /* ARGeosManager.m in Sources */,
+ 10FEF6151F774C9000EC21AE /* RCTARKitNodes.m in Sources */,
+ B1990B221FCEEBD60001AE2F /* color-grabber.m in Sources */,
+ 1021FE1C1F3EDB98000E7339 /* ARModelManager.m in Sources */,
+ 1021FE1D1F3EDB9B000E7339 /* ARTextManager.m in Sources */,
+ 105F124E1F7C0719006D4BA3 /* RCTConvert+ARKit.m in Sources */,
B3E7B58A1CC2AC0600A0062D /* RCTARKit.m in Sources */,
10062C831F127572009AE974 /* RCTARKitManager.m in Sources */,
+ B1CEA29F204C14090025C1B8 /* RCTARKitSpriteView.m in Sources */,
+ B1CEA2A3204C160D0025C1B8 /* RCTARKitSpriteViewManager.m in Sources */,
+ 10ED47A71F38BC01004DF043 /* DeviceMotion.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -164,7 +258,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -198,7 +292,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
@@ -208,12 +302,18 @@
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
+ "$(SRCROOT)/../../react-native-arcl/ios",
+ ../../../ios/Pods/Headers/Public/,
+ ../../../ios/Pods/Headers/Public/React,
+ "$(SRCROOT)/PocketSVG",
);
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTARKit;
@@ -224,12 +324,18 @@
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
+ "$(SRCROOT)/../../react-native-arcl/ios",
+ ../../../ios/Pods/Headers/Public/,
+ ../../../ios/Pods/Headers/Public/React,
+ "$(SRCROOT)/PocketSVG",
);
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTARKit;
diff --git a/ios/RCTARKit.xcodeproj/project.xcworkspace/xcuserdata/q.xcuserdatad/UserInterfaceState.xcuserstate b/ios/RCTARKit.xcodeproj/project.xcworkspace/xcuserdata/q.xcuserdatad/UserInterfaceState.xcuserstate
index 33342893..04e912a1 100644
Binary files a/ios/RCTARKit.xcodeproj/project.xcworkspace/xcuserdata/q.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/RCTARKit.xcodeproj/project.xcworkspace/xcuserdata/q.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/ios/RCTARKitDelegate.h b/ios/RCTARKitDelegate.h
new file mode 100644
index 00000000..c864ae4d
--- /dev/null
+++ b/ios/RCTARKitDelegate.h
@@ -0,0 +1,37 @@
+//
+// RCTARKitDelegate.h
+// RCTARKit
+//
+// Created by Zehao Li on 9/7/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+#import
+#import
+
+@protocol RCTARKitTouchDelegate
+@optional
+- (void)touches:(NSSet *)touches beganWithEvent:(UIEvent *)event;
+- (void)touches:(NSSet *)touches movedWithEvent:(UIEvent *)event;
+- (void)touches:(NSSet *)touches endedWithEvent:(UIEvent *)event;
+- (void)touches:(NSSet *)touches cancelledWithEvent:(UIEvent *)event;
+@end
+
+
+
+@protocol RCTARKitRendererDelegate
+@optional
+- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time;
+- (void)renderer:(id)renderer didRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)time;
+@end
+
+
+
+@protocol RCTARKitSessionDelegate
+@optional
+- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame;
+- (void)session:(ARSession *)session didAddAnchors:(NSArray*)anchors;
+- (void)session:(ARSession *)session didUpdateAnchors:(NSArray*)anchors;
+- (void)session:(ARSession *)session didRemoveAnchors:(NSArray*)anchors;
+@end
diff --git a/ios/RCTARKitIO.h b/ios/RCTARKitIO.h
new file mode 100644
index 00000000..1ddde7d9
--- /dev/null
+++ b/ios/RCTARKitIO.h
@@ -0,0 +1,24 @@
+//
+// RCTARKitIO.h
+// RCTARKit
+//
+// Created by Zehao Li on 9/9/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+#import
+
+typedef void (^ARKitIOExportHandler)(NSString* filename, NSString* path);
+
+
+@interface RCTARKitIO : NSObject
+
++ (instancetype)sharedInstance;
+
+- (SCNNode *)loadModel:(NSString *)path nodeName:(NSString *)nodeName withAnimation:(BOOL)withAnimation;
+- (SCNNode *)loadMDLModel:(NSString *)path nodeName:(NSString *)nodeName withAnimation:(BOOL)withAnimation;
+
+- (void)saveScene:(SCNScene *)scene as:(NSString *)filename finishHandler:(nullable ARKitIOExportHandler)finishHandler;
+
+@end
diff --git a/ios/RCTARKitIO.m b/ios/RCTARKitIO.m
new file mode 100644
index 00000000..4aa53e4a
--- /dev/null
+++ b/ios/RCTARKitIO.m
@@ -0,0 +1,167 @@
+//
+// RCTARKitIO.m
+// RCTARKit
+//
+// Created by Zehao Li on 9/9/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import "RCTARKitIO.h"
+#import
+#import
+#import
+
+@implementation RCTARKitIO
+
++ (instancetype)sharedInstance {
+ static RCTARKitIO *instance = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ if (instance == nil) {
+ instance = [[self alloc] init];
+ }
+ });
+ return instance;
+}
+
+- (instancetype)init {
+ if ((self = [super init])) {
+ }
+ return self;
+}
+
+
+- (SCNNode *)loadModel:(NSString *)path nodeName:(NSString *)nodeName withAnimation:(BOOL)withAnimation {
+ NSURL *url = [self urlFromPath:path];
+ NSError* error;
+
+ SCNScene *scene = [SCNScene sceneWithURL:url options:nil error:&error];
+ if(error) {
+ NSLog(@"%@",[error localizedDescription]);
+ }
+
+ SCNNode *node;
+ if (nodeName) {
+ node = [scene.rootNode childNodeWithName:nodeName recursively:YES];
+ } else {
+ node = [[SCNNode alloc] init];
+ NSArray *nodeArray = [scene.rootNode childNodes];
+ for (SCNNode *eachChild in nodeArray) {
+ [node addChildNode:eachChild];
+ }
+ }
+
+ if (withAnimation) {
+ NSMutableArray *animationMutableArray = [NSMutableArray array];
+ SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:url options:@{SCNSceneSourceAnimationImportPolicyKey:SCNSceneSourceAnimationImportPolicyPlayRepeatedly}];
+
+ NSArray *animationIds = [sceneSource identifiersOfEntriesWithClass:[CAAnimation class]];
+ for (NSString *eachId in animationIds){
+ CAAnimation *animation = [sceneSource entryWithIdentifier:eachId withClass:[CAAnimation class]];
+ [animationMutableArray addObject:animation];
+
+ }
+ NSArray *animationArray = [NSArray arrayWithArray:animationMutableArray];
+
+ int i = 1;
+ for (CAAnimation *animation in animationArray) {
+ NSString *key = [NSString stringWithFormat:@"ANIM_%d", i];
+ [node addAnimation:animation forKey:key];
+ i++;
+ }
+ }
+
+ return node;
+}
+
+
+
+- (SCNNode *)loadMDLModel:(NSString *)path nodeName:(NSString *)nodeName withAnimation:(BOOL)withAnimation {
+ NSURL *url = [self urlFromPath:path];
+ MDLAsset *asset = [[MDLAsset alloc] initWithURL:url];
+ SCNScene *scene = [SCNScene sceneWithMDLAsset:asset];
+
+ SCNNode *node;
+ if (nodeName) {
+ node = [scene.rootNode childNodeWithName:nodeName recursively:YES];
+ } else {
+ node = [[SCNNode alloc] init];
+ NSArray *nodeArray = [scene.rootNode childNodes];
+ for (SCNNode *eachChild in nodeArray) {
+ [node addChildNode:eachChild];
+ }
+ }
+
+ if (withAnimation) {
+ NSMutableArray *animationMutableArray = [NSMutableArray array];
+ SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:url options:@{SCNSceneSourceAnimationImportPolicyKey:SCNSceneSourceAnimationImportPolicyPlayRepeatedly}];
+
+ NSArray *animationIds = [sceneSource identifiersOfEntriesWithClass:[CAAnimation class]];
+ for (NSString *eachId in animationIds){
+ CAAnimation *animation = [sceneSource entryWithIdentifier:eachId withClass:[CAAnimation class]];
+ [animationMutableArray addObject:animation];
+
+ }
+ NSArray *animationArray = [NSArray arrayWithArray:animationMutableArray];
+
+ int i = 1;
+ for (CAAnimation *animation in animationArray) {
+ NSString *key = [NSString stringWithFormat:@"ANIM_%d", i];
+ [node addAnimation:animation forKey:key];
+ i++;
+ }
+ }
+
+ return node;
+}
+
+
+- (NSURL *)urlFromPath:(NSString *)path {
+
+ NSURL *url;
+
+ if([path hasPrefix: @"/"]) {
+ url = [NSURL fileURLWithPath: path];
+ } else if ([path rangeOfString:@"scnassets"].location == NSNotFound) {
+ NSString *assetPath = [self getAppLibraryCachesPathWithSubDirectory:nil];
+ NSString *modelPath = [NSString stringWithFormat:@"file://%@", [assetPath stringByAppendingPathComponent:path]];
+ url = [NSURL URLWithString:[modelPath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
+ } else {
+ url = [[NSBundle mainBundle] URLForResource:path withExtension:nil];
+ }
+
+ return url;
+}
+
+
+- (void)saveScene:(SCNScene *)scene as:(NSString *)filename finishHandler:(nullable ARKitIOExportHandler)finishHandler {
+ NSString *assetPath = [self getAppLibraryCachesPathWithSubDirectory:nil];
+ NSString *exportPath = [NSString stringWithFormat:@"file://%@/%@", assetPath, filename];
+ NSLog(@"exportModel to ===> %@", exportPath);
+ NSURL *url = [NSURL URLWithString:[exportPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+
+ [scene writeToURL:url options:nil delegate:nil progressHandler:^(float totalProgress, NSError * _Nullable error, BOOL * _Nonnull stop) {
+ if (totalProgress == 1.0 && finishHandler) {
+ finishHandler(filename, exportPath);
+ }
+ }];
+}
+
+
+- (NSString *)getAppLibraryCachesPathWithSubDirectory:(NSString *)directory {
+ NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]
+ stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]];
+ if (directory) {
+ path = [path stringByAppendingPathComponent:directory];
+ }
+
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ if (![fileManager fileExistsAtPath:path]) {
+ [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
+ }
+
+ return path;
+}
+
+@end
diff --git a/ios/RCTARKitManager.h b/ios/RCTARKitManager.h
index 40959e2c..3f9d8fc4 100644
--- a/ios/RCTARKitManager.h
+++ b/ios/RCTARKitManager.h
@@ -11,7 +11,4 @@
#import
@interface RCTARKitManager : RCTViewManager
-
-- (void)getCameraPosition:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
-
@end
diff --git a/ios/RCTARKitManager.m b/ios/RCTARKitManager.m
index 7c8a7eb5..2df43c4c 100644
--- a/ios/RCTARKitManager.m
+++ b/ios/RCTARKitManager.m
@@ -8,27 +8,394 @@
#import "RCTARKitManager.h"
#import "RCTARKit.h"
-
-@interface RCTARKitManager ()
-@end
+#import "RCTARKitNodes.h"
+#import
+#import
+#import "color-grabber.h"
@implementation RCTARKitManager
RCT_EXPORT_MODULE()
+
+
- (UIView *)view {
- return [RCTARKit sharedInstance];
+ return [ARKit sharedInstance];
+}
+
++ (BOOL)requiresMainQueueSetup
+{
+ return YES;
+}
+
+
+- (NSDictionary *)constantsToExport
+{
+
+ NSMutableDictionary * arHitTestResultType =
+ [NSMutableDictionary dictionaryWithDictionary:
+ @{
+ @"FeaturePoint": @(ARHitTestResultTypeFeaturePoint),
+ @"EstimatedHorizontalPlane": @(ARHitTestResultTypeEstimatedHorizontalPlane),
+ @"ExistingPlane": @(ARHitTestResultTypeExistingPlane),
+ @"ExistingPlaneUsingExtent": @(ARHitTestResultTypeExistingPlaneUsingExtent),
+ }];
+ NSMutableDictionary * arAnchorAligment =
+ [NSMutableDictionary
+ dictionaryWithDictionary:@{
+ @"Horizontal": @(ARPlaneAnchorAlignmentHorizontal)
+ }];
+ NSMutableDictionary * arPlaneDetection =
+ [NSMutableDictionary
+ dictionaryWithDictionary:@{
+ @"Horizontal": @(ARPlaneDetectionHorizontal),
+ @"None": @(ARPlaneDetectionNone),
+ }];
+ #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300
+ if (@available(iOS 11.3, *)) {
+ [arHitTestResultType
+ addEntriesFromDictionary:@{
+ @"ExistingPlaneUsingGeometry": @(ARHitTestResultTypeExistingPlaneUsingGeometry),
+ @"EstimatedVerticalPlane": @(ARHitTestResultTypeEstimatedVerticalPlane)
+ }];
+ [arPlaneDetection
+ addEntriesFromDictionary:@{
+ @"Vertical": @(ARPlaneDetectionVertical),
+ @"HorizontalVertical": @(ARPlaneDetectionHorizontal + ARPlaneDetectionVertical),
+ }];
+ [arAnchorAligment
+ addEntriesFromDictionary:@{
+ @"Vertical": @(ARPlaneAnchorAlignmentVertical)
+ }];
+ }
+ #endif
+
+
+
+
+ return @{
+ @"ARHitTestResultType": arHitTestResultType,
+ @"ARPlaneDetection": arPlaneDetection,
+ @"ARPlaneAnchorAlignment": arAnchorAligment,
+ @"LightingModel": @{
+ @"Constant": SCNLightingModelConstant,
+ @"Blinn": SCNLightingModelBlinn,
+ @"Lambert": SCNLightingModelLambert,
+ @"Phong": SCNLightingModelPhong,
+ @"PhysicallyBased": SCNLightingModelPhysicallyBased
+ },
+ @"LightType": @{
+ @"Ambient": SCNLightTypeAmbient,
+ @"Directional": SCNLightTypeDirectional,
+ @"Omni": SCNLightTypeOmni,
+ @"Probe": SCNLightTypeProbe,
+ @"Spot": SCNLightTypeSpot,
+ @"IES": SCNLightTypeIES
+ },
+ @"ShadowMode": @{
+ @"Forward": [@(SCNShadowModeForward) stringValue],
+ @"Deferred": [@(SCNShadowModeDeferred) stringValue],
+ @"ModeModulated": [@(SCNShadowModeModulated) stringValue],
+ },
+ @"ColorMask": @{
+ @"All": [@(SCNColorMaskAll) stringValue],
+ @"None": [@(SCNColorMaskNone) stringValue],
+ @"Alpha": [@(SCNColorMaskAlpha) stringValue],
+ @"Blue": [@(SCNColorMaskBlue) stringValue],
+ @"Red": [@(SCNColorMaskRed) stringValue],
+ @"Green": [@(SCNColorMaskGreen) stringValue],
+ },
+
+ @"ShaderModifierEntryPoint": @{
+ @"Geometry": SCNShaderModifierEntryPointGeometry,
+ @"Surface": SCNShaderModifierEntryPointSurface,
+ @"LighingModel": SCNShaderModifierEntryPointLightingModel,
+ @"Fragment": SCNShaderModifierEntryPointFragment
+ },
+ @"BlendMode": @{
+ @"Alpha": [@(SCNBlendModeAlpha) stringValue],
+ @"Add": [@(SCNBlendModeAdd) stringValue],
+ @"Subtract": [@(SCNBlendModeSubtract) stringValue],
+ @"Multiply": [@(SCNBlendModeMultiply) stringValue],
+ @"Screen": [@(SCNBlendModeScreen) stringValue],
+ @"Replace": [@(SCNBlendModeReplace) stringValue],
+ },
+ @"TransparencyMode": @{
+ @"Default": [@(SCNTransparencyModeAOne) stringValue],
+ @"RGBZero": [@(SCNTransparencyModeRGBZero) stringValue],
+ @"SingleLayer": [@(SCNTransparencyModeSingleLayer) stringValue],
+ @"DualLayer": [@(SCNTransparencyModeDualLayer) stringValue],
+ },
+ @"ChamferMode": @{
+ @"Both": [@(SCNChamferModeBoth) stringValue],
+ @"Back": [@(SCNChamferModeBack) stringValue],
+ @"Front": [@(SCNChamferModeBack) stringValue],
+
+ },
+ @"ARWorldAlignment": @{
+ @"Gravity": @(ARWorldAlignmentGravity),
+ @"GravityAndHeading": @(ARWorldAlignmentGravityAndHeading),
+ @"Camera": @(ARWorldAlignmentCamera),
+ },
+ @"FillMode": @{
+ @"Fill": [@(SCNFillModeFill) stringValue],
+ @"Lines": [@(SCNFillModeLines) stringValue],
+ },
+ @"WrapMode": @{
+ @"Clamp": [@(SCNWrapModeClamp) stringValue],
+ @"Repeat": [@(SCNWrapModeRepeat) stringValue],
+ @"Mirror": [@(SCNWrapModeMirror) stringValue],
+ },
+ @"Constraint": @{
+ @"None": @"0",
+ @"BillboardAxisAll": [@(SCNBillboardAxisAll) stringValue],
+ @"BillboardAxisX": [@(SCNBillboardAxisX) stringValue],
+ @"BillboardAxisY": [@(SCNBillboardAxisY) stringValue],
+ @"BillboardAxisZ": [@(SCNBillboardAxisZ) stringValue],
+ }
+ };
}
RCT_EXPORT_VIEW_PROPERTY(debug, BOOL)
-RCT_EXPORT_VIEW_PROPERTY(planeDetection, BOOL)
-RCT_EXPORT_VIEW_PROPERTY(lightEstimation, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(planeDetection, ARPlaneDetection)
+RCT_EXPORT_VIEW_PROPERTY(origin, NSDictionary *)
+RCT_EXPORT_VIEW_PROPERTY(lightEstimationEnabled, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(autoenablesDefaultLighting, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(worldAlignment, NSInteger)
+RCT_EXPORT_VIEW_PROPERTY(detectionImages, NSArray *)
RCT_EXPORT_VIEW_PROPERTY(onPlaneDetected, RCTBubblingEventBlock)
-RCT_EXPORT_VIEW_PROPERTY(onPlaneUpdate, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onPlaneUpdated, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onPlaneRemoved, RCTBubblingEventBlock)
+
+RCT_EXPORT_VIEW_PROPERTY(onAnchorDetected, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onAnchorUpdated, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onAnchorRemoved, RCTBubblingEventBlock)
+
+RCT_EXPORT_VIEW_PROPERTY(onTrackingState, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onFeaturesDetected, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onLightEstimation, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onTapOnPlaneUsingExtent, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onTapOnPlaneNoExtent, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onEvent, RCTBubblingEventBlock)
+RCT_EXPORT_VIEW_PROPERTY(onARKitError, RCTBubblingEventBlock)
+
+RCT_EXPORT_METHOD(pause:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ [[ARKit sharedInstance] pause];
+ resolve(@{});
+}
+
+RCT_EXPORT_METHOD(resume:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ [[ARKit sharedInstance] resume];
+ resolve(@{});
+}
+
+RCT_EXPORT_METHOD(reset:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ [[ARKit sharedInstance] reset];
+ resolve(@{});
+}
+
+RCT_EXPORT_METHOD(isInitialized:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ resolve(@([ARKit isInitialized]));
+}
+
+RCT_EXPORT_METHOD(isMounted:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ if( [ARKit isInitialized]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ resolve(@([[ARKit sharedInstance] isMounted]));
+ });
+ } else {
+ resolve(@(NO));
+ }
+}
+
+RCT_EXPORT_METHOD(
+ hitTestPlanes: (NSDictionary *)pointDict
+ types:(NSUInteger)types
+ resolve:(RCTPromiseResolveBlock)resolve
+ reject:(RCTPromiseRejectBlock)reject
+ ) {
+ CGPoint point = CGPointMake( [pointDict[@"x"] floatValue], [pointDict[@"y"] floatValue] );
+ [[ARKit sharedInstance] hitTestPlane:point types:types resolve:resolve reject:reject];
+}
+
+RCT_EXPORT_METHOD(
+ hitTestSceneObjects: (NSDictionary *)pointDict
+ resolve:(RCTPromiseResolveBlock)resolve
+ reject:(RCTPromiseRejectBlock)reject
+ ) {
+ CGPoint point = CGPointMake( [pointDict[@"x"] floatValue], [pointDict[@"y"] floatValue] );
+ [[ARKit sharedInstance] hitTestSceneObjects:point resolve:resolve reject:reject];
+}
+
+
+
+
+- (NSString *)getAssetUrl:(NSString *)localID {
+ // thx https://stackoverflow.com/a/34788748/1463534
+ // heck, objective c is such a mess
+ NSString * assetID = [localID stringByReplacingOccurrencesOfString:@"/.*" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [localID length])];
+ NSString * ext = @"JPG";
+ NSString * assetURLStr = [NSString stringWithFormat:@"assets-library://asset/asset.%@?id=%@&ext=%@", ext, assetID, ext];
+ return assetURLStr;
+}
+
+- (void)storeImageInPhotoAlbum:(UIImage *)image cameraProperties:(NSDictionary *) cameraProperties reject:(RCTPromiseRejectBlock)reject resolve:(RCTPromiseResolveBlock)resolve {
+ __block PHObjectPlaceholder *placeholder;
+
+ [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
+ PHAssetChangeRequest* createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
+ placeholder = [createAssetRequest placeholderForCreatedAsset];
+
+ } completionHandler:^(BOOL success, NSError *error) {
+ if (success)
+ {
+
+ NSString * localID = placeholder.localIdentifier;
+
+ NSString * assetURLStr = [self getAssetUrl:localID];
+
+ resolve(@{@"url": assetURLStr, @"width":@(image.size.width), @"height": @(image.size.height), @"camera":cameraProperties});
+ }
+ else
+ {
+ reject(@"snapshot_error", @"Could not store snapshot", error);
+ }
+ }];
+}
+
+
+- (void)storeImageInDirectory:(UIImage *)image directory:(NSString *)directory format:(NSString *)format cameraProperties:(NSDictionary *) cameraProperties reject:(RCTPromiseRejectBlock)reject resolve:(RCTPromiseResolveBlock)resolve {
+ NSData *data;
+ if([format isEqualToString:@"jpg"]) {
+ data = UIImageJPEGRepresentation(image, 0.9);
+ } else if([format isEqualToString:@"png"]) {
+ data = UIImagePNGRepresentation(image);
+ } else {
+ reject(@"snapshot_error", [NSString stringWithFormat:@"unkown file format '%@'", format], nil);
+ return;
+ }
+ NSString *prefixString = @"capture";
+
+ NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString] ;
+ NSString *uniqueFileName = [NSString stringWithFormat:@"%@_%@.%@", prefixString, guid, format];
+
+ NSString *filePath = [directory stringByAppendingPathComponent:uniqueFileName]; //Add the file name
+ bool success = [data writeToFile:filePath atomically:YES]; //Write the file
+ if(success) {
+ resolve(@{@"url": filePath, @"width":@(image.size.width), @"height": @(image.size.height), @"camera":cameraProperties});
+ } else {
+ // TODO use NSError from writeToFile
+ reject(@"snapshot_error", [NSString stringWithFormat:@"could not save to '%@'", filePath], nil);
+ }
+
+}
+
+- (void)storeImage:(UIImage *)image options:(NSDictionary *)options reject:(RCTPromiseRejectBlock)reject resolve:(RCTPromiseResolveBlock)resolve cameraProperties:(NSDictionary *)cameraProperties {
+ NSString * target = @"cameraRoll";
+ NSString * format = @"png";
+
+ if(options[@"target"]) {
+ target = options[@"target"];
+ }
+ if(options[@"format"]) {
+ format = options[@"format"];
+ }
+ if([target isEqualToString:@"cameraRoll"]) {
+ // camera roll / photo album
+ [self storeImageInPhotoAlbum:image cameraProperties:cameraProperties reject:reject resolve:resolve ];
+ } else {
+ NSString * dir;
+ if([target isEqualToString:@"cache"]) {
+ dir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
+ } else if([target isEqualToString:@"documents"]) {
+ dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
+
+ } else {
+ dir = target;
+ }
+ [self storeImageInDirectory:image directory:dir format:format cameraProperties:cameraProperties reject:reject resolve:resolve ];
+ }
+}
+
+RCT_EXPORT_METHOD(snapshot:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSDictionary * selection = options[@"selection"];
+ NSDictionary * cameraProperties = [[ARKit sharedInstance] readCamera];
+ UIImage *image = [[ARKit sharedInstance] getSnapshot:selection];
+
+ [self storeImage:image options:options reject:reject resolve:resolve cameraProperties:cameraProperties ];
+ });
+}
+
+
+
+
+RCT_EXPORT_METHOD(snapshotCamera:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSDictionary * selection = options[@"selection"];
+ NSDictionary * cameraProperties = [[ARKit sharedInstance] readCamera];
+ UIImage *image = [[ARKit sharedInstance] getSnapshotCamera:selection];
+ [self storeImage:image options:options reject:reject resolve:resolve cameraProperties:cameraProperties];
+ });
+}
+
+RCT_EXPORT_METHOD(pickColorsRaw:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSDictionary * selection = options[@"selection"];
+ UIImage *image = [[ARKit sharedInstance] getSnapshotCamera:selection];
+ resolve([[ColorGrabber sharedInstance] getColorsFromImage:image options:options]);
+ });
+}
+
+RCT_EXPORT_METHOD(pickColorsRawFromFile:(NSString * )filePath options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ UIImage *image = [UIImage imageWithContentsOfFile:filePath];
+ resolve([[ColorGrabber sharedInstance] getColorsFromImage:image options:options]);
+ });
+}
+
+RCT_EXPORT_METHOD(getCamera:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ resolve([[ARKit sharedInstance] readCamera]);
+}
RCT_EXPORT_METHOD(getCameraPosition:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
- resolve([[RCTARKit sharedInstance] cameraPosition]);
+ resolve([[ARKit sharedInstance] readCameraPosition]);
+}
+
+RCT_EXPORT_METHOD(getCurrentLightEstimation:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ resolve([[ARKit sharedInstance] getCurrentLightEstimation]);
+}
+
+RCT_EXPORT_METHOD(getCurrentDetectedFeaturePoints:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ resolve([[ARKit sharedInstance] getCurrentDetectedFeaturePoints]);
+}
+
+RCT_EXPORT_METHOD(projectPoint:
+ (NSDictionary *)pointDict
+ resolve:(RCTPromiseResolveBlock)resolve
+ reject:(RCTPromiseRejectBlock)reject) {
+ SCNVector3 point = SCNVector3Make( [pointDict[@"x"] floatValue], [pointDict[@"y"] floatValue], [pointDict[@"z"] floatValue] );
+ SCNVector3 pointProjected = [[ARKit sharedInstance] projectPoint:point];
+ float distance = [[ARKit sharedInstance] getCameraDistanceToPoint:point];
+ resolve(@{
+ @"x": @(pointProjected.x),
+ @"y": @(pointProjected.y),
+ @"z": @(pointProjected.z),
+ @"distance": @(distance)
+ });
+
+}
+
+RCT_EXPORT_METHOD(focusScene:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ [[ARKit sharedInstance] focusScene];
+ resolve(@{});
+}
+
+RCT_EXPORT_METHOD(clearScene:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ [[ARKit sharedInstance] clearScene];
+ resolve(@{});
}
@end
diff --git a/ios/RCTARKitNodes.h b/ios/RCTARKitNodes.h
new file mode 100644
index 00000000..a6ad4be5
--- /dev/null
+++ b/ios/RCTARKitNodes.h
@@ -0,0 +1,49 @@
+//
+// RCTARKitNodes.h
+// RCTARKit
+//
+// Created by Zehao Li on 9/9/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+#import
+#import
+#import "RCTARKitDelegate.h"
+
+typedef NS_OPTIONS(NSUInteger, RFReferenceFrame) {
+ RFReferenceFrameLocal = 0,
+ RFReferenceFrameCamera = 1,
+ RFReferenceFrameFrontOfCamera = 2,
+ RFReferenceFrameWorld = 3, // to be implemented
+};
+
+@interface SCNNode (ReferenceFrame)
+@property (nonatomic, assign) RFReferenceFrame referenceFrame;
+@end
+
+
+@interface RCTARKitNodes : NSObject
+
+@property (nonatomic, strong) ARSCNView *arView;
+
+@property (nonatomic, strong) SCNNode *localOrigin;
+@property (nonatomic, strong) SCNNode *cameraOrigin;
+@property (nonatomic, strong) SCNNode *frontOfCamera;
+@property (nonatomic, assign) SCNVector3 cameraDirection;
+
+
++ (instancetype)sharedInstance;
+
+- (void)addNodeToScene:(SCNNode *)node inReferenceFrame:(NSString *)referenceFrame withParentId:(NSString *)parentId;
+- (bool)updateNode:(NSString *)nodeId properties:(NSDictionary *) properties;
+- (float)getCameraDistanceToPoint:(SCNVector3)point;
+- (void)registerNode:(SCNNode *)node withId:(NSString *)key;
+- (SCNNode *)getNodeWithId:(NSString *)key;
+- (void)removeNode:(NSString *)key;
+- (NSDictionary *)getSceneObjectsHitResult:(const CGPoint)tapPoint;
+- (void)clear;
+- (NSMutableArray *) mapHitResults:(NSArray *)results;
+- (SCNVector3)getAbsolutePositionToOrigin:(const SCNVector3)positionRelative;
+- (SCNVector3)getRelativePositionToOrigin:(const SCNVector3)positionAbsolute;
+@end
diff --git a/ios/RCTARKitNodes.m b/ios/RCTARKitNodes.m
new file mode 100644
index 00000000..6379fb8a
--- /dev/null
+++ b/ios/RCTARKitNodes.m
@@ -0,0 +1,407 @@
+//
+// RCTARKitNodes.m
+// RCTARKit
+//
+// Created by Zehao Li on 9/9/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import "RCTARKit.h"
+#import "RCTARKitNodes.h"
+#import "RCTConvert+ARKit.h"
+
+
+@implementation SCNNode (ReferenceFrame)
+@dynamic referenceFrame;
+@end
+
+CGFloat focDistance = 0.2f;
+
+
+@interface RCTARKitNodes ()
+
+
+@property (nonatomic, strong) SCNNode* rootNode;
+
+@property NSMutableDictionary *nodes;
+@property NSMutableDictionary *orphans;
+
+@end
+
+
+
+@implementation RCTARKitNodes
+
++ (instancetype)sharedInstance {
+ static RCTARKitNodes *instance = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ if (instance == nil) {
+ instance = [[self alloc] init];
+ }
+ });
+ return instance;
+}
+
+- (instancetype)init {
+ if ((self = [super init])) {
+ // local reference frame origin
+ self.localOrigin = [[SCNNode alloc] init];
+ self.localOrigin.name = @"localOrigin";
+
+ // camera reference frame origin
+ self.cameraOrigin = [[SCNNode alloc] init];
+ self.cameraOrigin.name = @"cameraOrigin";
+
+ // front-of-camera frame origin
+ self.frontOfCamera = [[SCNNode alloc] init];
+ self.frontOfCamera.name = @"frontOfCamera";
+
+ // init caches
+ self.nodes = [NSMutableDictionary new];
+ self.orphans = [NSMutableDictionary new];
+ }
+ return self;
+}
+
+- (void)setArView:(ARSCNView *)arView {
+ //NSLog(@"setArView");
+ _arView = arView;
+ self.rootNode = arView.scene.rootNode;
+
+ self.rootNode.name = @"root";
+
+ [self.rootNode addChildNode:self.localOrigin];
+ [self.rootNode addChildNode:self.cameraOrigin];
+ [self.rootNode addChildNode:self.frontOfCamera];
+}
+
+#pragma mark
+
+
+
+
+/**
+ add a node to scene on a frame (defaults to Local) or to a parentNode (if parentId is given)
+ */
+- (void)addNodeToScene:(SCNNode *)node inReferenceFrame:(NSString *)referenceFrame withParentId:(NSString *)parentId {
+ //NSLog(@"addNodeToScene node: %@ ", node.name);
+ if(parentId) {
+ [self addNodeToParent:node parentId:parentId];
+ } else {
+ [self addNodeToFrame:node referenceFrame:referenceFrame];
+ }
+
+}
+/**
+ add a node to scene in a reference frame
+ */
+- (void)addNodeToFrame:(SCNNode *)node referenceFrame:(NSString *)referenceFrame {
+ [self registerNode:node withId:node.name];
+ if (!referenceFrame) {
+ referenceFrame = @"Local"; // default to Local frame
+ }
+ NSString *selectorString = [NSString stringWithFormat:@"addNodeTo%@Frame:", referenceFrame];
+ SEL selector = NSSelectorFromString(selectorString);
+ if ([self respondsToSelector:selector]) {
+ // check https://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown
+ IMP imp = [self methodForSelector:selector];
+ void (*func)(id, SEL, SCNNode*) = (void *)imp;
+ func(self, selector, node);
+ }
+}
+
+
+- (void)addNodeToParent:(SCNNode *)node parentId:(NSString *)parentId {
+ SCNNode * parentNode = [self getNodeWithId:parentId];
+
+ if(!parentNode) {
+ NSMutableSet * orphansLookingForParent = [self.orphans objectForKey:parentId];
+ if(!orphansLookingForParent) {
+ orphansLookingForParent = [NSMutableSet setWithObject:node];
+ } else {
+ [orphansLookingForParent addObject:node];
+ }
+ [self.orphans setObject:orphansLookingForParent forKey:parentId];
+
+ } else {
+ [parentNode addChildNode:node];
+
+ }
+ [self registerNode:node withId:node.name];
+
+
+
+}
+
+- (void)clear {
+ // clear scene
+ NSArray *keys = [self.nodes allKeys];
+
+ for (id key in keys) {
+ id node = [self.nodes objectForKey:key];
+ if (node) {
+ [node removeFromParentNode];
+ }
+
+ }
+ [self.nodes removeAllObjects];
+}
+
+- (void)addNodeToLocalFrame:(SCNNode *)node {
+ node.referenceFrame = RFReferenceFrameLocal;
+
+ [self.localOrigin addChildNode:node];
+ //NSLog(@"[RCTARKitNodes] Add node %@ to Local frame at (%.2f, %.2f, %.2f)", node.name, node.position.x, node.position.y, node.position.z);
+
+}
+
+- (void)addNodeToCameraFrame:(SCNNode *)node {
+ node.referenceFrame = RFReferenceFrameCamera;
+ //NSLog(@"[RCTARKitNodes] Add node %@ to Camera frame at (%.2f, %.2f, %.2f)", node.name, node.position.x, node.position.y, node.position.z);
+
+ [self.cameraOrigin addChildNode:node];
+}
+
+- (void)addNodeToFrontOfCameraFrame:(SCNNode *)node {
+ node.referenceFrame = RFReferenceFrameFrontOfCamera;
+
+ //NSLog(@"[RCTARKitNodes] Add node %@ to FrontOfCamera frame at (%.2f, %.2f, %.2f)", node.name, node.position.x, node.position.y, node.position.z);
+
+ [self.frontOfCamera addChildNode:node];
+}
+
+
+- (NSDictionary *)getSceneObjectsHitResult:(const CGPoint)tapPoint {
+ NSDictionary *options = @{
+ SCNHitTestRootNodeKey: self.localOrigin,
+ SCNHitTestSortResultsKey: @(YES),
+ SCNHitTestOptionSearchMode: @(SCNHitTestSearchModeAll)
+ };
+ NSArray *results = [_arView hitTest:tapPoint options:options];
+ NSMutableArray * resultsMapped = [self mapHitResultsWithSceneResults:results];
+ NSDictionary *result = getSceneObjectHitResult(resultsMapped, tapPoint);
+ return result;
+}
+
+
+static NSDictionary * getSceneObjectHitResult(NSMutableArray *resultsMapped, const CGPoint tapPoint) {
+ return @{
+ @"results": resultsMapped
+ };
+}
+
+
+static SCNVector3 toSCNVector3(simd_float4 float4) {
+ SCNVector3 positionAbsolute = SCNVector3Make(float4.x, float4.y, float4.z);
+ return positionAbsolute;
+}
+
+- (SCNVector3)getRelativePositionToOrigin:(const SCNVector3)positionAbsolute {
+ SCNVector3 originPosition = self.localOrigin.position;
+ SCNVector3 position = SCNVector3Make(positionAbsolute.x - originPosition.x, positionAbsolute.y- originPosition.y, positionAbsolute.z - originPosition.z);
+ return position;
+}
+
+- (SCNVector3)getAbsolutePositionToOrigin:(const SCNVector3)positionRelative {
+ SCNVector3 originPosition = self.localOrigin.position;
+ SCNVector3 position = SCNVector3Make(positionRelative.x + originPosition.x, positionRelative.y+ originPosition.y, positionRelative.z + originPosition.z);
+ return position;
+}
+
+
+
+- (NSMutableArray *) mapHitResultsWithSceneResults: (NSArray *)results {
+
+ NSMutableArray *resultsMapped = [NSMutableArray arrayWithCapacity:[results count]];
+
+ [results enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
+ SCNHitTestResult *result = (SCNHitTestResult *) obj;
+ SCNNode * node = result.node;
+
+ NSString * nodeId = [self findNodeId:node];
+ if(nodeId) {
+
+ SCNVector3 positionAbsolute = result.worldCoordinates;
+ SCNVector3 position = [self getRelativePositionToOrigin:positionAbsolute];
+ SCNVector3 normal = result.worldNormal;
+ float distance = [self getCameraDistanceToPoint:positionAbsolute];
+ NSDictionary *result = @{
+ @"id": nodeId,
+ @"distance": @(distance),
+ @"positionAbsolute": @{
+ @"x": @(positionAbsolute.x),
+ @"y": @(positionAbsolute.y),
+ @"z": @(positionAbsolute.z)
+ },
+ @"position": @{
+ @"x": @(position.x),
+ @"y": @(position.y),
+ @"z": @(position.z)
+ },
+ @"normal": @{
+ @"x": @(normal.x),
+ @"y": @(normal.y),
+ @"z": @(normal.z)
+ }
+ };
+ [resultsMapped addObject:(result )];
+ }
+
+ }];
+
+ return resultsMapped;
+
+}
+
+
+
+static id ObjectOrNull(id object)
+{
+ return object ?: [NSNull null];
+}
+
+- (NSMutableArray *) mapHitResults:(NSArray *)results {
+ NSMutableArray *resultsMapped = [NSMutableArray arrayWithCapacity:[results count]];
+
+ [results enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
+ ARHitTestResult *result = (ARHitTestResult *) obj;
+
+ SCNVector3 positionAbsolute = toSCNVector3(result.worldTransform.columns[3]);
+ SCNVector3 position = [self getRelativePositionToOrigin:positionAbsolute];
+ [resultsMapped addObject:(@{
+ @"distance": @(result.distance),
+ @"id": ObjectOrNull(result.anchor.identifier.UUIDString),
+ @"positionAbsolute": @{
+ @"x": @(positionAbsolute.x),
+ @"y": @(positionAbsolute.y),
+ @"z": @(positionAbsolute.z)
+ },
+ @"position": @{
+ @"x": @(position.x),
+ @"y": @(position.y),
+ @"z": @(position.z)
+ }
+ } )];
+ }];
+ return resultsMapped;
+}
+
+#pragma mark - node register
+- (void)registerNode:(SCNNode *)node withId:(NSString *)nodeId {
+ [self removeNode:nodeId];
+ if (node) {
+ //NSLog(@"registering node %@", nodeId);
+ [self.nodes setObject:node forKey:nodeId];
+
+ // are there any orphans? (3d objects that have a parent, but parent has not yet been mounted
+ // this seems to be the default case as react mounts first the children
+ NSSet * orphans = [self.orphans objectForKey:nodeId];
+ if(orphans) {
+ for (SCNNode * child in [orphans allObjects]) {
+ [node addChildNode:child];
+ }
+ [self.orphans removeObjectForKey:nodeId];
+ }
+ }
+}
+
+
+- (NSString *) findNodeId:(SCNNode *)nodeWithParents {
+
+ SCNNode* _node = nodeWithParents;
+ while(_node) {
+ if(_node.name && [self.nodes objectForKey:_node.name]) {
+ return _node.name;
+ }
+ _node = _node.parentNode;
+ }
+ return nil;
+
+}
+
+
+- (SCNNode *)getNodeWithId:(NSString *)nodeId {
+ return [self.nodes objectForKey:nodeId];
+}
+
+- (void)removeNode:(NSString *)nodeId {
+
+ SCNNode *node = [self getNodeWithId:nodeId];
+ if (node) {
+ //NSLog(@"removing node: %@ ", key);
+
+ if(node.parentNode) {
+ if(node.light) {
+ // see https://stackoverflow.com/questions/47270056/how-to-remove-a-light-with-shadowmode-deferred-in-scenekit-arkit?noredirect=1#comment81491270_47270056
+ node.hidden = YES;
+ [node removeFromParentNode];
+ } else {
+ [node removeFromParentNode];
+ }
+ }
+ [self.nodes removeObjectForKey:nodeId];
+ }
+}
+
+- (bool)updateNode:(NSString *)nodeId
+ properties:(NSDictionary *) properties {
+
+ SCNNode *node = [self getNodeWithId:nodeId];
+ //NSLog(@"updating node %@ :%@", nodeId, properties);
+ if(node) {
+ [RCTConvert setNodeProperties:node properties:properties];
+ if(node.geometry && properties[@"shape"]) {
+ [RCTConvert setShapeProperties:node.geometry properties:properties[@"shape"]];
+ }
+ if(properties[@"material"]) {
+ for (id material in node.geometry.materials) {
+ [RCTConvert setMaterialProperties:material properties:properties[@"material"]];
+ }
+ }
+ if(node.light) {
+ [RCTConvert setLightProperties:node.light properties:properties];
+ }
+ return true;
+ } else {
+ NSLog(@"WARNING: node does not exists: %@. This means that the node has not been mounted yet, so native calls got out of order", nodeId);
+
+ return false;
+ }
+
+}
+
+
+
+
+#pragma mark - RCTARKitSessionDelegate
+- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame {
+ simd_float4 pos = frame.camera.transform.columns[3];
+ self.cameraOrigin.position = SCNVector3Make(pos.x, pos.y, pos.z);
+ simd_float4 z = frame.camera.transform.columns[2];
+ self.cameraDirection = SCNVector3Make(-z.x, -z.y, -z.z);
+ self.cameraOrigin.eulerAngles = SCNVector3Make(0, atan2f(z.x, z.z), 0);
+ self.frontOfCamera.position = SCNVector3Make(pos.x - focDistance * z.x, pos.y - focDistance * z.y, pos.z - focDistance * z.z);
+ self.frontOfCamera.eulerAngles = self.cameraOrigin.eulerAngles;
+
+}
+
+- (float)getCameraDistanceToPoint:(SCNVector3)point {
+ return getDistance(self.cameraOrigin.position, point);
+}
+
+static float getDistance(const SCNVector3 pointA, const SCNVector3 pointB) {
+ float xd = pointB.x - pointA.x;
+ float yd = pointB.y - pointA.y;
+ float zd = pointB.z - pointA.z;
+ float distance = sqrt(xd * xd + yd * yd + zd * zd);
+
+ if (distance < 0){
+ return (distance * -1);
+ } else {
+ return (distance);
+ }
+}
+
+@end
diff --git a/ios/RCTConvert+ARKit.h b/ios/RCTConvert+ARKit.h
new file mode 100644
index 00000000..60beee71
--- /dev/null
+++ b/ios/RCTConvert+ARKit.h
@@ -0,0 +1,48 @@
+//
+// RCTConvert+ARKit.h
+// RCTARKit
+//
+// Created by Zehao Li on 9/28/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+#import
+#import
+#import
+
+@interface SCNTextNode : SCNNode
+@end
+
+
+@interface RCTConvert (ARKit)
+
++ (SCNMaterial *)SCNMaterial:(id)json;
+
++ (SCNVector3)SCNVector3:(id)json;
++ (SCNVector4)SCNVector4:(id)json;
++ (SCNNode *)SCNNode:(id)json;
+
++ (SCNBox *)SCNBox:(id)json;
++ (SCNSphere *)SCNSphere:(id)json;
++ (SCNCylinder *)SCNCylinder:(id)json;
++ (SCNCone *)SCNCone:(id)json;
++ (SCNPyramid *)SCNPyramid:(id)json;
++ (SCNTube *)SCNTube:(id)json;
++ (SCNTorus *)SCNTorus:(id)json;
++ (SCNCapsule *)SCNCapsule:(id)json;
++ (SCNPlane *)SCNPlane:(id)json;
++ (SCNShape * )SCNShape:(id)json;
++ (SCNLight *)SCNLight:(id)json;
+
++ (SCNTextNode *)SCNTextNode:(id)json;
+
++ (void)setNodeProperties:(SCNNode *)node properties:(id)json;
++ (void)setMaterialProperties:(SCNMaterial *)material properties:(id)json;
++ (void)setShapeProperties:(SCNGeometry *)geometry properties:(id)json;
++ (void)setLightProperties:(SCNLight *)light properties:(id)json;
+
++ (ARPlaneDetection)ARPlaneDetection:(id)number;
+
+@end
+
diff --git a/ios/RCTConvert+ARKit.m b/ios/RCTConvert+ARKit.m
new file mode 100644
index 00000000..16c1f133
--- /dev/null
+++ b/ios/RCTConvert+ARKit.m
@@ -0,0 +1,619 @@
+//
+// RCTConvert+ARKit.m
+// RCTARKit
+//
+// Created by Zehao Li on 9/28/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import "RCTConvert+ARKit.h"
+#import "SVGBezierPath.h"
+
+@implementation RCTConvert (ARKit)
+
++ (SCNMaterial *)SCNMaterial:(id)json {
+ SCNMaterial *material = [SCNMaterial new];
+ [self setMaterialProperties:material properties:json];
+
+ return material;
+}
+
+
+
++ (SCNVector3)SCNVector3:(id)json {
+ CGFloat x = [json[@"x"] floatValue];
+ CGFloat y = [json[@"y"] floatValue];
+ CGFloat z = [json[@"z"] floatValue];
+ return SCNVector3Make(x, y, z);
+}
+
++ (SCNVector4)SCNVector4:(id)json {
+ CGFloat x = [json[@"x"] floatValue];
+ CGFloat y = [json[@"y"] floatValue];
+ CGFloat z = [json[@"z"] floatValue];
+ CGFloat w = [json[@"w"] floatValue];
+ return SCNVector4Make(x, y, z, w);
+}
+
+
++ (SCNNode *)SCNNode:(id)json {
+ SCNNode *node = [SCNNode new];
+
+ node.name = [NSString stringWithFormat:@"%@", json[@"id"]];
+ [self setNodeProperties:node properties:json];
+
+ return node;
+}
+
+
++ (void)addMaterials:(SCNGeometry *)geometry json:(id)json sides:(int) sides {
+ SCNMaterial *material = [self SCNMaterial:json[@"material"]];
+
+ NSMutableArray *materials = [NSMutableArray array];
+ for (int i = 0; i < sides; i++)
+ [materials addObject: material];
+ geometry.materials = materials;
+}
+
++ (SCNBox *)SCNBox:(id)json {
+ NSDictionary *shape = json[@"shape"];
+
+
+ CGFloat width = [shape[@"width"] floatValue];
+ CGFloat height = [shape[@"height"] floatValue];
+ CGFloat length = [shape[@"length"] floatValue];
+ CGFloat chamfer = [shape[@"chamfer"] floatValue];
+ SCNBox *geometry = [SCNBox boxWithWidth:width height:height length:length chamferRadius:chamfer];
+
+ [self addMaterials:geometry json:json sides:6];
+
+ return geometry;
+}
+
+
++ (SCNSphere *)SCNSphere:(id)json {
+ NSDictionary* shape = json[@"shape"];
+ CGFloat radius = [shape[@"radius"] floatValue];
+ SCNSphere *geometry = [SCNSphere sphereWithRadius:radius];
+
+ [self addMaterials:geometry json:json sides:1];
+
+ return geometry;
+}
+
++ (SCNCylinder *)SCNCylinder:(id)json {
+ NSDictionary* shape = json[@"shape"];
+ CGFloat radius = [shape[@"radius"] floatValue];
+ CGFloat height = [shape[@"height"] floatValue];
+ SCNCylinder *geometry = [SCNCylinder cylinderWithRadius:radius height:height];
+
+ [self addMaterials:geometry json:json sides:3];
+
+ return geometry;
+}
+
++ (SCNCone *)SCNCone:(id)json {
+ NSDictionary* shape = json[@"shape"];
+ CGFloat topR = [shape[@"topR"] floatValue];
+ CGFloat bottomR = [shape[@"bottomR"] floatValue];
+ CGFloat height = [shape[@"height"] floatValue];
+ SCNCone *geometry = [SCNCone coneWithTopRadius:topR bottomRadius:bottomR height:height];
+
+ [self addMaterials:geometry json:json sides:2];
+
+ return geometry;
+}
+
++ (SCNPyramid *)SCNPyramid:(id)json {
+ NSDictionary* shape = json[@"shape"];
+ CGFloat width = [shape[@"width"] floatValue];
+ CGFloat length = [shape[@"length"] floatValue];
+ CGFloat height = [shape[@"height"] floatValue];
+ SCNPyramid *geometry = [SCNPyramid pyramidWithWidth:width height:height length:length];
+
+ [self addMaterials:geometry json:json sides:5];
+
+ return geometry;
+}
+
++ (SCNTube *)SCNTube:(id)json {
+ NSDictionary* shape = json[@"shape"];
+ CGFloat innerR = [shape[@"innerR"] floatValue];
+ CGFloat outerR = [shape[@"outerR"] floatValue];
+ CGFloat height = [shape[@"height"] floatValue];
+ SCNTube *geometry = [SCNTube tubeWithInnerRadius:innerR outerRadius:outerR height:height];
+
+ [self addMaterials:geometry json:json sides:4];
+
+ return geometry;
+}
+
++ (SCNTorus *)SCNTorus:(id)json {
+ NSDictionary* shape = json[@"shape"];
+ CGFloat ringR = [shape[@"ringR"] floatValue];
+ CGFloat pipeR = [shape[@"pipeR"] floatValue];
+ SCNTorus *geometry = [SCNTorus torusWithRingRadius:ringR pipeRadius:pipeR];
+
+ [self addMaterials:geometry json:json sides:1];
+
+ return geometry;
+}
+
++ (SCNCapsule *)SCNCapsule:(id)json {
+ NSDictionary* shape = json[@"shape"];
+ CGFloat capR = [shape[@"capR"] floatValue];
+ CGFloat height = [shape[@"height"] floatValue];
+ SCNCapsule *geometry = [SCNCapsule capsuleWithCapRadius:capR height:height];
+
+ [self addMaterials:geometry json:json sides:1];
+
+ return geometry;
+}
+
++ (SCNPlane *)SCNPlane:(id)json {
+ NSDictionary* shape = json[@"shape"];
+ CGFloat width = [shape[@"width"] floatValue];
+ CGFloat height = [shape[@"height"] floatValue];
+ SCNPlane *geometry = [SCNPlane planeWithWidth:width height:height];
+ if(shape[@"cornerRadius"]) {
+ geometry.cornerRadius = [shape[@"cornerRadius"] floatValue];
+ }
+ if(shape[@"cornerSegmentCount"]) {
+ geometry.cornerSegmentCount = [shape[@"cornerSegmentCount"] intValue];
+ }
+ if(shape[@"widthSegmentCount"]) {
+ geometry.widthSegmentCount = [shape[@"widthSegmentCount"] intValue];
+ }
+ if(shape[@"heightSegmentCount"]) {
+ geometry.heightSegmentCount = [shape[@"heightSegmentCount"] intValue];
+ }
+ [self addMaterials:geometry json:json sides:1];
+
+ return geometry;
+}
+
++ (SVGBezierPath *)svgStringToBezier:(NSString *)pathString {
+ NSArray * paths = [SVGBezierPath pathsFromSVGString:pathString];
+ SVGBezierPath * fullPath;
+ for(SVGBezierPath *path in paths) {
+ if(!fullPath) {
+ fullPath = path;
+ } else {
+ [fullPath appendPath:path];
+ }
+ }
+ return fullPath;
+}
+
++ (void)setChamferProfilePathSvg:(SCNShape *)geometry properties:(NSDictionary *)shape {
+ if (shape[@"chamferProfilePathSvg"]) {
+
+
+ SVGBezierPath * path = [self svgStringToBezier:shape[@"chamferProfilePathSvg"]];
+ if(shape[@"chamferProfilePathFlatness"]) {
+ path.flatness = [shape[@"chamferProfilePathFlatness"] floatValue];
+ }
+ // normalize path
+ CGRect boundingBox = path.bounds;
+ if(path.bounds.size.width !=0 && path.bounds.size.height != 0) {
+ CGFloat scaleX = 1/boundingBox.size.width;
+ CGFloat scaleY = scaleY = 1/boundingBox.size.height;
+
+ CGAffineTransform transform = CGAffineTransformMakeScale(scaleX, scaleY);
+ [path applyTransform:transform];
+ geometry.chamferProfile = path;
+ } else {
+ NSLog(@"Invalid chamferProfilePathFlatness");
+ }
+ }
+
+}
+
++ (SCNShape * )SCNShape:(id)json {
+ NSDictionary* shape = json[@"shape"];
+
+
+ NSString * pathString = shape[@"pathSvg"];
+
+ SVGBezierPath * path = [self svgStringToBezier:pathString];
+
+ if (shape[@"pathFlatness"]) {
+ path.flatness = [shape[@"pathFlatness"] floatValue];
+ }
+
+ CGFloat extrusion = [shape[@"extrusion"] floatValue];
+ SCNShape *geometry = [SCNShape shapeWithPath:path extrusionDepth:extrusion];
+
+ if (shape[@"chamferMode"]) {
+ geometry.chamferMode = (SCNChamferMode) [shape[@"chamferMode"] integerValue];
+ }
+ if (shape[@"chamferRadius"]) {
+ geometry.chamferRadius = [shape[@"chamferRadius"] floatValue];
+ }
+ if (shape[@"chamferProfilePathSvg"]) {
+ [self setChamferProfilePathSvg:geometry properties:shape];
+ }
+
+ [self addMaterials:geometry json:json sides:1];
+
+ return geometry;
+}
+
+
++ (SCNTextNode *)SCNTextNode:(id)json {
+ // init SCNText
+ NSString *text = [NSString stringWithFormat:@"%@", json[@"text"]];
+ if (!text) {
+ text = @"(null)";
+ }
+
+ NSDictionary* font = json[@"font"];
+ CGFloat depth = [font[@"depth"] floatValue];
+ if (!depth) {
+ depth = 0.0f;
+ }
+ CGFloat fontSize = [font[@"size"] floatValue];
+ CGFloat size = fontSize / 12;
+ SCNText *scnText = [SCNText textWithString:text extrusionDepth:depth / size];
+
+ scnText.flatness = 0.1;
+
+ // font
+ NSString *fontName = font[@"name"];
+ if (fontName) {
+ scnText.font = [UIFont fontWithName:fontName size:12];
+ } else {
+ scnText.font = [UIFont systemFontOfSize:12];
+ }
+
+ // chamfer
+ CGFloat chamfer = [font[@"chamfer"] floatValue];
+ if (!chamfer) {
+ chamfer = 0.0f;
+ }
+ scnText.chamferRadius = chamfer / size;
+
+ // material
+ // scnText.materials = @[face, face, border, border, border];
+ [self addMaterials:scnText json:json sides:5];
+
+
+ // SCNTextNode
+ SCNTextNode *textNode = [SCNNode nodeWithGeometry:scnText];
+ textNode.name = [NSString stringWithFormat:@"%@", json[@"id"]];
+
+
+ textNode.scale = SCNVector3Make(size, size, size);
+
+ // position textNode
+ SCNVector3 min = SCNVector3Zero;
+ SCNVector3 max = SCNVector3Zero;
+ [textNode getBoundingBoxMin:&min max:&max];
+
+ textNode.position = SCNVector3Make(-(min.x + max.x) / 2 * size,
+ -(min.y + max.y) / 2 * size,
+ -(min.z + max.z) / 2 * size);
+
+ return textNode;
+}
+
+
++ (SCNLight *)SCNLight:(id)json {
+ SCNLight * light = [SCNLight light];
+ [self setLightProperties:light properties:json];
+ return light;
+}
+
+
++ (void)setMaterialPropertyContents:(id)property material:(SCNMaterialProperty *)material {
+
+ if (property[@"path"]) {
+ SCNMatrix4 m = SCNMatrix4Identity;
+
+ // scenekit has an issue with indexed-colour png's on some devices, so we redraw the image. See for more details: https://stackoverflow.com/questions/40058359/scenekit-some-textures-have-a-red-hue/45824190#45824190
+
+ UIImage *correctedImage;
+ UIImage *inputImage = [UIImage imageNamed:property[@"path"]];
+ CGFloat width = inputImage.size.width;
+ CGFloat height = inputImage.size.height;
+
+ UIGraphicsBeginImageContext(inputImage.size);
+ [inputImage drawInRect:(CGRectMake(0, 0, width, height))];
+ correctedImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ material.contents = correctedImage;
+
+
+ if (property[@"wrapS"]) {
+ material.wrapS = (SCNWrapMode) [property[@"wrapS"] integerValue];
+ }
+
+ if (property[@"wrapT"]) {
+ material.wrapT = (SCNWrapMode) [property[@"wrapT"] integerValue];
+ }
+
+ if (property[@"wrap"]) {
+ material.wrapT = (SCNWrapMode) [property[@"wrapT"] integerValue];
+ material.wrapS = (SCNWrapMode) [property[@"wrapS"] integerValue];
+ }
+
+ if (property[@"scale"]) {
+ float x = [property[@"scale"][@"x"] floatValue];
+ float y = [property[@"scale"][@"y"] floatValue];
+ float z = [property[@"scale"][@"z"] floatValue];
+
+ m = SCNMatrix4Mult(m, SCNMatrix4MakeScale(x, y, z));
+ }
+
+ if (property[@"rotation"]) {
+ float a = [property[@"rotation"][@"angle"] floatValue];
+ float x = [property[@"rotation"][@"x"] floatValue];
+ float y = [property[@"rotation"][@"y"] floatValue];
+ float z = [property[@"rotation"][@"z"] floatValue];
+
+ m = SCNMatrix4Mult(m, SCNMatrix4MakeRotation(a, x, y, z));
+ }
+
+ if (property[@"translation"]) {
+ float x = [property[@"translation"][@"x"] floatValue];
+ float y = [property[@"translation"][@"y"] floatValue];
+ float z = [property[@"translation"][@"z"] floatValue];
+
+ m = SCNMatrix4Mult(m, SCNMatrix4MakeTranslation(x, y, z));
+ }
+
+ material.contentsTransform = m;
+
+
+ } else if (property[@"color"]) {
+ material.contents = [self UIColor:property[@"color"]];
+ }
+ if (property[@"intensity"]) {
+ material.intensity = [property[@"intensity"] floatValue];
+ }
+}
+
++ (void)setMaterialProperties:(SCNMaterial *)material properties:(id)json {
+ if (json[@"doubleSided"]) {
+ material.doubleSided = [json[@"doubleSided"] boolValue];
+ } else {
+ material.doubleSided = YES;
+ }
+
+ if (json[@"blendMode"]) {
+ material.blendMode = (SCNBlendMode) [json[@"blendMode"] integerValue];
+ }
+
+ if (json[@"transparencyMode"]) {
+ material.transparencyMode = (SCNTransparencyMode) [json[@"transparencyMode"] integerValue];
+ }
+
+ if (json[@"lightingModel"]) {
+ material.lightingModelName = json[@"lightingModel"];
+ }
+
+ if (json[@"diffuse"]) {
+ [self setMaterialPropertyContents:json[@"diffuse"] material:material.diffuse];
+ }
+
+ if (json[@"normal"]) {
+ [self setMaterialPropertyContents:json[@"normal"] material:material.normal];
+ }
+
+ if (json[@"displacement"]) {
+ [self setMaterialPropertyContents:json[@"displacement"] material:material.displacement];
+ }
+
+ if (json[@"specular"]) {
+ [self setMaterialPropertyContents:json[@"specular"] material:material.specular];
+ }
+
+ if (json[@"transparency"]) {
+ material.transparency = [json[@"transparency"] floatValue];
+ }
+
+ if (json[@"metalness"]) {
+ material.lightingModelName = SCNLightingModelPhysicallyBased;
+ material.metalness.contents = @([json[@"metalness"] floatValue]);
+ }
+
+ if (json[@"roughness"]) {
+ material.lightingModelName = SCNLightingModelPhysicallyBased;
+ material.roughness.contents = @([json[@"roughness"] floatValue]);
+ }
+
+ if(json[@"shaders"] ) {
+ material.shaderModifiers = json[@"shaders"];
+ }
+
+ if(json[@"writesToDepthBuffer"] ) {
+ material.writesToDepthBuffer = [json[@"writesToDepthBuffer"] boolValue];
+ }
+
+ if(json[@"colorBufferWriteMask"] ) {
+ material.colorBufferWriteMask = [json[@"colorBufferWriteMask"] integerValue];
+ }
+
+ if(json[@"fillMode"] ) {
+ material.fillMode = [json[@"fillMode"] integerValue];
+ }
+
+ if(json[@"doubleSided"]) {
+ material.doubleSided = [json[@"doubleSided"] boolValue];
+ }
+
+ if(json[@"litPerPixel"]) {
+ material.litPerPixel = [json[@"litPerPixel"] boolValue];
+ }
+}
+
++ (void)setNodeProperties:(SCNNode *)node properties:(id)json {
+
+ if (json[@"categoryBitMask"]) {
+ node.categoryBitMask = [json[@"categoryBitMask"] integerValue];
+ }
+ if (json[@"renderingOrder"]) {
+ node.renderingOrder = [json[@"renderingOrder"] integerValue];
+ }
+ if (json[@"castsShadow"]) {
+ node.castsShadow = [json[@"castsShadow"] boolValue];
+ }
+ if (json[@"constraint"]) {
+ SCNBillboardConstraint *constraint = [SCNBillboardConstraint billboardConstraint];
+ constraint.freeAxes = [json[@"constraint"] integerValue];
+ node.constraints = @[constraint];
+ }
+ if(json[@"transition"]) {
+ NSDictionary * transition =json[@"transition"];
+ if(transition[@"duration"]) {
+ [SCNTransaction setAnimationDuration:[transition[@"duration"] floatValue]];
+ } else {
+ [SCNTransaction setAnimationDuration:0.0];
+ }
+
+ } else {
+ [SCNTransaction setAnimationDuration:0.0];
+ }
+ if (json[@"position"]) {
+ node.position = [self SCNVector3:json[@"position"]];
+ }
+
+ if (json[@"scale"]) {
+
+ CGFloat scale = [json[@"scale"] floatValue];
+ node.scale = SCNVector3Make(scale, scale, scale);
+
+ }
+
+ if (json[@"eulerAngles"]) {
+ node.eulerAngles = [self SCNVector3:json[@"eulerAngles"]];
+ }
+
+ if (json[@"orientation"]) {
+ node.orientation = [self SCNVector4:json[@"orientation"]];
+ }
+
+ if (json[@"rotation"]) {
+ node.rotation = [self SCNVector4:json[@"rotation"]];
+ }
+
+ if (json[@"opacity"]) {
+ node.opacity = [json[@"opacity"] floatValue];
+ }
+}
+
+
++ (NSSet *) specialShapeProperties {
+ return [[NSSet alloc] initWithArray:
+ @[@"pathSvg", @"chamferProfilePathSvg"]];
+}
+
+
++ (void)setShapeProperties:(SCNGeometry *)geometry properties:(id)shapeJson {
+
+ // most properties are strings
+ for (NSString* key in shapeJson) {
+ if(![self.specialShapeProperties containsObject:key]) {
+ id value = [NSNumber numberWithFloat:[shapeJson[key] floatValue]];
+ [geometry setValue:value forKey:key];
+ }
+ }
+
+ if([geometry isKindOfClass:[SCNShape class]]) {
+ SCNShape * shapeGeometry = (SCNShape * ) geometry;
+ if(shapeJson[@"pathSvg"]) {
+ NSString * pathString = shapeJson[@"pathSvg"];
+ SVGBezierPath * path = [self svgStringToBezier:pathString];
+ if (shapeJson[@"pathFlatness"]) {
+ path.flatness = [shapeJson[@"pathFlatness"] floatValue];
+ }
+ shapeGeometry.path = path;
+ }
+ if (shapeJson[@"chamferProfilePathSvg"]) {
+ [self setChamferProfilePathSvg: shapeGeometry properties:shapeJson];
+ }
+
+ }
+}
+
+
+
++ (void)setLightProperties:(SCNLight *)light properties:(id)json {
+ if (json[@"lightCategoryBitMask"]) {
+ light.categoryBitMask = [json[@"lightCategoryBitMask"] integerValue];
+ }
+ if(json[@"type"]) {
+ light.type = json[@"type"];
+ }
+ if(json[@"color"]) {
+ light.color = (__bridge id _Nonnull)([RCTConvert CGColor:json[@"color"]]);
+ }
+ if(json[@"temperature"]) {
+ light.temperature = [json[@"temperature"] floatValue];
+ }
+
+ if(json[@"intensity"]) {
+ light.intensity = [json[@"intensity"] floatValue];
+ }
+
+ if(json[@"attenuationStartDistance"]) {
+ light.attenuationStartDistance = [json[@"attenuationStartDistance"] floatValue];
+ }
+
+ if(json[@"attenuationEndDistance"]) {
+ light.attenuationEndDistance = [json[@"attenuationEndDistance"] floatValue];
+ }
+
+ if(json[@"spotInnerAngle"]) {
+ light.spotInnerAngle = [json[@"spotInnerAngle"] floatValue];
+ }
+
+ if(json[@"spotOuterAngle"]) {
+ light.spotOuterAngle = [json[@"spotOuterAngle"] floatValue];
+ }
+
+ if(json[@"castsShadow"]) {
+ light.castsShadow = [json[@"castsShadow"] boolValue];
+ }
+
+ if(json[@"shadowRadius"]) {
+ light.shadowRadius = [json[@"shadowRadius"] floatValue];
+ }
+
+ if(json[@"shadowColor"]) {
+ light.shadowColor = (__bridge id _Nonnull)([RCTConvert CGColor:json[@"shadowColor"]]);
+ }
+
+
+ if(json[@"shadowSampleCount"]) {
+ light.shadowSampleCount = [json[@"shadowSampleCount"] integerValue];
+ }
+
+ if(json[@"shadowBias"]) {
+ light.shadowBias = [json[@"shadowBias"] floatValue];
+ }
+
+ if(json[@"shadowMode"]) {
+ light.shadowMode = [json[@"shadowMode"] integerValue];
+ }
+ if(json[@"orthographicScale"]) {
+ light.orthographicScale = [json[@"orthographicScale"] floatValue];
+ }
+
+ if(json[@"zFar"]) {
+ light.zFar = [json[@"zFar"] floatValue];
+ }
+
+ if(json[@"zNear"]) {
+ light.zNear = [json[@"zNear"] floatValue];
+ }
+}
+
++ (ARPlaneDetection)ARPlaneDetection:(id)number {
+ return (ARPlaneDetection) [number integerValue];
+}
+
+
+
+@end
diff --git a/ios/color-grabber/color-grabber.h b/ios/color-grabber/color-grabber.h
new file mode 100644
index 00000000..69cb1eaf
--- /dev/null
+++ b/ios/color-grabber/color-grabber.h
@@ -0,0 +1,13 @@
+//
+// color-grabber.h
+//
+
+#import
+#import
+#import
+
+@interface ColorGrabber : NSObject
+
++ (instancetype)sharedInstance;
+- (NSArray *)getColorsFromImage:(UIImage *)image options:(NSDictionary *)options;
+@end
diff --git a/ios/color-grabber/color-grabber.m b/ios/color-grabber/color-grabber.m
new file mode 100644
index 00000000..3c334535
--- /dev/null
+++ b/ios/color-grabber/color-grabber.m
@@ -0,0 +1,250 @@
+
+#import
+#import
+#import "color-grabber.h"
+
+#define CLAMP(x, low, high) ({\
+__typeof__(x) __x = (x); \
+__typeof__(low) __low = (low);\
+__typeof__(high) __high = (high);\
+__x > __high ? __high : (__x < __low ? __low : __x);\
+})
+
+@implementation ColorGrabber
+
+
+
+
+RCT_EXPORT_MODULE();
+
+- (NSArray *)getColorsFromImage:(UIImage *)image options:(NSDictionary *)options {
+
+ float dimension = [RCTConvert float:options[@"dimension"]]; // 4
+ float flexibility = [RCTConvert float:options[@"flexibility"]]; // 5;
+ float range = [RCTConvert float:options[@"range"]]; // 40;
+
+ NSMutableArray * colours = [NSMutableArray new];
+ CGImageRef imageRef = [image CGImage];
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ unsigned char *rawData = (unsigned char*) calloc(dimension * dimension * 4, sizeof(unsigned char));
+ NSUInteger bytesPerPixel = 4;
+ NSUInteger bytesPerRow = bytesPerPixel * dimension;
+ NSUInteger bitsPerComponent = 8;
+ CGContextRef context = CGBitmapContextCreate(rawData, dimension, dimension, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
+ CGColorSpaceRelease(colorSpace);
+ CGContextDrawImage(context, CGRectMake(0, 0, dimension, dimension), imageRef);
+ CGContextRelease(context);
+
+ float x = 0;
+ float y = 0;
+ for (int n = 0; n<(dimension*dimension); n++){
+
+ int index = (bytesPerRow * y) + x * bytesPerPixel;
+ int red = rawData[index];
+ int green = rawData[index + 1];
+ int blue = rawData[index + 2];
+ int alpha = rawData[index + 3];
+ NSArray * a = [NSArray arrayWithObjects:[NSString stringWithFormat:@"%i",red],[NSString stringWithFormat:@"%i",green],[NSString stringWithFormat:@"%i",blue],[NSString stringWithFormat:@"%i",alpha], nil];
+ [colours addObject:a];
+
+ y++;
+ if (y==dimension){
+ y=0;
+ x++;
+ }
+ }
+ free(rawData);
+
+ // Add some colour flexibility (adds more colours either side of the colours in the image)
+ NSArray * copyColours = [NSArray arrayWithArray:colours];
+ NSMutableArray * flexibleColours = [NSMutableArray new];
+
+ float flexFactor = flexibility * 2 + 1;
+ float factor = flexFactor * flexFactor * 3; //(r,g,b) == *3
+ for (int n = 0; n<(dimension * dimension); n++){
+
+ NSArray * pixelColours = copyColours[n];
+ NSMutableArray * reds = [NSMutableArray new];
+ NSMutableArray * greens = [NSMutableArray new];
+ NSMutableArray * blues = [NSMutableArray new];
+
+ for (int p = 0; p<3; p++){
+
+ NSString * rgbStr = pixelColours[p];
+ int rgb = [rgbStr intValue];
+
+ for (int f = -flexibility; f= ranged_r-range && r<= ranged_r+range){
+ if (g>= ranged_g-range && g<= ranged_g+range){
+ if (b>= ranged_b-range && b<= ranged_b+range){
+ exclude = true;
+ }
+ }
+ }
+ }
+
+ if (!exclude){ [ranges addObject:key]; }
+ }
+
+
+
+ // If you want percentages to colours continue below
+ NSMutableDictionary * temp = [NSMutableDictionary new];
+ float totalCount = 0.0f;
+ for (NSString * rangeKey in ranges){
+ NSNumber * count = colourCounter[rangeKey];
+ totalCount += [count intValue];
+ temp[rangeKey]=count;
+ }
+
+ // Set percentages
+ NSMutableArray * colors = [NSMutableArray new];
+ for (NSString * key in temp){
+ float count = [temp[key] floatValue];
+ float percentage = count/totalCount;
+ //NSLog(@"%f",percentage);
+ NSArray * rgb = [key componentsSeparatedByString:@","];
+ float r = [rgb[0] floatValue];
+ float g = [rgb[1] floatValue];
+ float b = [rgb[2] floatValue];
+
+
+
+ [colors addObject:@{
+ @"color": @{
+ @"r": @(r),
+ @"g": @(g),
+ @"b": @(b)
+ },
+ @"percentage":@(percentage)
+ }];
+
+ }
+ return colors;
+}
+
+RCT_EXPORT_METHOD(getColors:(NSString *)path options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
+{
+
+ NSURL* aURL = [NSURL URLWithString:path];
+
+ if([path hasPrefix: @"/"]) {
+ UIImage *image = [UIImage imageWithContentsOfFile:path];
+
+ NSArray * colors = [self getColorsFromImage:image options:options];
+ resolve(colors);
+ } else {
+ ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
+
+ [library assetForURL:aURL resultBlock:^(ALAsset *asset) {
+ UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage] scale:0.5 orientation:UIImageOrientationUp];
+ NSArray * colors = [self getColorsFromImage:image options:options];
+ resolve(colors);
+
+ }
+ failureBlock:^(NSError *error) {
+ reject(@"asset error", @"cant't get colors from asset", error );
+ }];
+ }
+
+
+
+
+}
+
+- (NSString *)hexStringFromColor:(UIColor *)color {
+ const CGFloat *components = CGColorGetComponents(color.CGColor);
+
+ CGFloat r = components[0];
+ CGFloat g = components[1];
+ CGFloat b = components[2];
+
+ return [NSString stringWithFormat:@"#%02lX%02lX%02lX",
+ lroundf(r * 255),
+ lroundf(g * 255),
+ lroundf(b * 255)];
+}
+
+
++ (instancetype)sharedInstance {
+ static ColorGrabber *instance = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ if (instance == nil) {
+ instance = [[self alloc] init];
+ }
+ });
+ return instance;
+}
+
+
+@end
+
+
+
diff --git a/ios/components/ARGeosManager.h b/ios/components/ARGeosManager.h
new file mode 100644
index 00000000..76595137
--- /dev/null
+++ b/ios/components/ARGeosManager.h
@@ -0,0 +1,13 @@
+//
+// ARGeosManager.h
+// RCTARKit
+//
+// Created by Zehao Li on 9/28/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+
+@interface ARGeosManager : NSObject
+
+@end
diff --git a/ios/components/ARGeosManager.m b/ios/components/ARGeosManager.m
new file mode 100644
index 00000000..d5d7d9e3
--- /dev/null
+++ b/ios/components/ARGeosManager.m
@@ -0,0 +1,105 @@
+//
+// ARGeosManager.m
+// RCTARKit
+//
+// Created by Zehao Li on 9/28/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import "ARGeosManager.h"
+#import "RCTARKitNodes.h"
+
+@implementation ARGeosManager
+
+RCT_EXPORT_MODULE()
+
+
+RCT_EXPORT_METHOD(addBox:(SCNBox *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject ) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addGroup:(id)bla node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addSphere:(SCNSphere *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addCylinder:(SCNCylinder *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addCone:(SCNCone *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addPyramid:(SCNPyramid *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addTube:(SCNTube *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addTorus:(SCNTorus *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addCapsule:(SCNCapsule *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addPlane:(SCNPlane *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addShape:(SCNShape *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.geometry = geometry;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+RCT_EXPORT_METHOD(addLight:(SCNLight *)light node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ node.light = light;
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ resolve(nil);
+}
+
+
+RCT_EXPORT_METHOD(unmount:(NSString *)identifier) {
+ //NSLog(@"unmounting node: %@ ", identifier);
+ [[RCTARKitNodes sharedInstance] removeNode:identifier];
+}
+
+RCT_EXPORT_METHOD(updateNode:(NSString *)identifier properties:(NSDictionary *) properties resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
+ //NSLog(@"updating node: %@ ", identifier);
+ bool success = [[RCTARKitNodes sharedInstance] updateNode:identifier properties:properties];
+ if(success) {
+ resolve(nil);
+ } else {
+ reject(@"updateNodeError", @"could not update node", nil);
+ }
+}
+
+@end
+
diff --git a/ios/components/ARModelManager.h b/ios/components/ARModelManager.h
new file mode 100644
index 00000000..7966fd1c
--- /dev/null
+++ b/ios/components/ARModelManager.h
@@ -0,0 +1,13 @@
+//
+// ARModelManager.h
+// RCTARKit
+//
+// Created by Zehao Li on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+
+@interface ARModelManager : NSObject
+
+@end
diff --git a/ios/components/ARModelManager.m b/ios/components/ARModelManager.m
new file mode 100644
index 00000000..c7cb54ce
--- /dev/null
+++ b/ios/components/ARModelManager.m
@@ -0,0 +1,72 @@
+//
+// ARModelManager.m
+// RCTARKit
+//
+// Created by Zehao Li on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import "ARModelManager.h"
+#import "RCTARKitNodes.h"
+#import "RCTARKitIO.h"
+#import "RCTConvert+ARKit.h"
+
+@implementation ARModelManager
+
+RCT_EXPORT_MODULE()
+
+RCT_EXPORT_METHOD(mount:(NSDictionary *)property node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId) {
+ //NSLog(@"mounting node: %@ ", node.name);
+ // we need to mount first, otherwise, if the loading of the model is slow, it will be registered too late
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+ // we need to do the model loading in its own queue, otherwise it can block, so that react-to-native-calls get out of order
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSDictionary *model = property[@"model"];
+ CGFloat scale = 1;
+ if(model[@"scale"]) {
+ // deprecated
+ scale = [model[@"scale"] floatValue];
+ }
+
+ NSString *path = [NSString stringWithFormat:@"%@", model[@"file"]];
+ //NSLog(@"mounting model: %@ %@", node.name, path);
+ SCNNode *modelNode = [[RCTARKitIO sharedInstance] loadModel:path nodeName:model[@"node"] withAnimation:YES];
+ modelNode.scale = SCNVector3Make(scale, scale, scale);
+ // transfer some properties to modeNode like "castsShadow"
+ modelNode.castsShadow = node.castsShadow;
+
+
+ NSDictionary* materialJson;
+ if(property[@"material"] ) {
+ materialJson = property[@"material"];
+ }
+
+
+ if(materialJson) {
+ for(id idx in modelNode.geometry.materials) {
+ SCNMaterial* material = (SCNMaterial* )idx;
+ [RCTConvert setMaterialProperties:material properties:materialJson];
+ }
+ }
+
+ for(id idx in modelNode.childNodes) {
+ // iterate over all childnodes and apply shaders
+
+ SCNNode* childNode = (SCNNode *)idx;
+ childNode.castsShadow = node.castsShadow;
+ if(materialJson) {
+ for(id idx in childNode.geometry.materials) {
+ SCNMaterial* material = (SCNMaterial* )idx;
+ [RCTConvert setMaterialProperties:material properties:materialJson];
+ }
+ }
+
+ }
+ [node addChildNode:modelNode];
+ //NSLog(@"load model finished: %@", node.name);
+ });
+
+
+}
+
+@end
diff --git a/ios/components/ARTextManager.h b/ios/components/ARTextManager.h
new file mode 100644
index 00000000..59b01a75
--- /dev/null
+++ b/ios/components/ARTextManager.h
@@ -0,0 +1,13 @@
+//
+// ARTextManager.h
+// RCTARKit
+//
+// Created by Zehao Li on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import
+
+@interface ARTextManager : NSObject
+
+@end
diff --git a/ios/components/ARTextManager.m b/ios/components/ARTextManager.m
new file mode 100644
index 00000000..54f00884
--- /dev/null
+++ b/ios/components/ARTextManager.m
@@ -0,0 +1,22 @@
+//
+// ARTextManager.m
+// RCTARKit
+//
+// Created by Zehao Li on 8/12/17.
+// Copyright © 2017 HippoAR. All rights reserved.
+//
+
+#import "ARTextManager.h"
+#import "RCTARKitNodes.h"
+#import "RCTConvert+ARKit.h"
+
+@implementation ARTextManager
+
+RCT_EXPORT_MODULE()
+
+RCT_EXPORT_METHOD(mount:(SCNTextNode *)textNode node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId) {
+ [node addChildNode:textNode];
+ [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId];
+}
+
+@end
diff --git a/ios/components/RCTARKitSpriteView.h b/ios/components/RCTARKitSpriteView.h
new file mode 100644
index 00000000..aa6813b4
--- /dev/null
+++ b/ios/components/RCTARKitSpriteView.h
@@ -0,0 +1,22 @@
+//
+// RCTARKitSpriteView.h
+// RCTARKit
+//
+// Created by Marco Wettstein on 04.03.18.
+// Copyright © 2018 HippoAR. All rights reserved.
+//
+
+#import
+#import "RCTARKit.h"
+#import
+#import "RCTARKitDelegate.h"
+
+@interface RCTARKitSpriteView : RCTView
+
+@property (nonatomic, assign) SCNVector3 position3D;
+@property (nonatomic, assign) float transitionDuration;
+
+@end
+
+
+
diff --git a/ios/components/RCTARKitSpriteView.m b/ios/components/RCTARKitSpriteView.m
new file mode 100644
index 00000000..44813438
--- /dev/null
+++ b/ios/components/RCTARKitSpriteView.m
@@ -0,0 +1,52 @@
+//
+// RCTARKitSpriteView.m
+// RCTARKit
+//
+// Created by Marco Wettstein on 04.03.18.
+// Copyright © 2018 HippoAR. All rights reserved.
+//
+
+#import
+
+
+#import "RCTARKitSpriteView.h"
+#import "RCTConvert+ARKit.h"
+#import
+#import
+
+@implementation RCTARKitSpriteView {
+
+}
+
+
+
+- (CGAffineTransform)getTransform {
+ SCNVector3 point = [[ARKit sharedInstance] projectPoint:self.position3D];
+
+ // the sprite is behind the camera so push it off screen
+ float yTransform = point.z < 1 ? point.y : 10000;
+
+ CGAffineTransform t = CGAffineTransformMakeTranslation(point.x, yTransform);
+ return t;
+}
+
+- (void)didMoveToSuperview {
+ // set it once
+ CGAffineTransform t = [self getTransform];
+ [self setTransform:t];
+}
+
+- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time {
+ [self performSelectorOnMainThread:@selector(setTransformByProject) withObject:nil waitUntilDone:NO];
+
+}
+
+- (void)setTransformByProject {
+ CGAffineTransform t = [self getTransform];
+ [UIView beginAnimations:nil context:NULL];
+ [UIView setAnimationDuration:self.transitionDuration];
+ [self setTransform:t];
+ [UIView commitAnimations];
+}
+
+@end
diff --git a/ios/components/RCTARKitSpriteViewManager.h b/ios/components/RCTARKitSpriteViewManager.h
new file mode 100644
index 00000000..5a705b90
--- /dev/null
+++ b/ios/components/RCTARKitSpriteViewManager.h
@@ -0,0 +1,5 @@
+#import
+
+@interface RCTARKitSpriteViewManager : RCTViewManager
+
+@end
diff --git a/ios/components/RCTARKitSpriteViewManager.m b/ios/components/RCTARKitSpriteViewManager.m
new file mode 100644
index 00000000..39add846
--- /dev/null
+++ b/ios/components/RCTARKitSpriteViewManager.m
@@ -0,0 +1,46 @@
+#import "RCTARKitSpriteViewManager.h"
+#import "RCTARKitSpriteView.h"
+#import
+#import
+#import "RCTARKit.h"
+
+@implementation RCTARKitSpriteViewManager
+
+RCT_EXPORT_MODULE();
+
+@synthesize bridge = _bridge;
+
+RCT_EXPORT_VIEW_PROPERTY(position3D, SCNVector3)
+RCT_EXPORT_VIEW_PROPERTY(transitionDuration, float)
+
+- (UIView *)view
+{
+ UIView * view = [[RCTARKitSpriteView alloc] init];
+
+ return view;
+}
+
+
+RCT_EXPORT_METHOD(startAnimation:(nonnull NSNumber *)reactTag)
+{
+ [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
+ RCTARKitSpriteView *view = viewRegistry[reactTag];
+ if ([view isKindOfClass:[RCTARKitSpriteView class]]) {
+ [[ARKit sharedInstance] addRendererDelegates:view];
+ }
+ }];
+}
+
+RCT_EXPORT_METHOD(stopAnimation:(nonnull NSNumber *)reactTag)
+{
+ [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
+ RCTARKitSpriteView *view = viewRegistry[reactTag];
+ if ([view isKindOfClass:[RCTARKitSpriteView class]]) {
+ [[ARKit sharedInstance] removeRendererDelegates:view];
+ }
+ }];
+}
+
+
+
+@end
diff --git a/lib/colorUtils.js b/lib/colorUtils.js
new file mode 100644
index 00000000..d41e81e9
--- /dev/null
+++ b/lib/colorUtils.js
@@ -0,0 +1,43 @@
+// kudos to to https://github.com/jverhoelen/camanjs-whitebalance/blob/master/src/caman.whitebalance.js
+import { NativeModules } from 'react-native';
+
+const ARKitManager = NativeModules.ARKitManager;
+
+export const colorTemperatureToRgb = temperature => {
+ const m = global.Math;
+ const temp = temperature / 100;
+ let r;
+ let g;
+ let b;
+
+ if (temp <= 66) {
+ r = 255;
+ g = m.min(m.max(99.4708025861 * m.log(temp) - 161.1195681661, 0), 255);
+ } else {
+ r = m.min(m.max(329.698727446 * m.pow(temp - 60, -0.1332047592), 0), 255);
+ g = m.min(m.max(288.1221695283 * m.pow(temp - 60, -0.0755148492), 0), 255);
+ }
+
+ if (temp >= 66) {
+ b = 255;
+ } else if (temp <= 19) {
+ b = 0;
+ } else {
+ b = temp - 10;
+ b = m.min(m.max(138.5177312231 * m.log(b) - 305.0447927307, 0), 255);
+ }
+
+ return {
+ r,
+ g,
+ b,
+ };
+};
+export const whiteBalanceWithTemperature = ({ r, g, b }, temperature) => {
+ const temperatureRgb = colorTemperatureToRgb(temperature);
+ return {
+ r: r * 255 / temperatureRgb.r,
+ g: g * 255 / temperatureRgb.g,
+ b: b * 255 / temperatureRgb.b,
+ };
+};
diff --git a/lib/pickColors.js b/lib/pickColors.js
new file mode 100644
index 00000000..2d83a1fc
--- /dev/null
+++ b/lib/pickColors.js
@@ -0,0 +1,65 @@
+import { NativeModules } from 'react-native';
+
+import { whiteBalanceWithTemperature } from './colorUtils';
+
+const ARKitManager = NativeModules.ARKitManager;
+
+const doWhiteBalance = async (colors, { includeRawColors }) => {
+ const lightEstimation = await ARKitManager.getCurrentLightEstimation();
+
+ if (!lightEstimation) {
+ return colors;
+ }
+
+ return colors.map(({ color, ...p }) => ({
+ color: whiteBalanceWithTemperature(
+ color,
+ lightEstimation.ambientColorTemperature,
+ ),
+ ...p,
+ ...(includeRawColors ? { colorRaw: color } : {}),
+ }));
+};
+export const pickColorsFromFile = async (
+ filePath,
+ {
+ whiteBalance = true,
+ includeRawColors = false,
+ // color grabber options, currently undocumented
+ range = 40,
+ dimension = 4,
+ flexibility = 5,
+ } = {},
+) => {
+ const colors = await ARKitManager.pickColorsRawFromFile(filePath, {
+ range,
+ dimension,
+ flexibility,
+ });
+ if (!whiteBalance) {
+ return colors;
+ }
+ return doWhiteBalance(colors, { includeRawColors });
+};
+export const pickColors = async (
+ {
+ whiteBalance = true,
+ includeRawColors = false,
+ selection = null,
+ // color grabber options, currently undocumented
+ range = 40,
+ dimension = 4,
+ flexibility = 5,
+ } = {},
+) => {
+ const colors = await ARKitManager.pickColorsRaw({
+ selection,
+ range,
+ dimension,
+ flexibility,
+ });
+ if (!whiteBalance) {
+ return colors;
+ }
+ return doWhiteBalance(colors, { includeRawColors });
+};
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..8ea2eda5
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2490 @@
+{
+ "name": "react-native-arkit",
+ "version": "0.8.0-beta.2",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@panter/react-animation-frame": {
+ "version": "0.3.7",
+ "resolved": "https://registry.npmjs.org/@panter/react-animation-frame/-/react-animation-frame-0.3.7.tgz",
+ "integrity": "sha1-MaDXaD5KjS4bKf9RLK02UTvUPQA=",
+ "requires": {
+ "react": "15.4.2"
+ },
+ "dependencies": {
+ "react": {
+ "version": "15.4.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-15.4.2.tgz",
+ "integrity": "sha1-QfeZGyYYU5K6m66WyIiefgGDl+8=",
+ "requires": {
+ "fbjs": "0.8.15",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1"
+ }
+ }
+ }
+ },
+ "acorn": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz",
+ "integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+ "dev": true,
+ "requires": {
+ "acorn": "3.3.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+ "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+ "dev": true
+ }
+ }
+ },
+ "ajv": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz",
+ "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=",
+ "dev": true,
+ "requires": {
+ "co": "4.6.0",
+ "fast-deep-equal": "1.0.0",
+ "json-schema-traverse": "0.3.1",
+ "json-stable-stringify": "1.0.1"
+ }
+ },
+ "ajv-keywords": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz",
+ "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz",
+ "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
+ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "1.0.3"
+ }
+ },
+ "aria-query": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.0.tgz",
+ "integrity": "sha512-/r2lHl09V3o74+2MLKEdewoj37YZqiQZnfen1O4iNlrOjUgeKuu1U2yF3iKh6HJxqF+OXkLMfQv65Z/cvxD6vA==",
+ "dev": true,
+ "requires": {
+ "ast-types-flow": "0.0.7"
+ }
+ },
+ "array-includes": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
+ "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
+ "dev": true,
+ "requires": {
+ "define-properties": "1.1.2",
+ "es-abstract": "1.8.2"
+ }
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "1.0.3"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ },
+ "ast-types-flow": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+ "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
+ "dev": true
+ },
+ "axobject-query": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz",
+ "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=",
+ "dev": true,
+ "requires": {
+ "ast-types-flow": "0.0.7"
+ }
+ },
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "dev": true,
+ "requires": {
+ "chalk": "1.1.3",
+ "esutils": "2.0.2",
+ "js-tokens": "3.0.2"
+ }
+ },
+ "babel-eslint": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz",
+ "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0"
+ }
+ },
+ "babel-helper-builder-react-jsx": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz",
+ "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "esutils": "2.0.2"
+ }
+ },
+ "babel-helper-call-delegate": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
+ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
+ "dev": true,
+ "requires": {
+ "babel-helper-hoist-variables": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-define-map": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
+ "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "lodash": "4.17.4"
+ }
+ },
+ "babel-helper-function-name": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
+ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
+ "dev": true,
+ "requires": {
+ "babel-helper-get-function-arity": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-get-function-arity": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
+ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-hoist-variables": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
+ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-optimise-call-expression": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
+ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-replace-supers": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
+ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
+ "dev": true,
+ "requires": {
+ "babel-helper-optimise-call-expression": "6.24.1",
+ "babel-messages": "6.23.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-check-es2015-constants": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
+ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-react-transform": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz",
+ "integrity": "sha1-UVu/qZaJOYEULZCx+bFjXeKZUQk=",
+ "dev": true,
+ "requires": {
+ "lodash": "4.17.4"
+ }
+ },
+ "babel-plugin-syntax-async-functions": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
+ "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=",
+ "dev": true
+ },
+ "babel-plugin-syntax-class-properties": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
+ "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=",
+ "dev": true
+ },
+ "babel-plugin-syntax-flow": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
+ "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=",
+ "dev": true
+ },
+ "babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
+ "dev": true
+ },
+ "babel-plugin-syntax-object-rest-spread": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
+ "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=",
+ "dev": true
+ },
+ "babel-plugin-syntax-trailing-function-commas": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
+ "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=",
+ "dev": true
+ },
+ "babel-plugin-transform-class-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
+ "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "6.24.1",
+ "babel-plugin-syntax-class-properties": "6.13.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-arrow-functions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
+ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-block-scoping": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
+ "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "lodash": "4.17.4"
+ }
+ },
+ "babel-plugin-transform-es2015-classes": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
+ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
+ "dev": true,
+ "requires": {
+ "babel-helper-define-map": "6.26.0",
+ "babel-helper-function-name": "6.24.1",
+ "babel-helper-optimise-call-expression": "6.24.1",
+ "babel-helper-replace-supers": "6.24.1",
+ "babel-messages": "6.23.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-computed-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
+ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-destructuring": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
+ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-for-of": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
+ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-function-name": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
+ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-literals": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
+ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-commonjs": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz",
+ "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-strict-mode": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-parameters": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
+ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
+ "dev": true,
+ "requires": {
+ "babel-helper-call-delegate": "6.24.1",
+ "babel-helper-get-function-arity": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-shorthand-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
+ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-spread": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
+ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-template-literals": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
+ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-flow-strip-types": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz",
+ "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-flow": "6.18.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-object-assign": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz",
+ "integrity": "sha1-+Z0vZvGgsNSY40bFNZaEdAyqILo=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-object-rest-spread": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz",
+ "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-object-rest-spread": "6.13.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-react-display-name": {
+ "version": "6.25.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz",
+ "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-react-jsx": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz",
+ "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=",
+ "dev": true,
+ "requires": {
+ "babel-helper-builder-react-jsx": "6.26.0",
+ "babel-plugin-syntax-jsx": "6.18.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-react-jsx-source": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz",
+ "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-jsx": "6.18.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-regenerator": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
+ "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=",
+ "dev": true,
+ "requires": {
+ "regenerator-transform": "0.10.1"
+ }
+ },
+ "babel-plugin-transform-strict-mode": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
+ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-preset-react-native": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-react-native/-/babel-preset-react-native-2.1.0.tgz",
+ "integrity": "sha1-kBPr2C2hyIECv1iIEP9Z4gnKK4o=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-check-es2015-constants": "6.22.0",
+ "babel-plugin-react-transform": "2.0.2",
+ "babel-plugin-syntax-async-functions": "6.13.0",
+ "babel-plugin-syntax-class-properties": "6.13.0",
+ "babel-plugin-syntax-flow": "6.18.0",
+ "babel-plugin-syntax-jsx": "6.18.0",
+ "babel-plugin-syntax-trailing-function-commas": "6.22.0",
+ "babel-plugin-transform-class-properties": "6.24.1",
+ "babel-plugin-transform-es2015-arrow-functions": "6.22.0",
+ "babel-plugin-transform-es2015-block-scoping": "6.26.0",
+ "babel-plugin-transform-es2015-classes": "6.24.1",
+ "babel-plugin-transform-es2015-computed-properties": "6.24.1",
+ "babel-plugin-transform-es2015-destructuring": "6.23.0",
+ "babel-plugin-transform-es2015-for-of": "6.23.0",
+ "babel-plugin-transform-es2015-function-name": "6.24.1",
+ "babel-plugin-transform-es2015-literals": "6.22.0",
+ "babel-plugin-transform-es2015-modules-commonjs": "6.26.0",
+ "babel-plugin-transform-es2015-parameters": "6.24.1",
+ "babel-plugin-transform-es2015-shorthand-properties": "6.24.1",
+ "babel-plugin-transform-es2015-spread": "6.22.0",
+ "babel-plugin-transform-es2015-template-literals": "6.22.0",
+ "babel-plugin-transform-flow-strip-types": "6.22.0",
+ "babel-plugin-transform-object-assign": "6.22.0",
+ "babel-plugin-transform-object-rest-spread": "6.26.0",
+ "babel-plugin-transform-react-display-name": "6.25.0",
+ "babel-plugin-transform-react-jsx": "6.24.1",
+ "babel-plugin-transform-react-jsx-source": "6.22.0",
+ "babel-plugin-transform-regenerator": "6.26.0",
+ "react-transform-hmr": "1.0.4"
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "dev": true,
+ "requires": {
+ "core-js": "2.5.1",
+ "regenerator-runtime": "0.11.0"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
+ "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=",
+ "dev": true
+ }
+ }
+ },
+ "babel-template": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "lodash": "4.17.4"
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "6.26.0",
+ "babel-messages": "6.23.0",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "debug": "2.6.9",
+ "globals": "9.18.0",
+ "invariant": "2.2.2",
+ "lodash": "4.17.4"
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "esutils": "2.0.2",
+ "lodash": "4.17.4",
+ "to-fast-properties": "1.0.3"
+ }
+ },
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
+ "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+ "dev": true,
+ "requires": {
+ "balanced-match": "1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "caller-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+ "dev": true,
+ "requires": {
+ "callsites": "0.2.0"
+ }
+ },
+ "callsites": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "2.2.1",
+ "escape-string-regexp": "1.0.5",
+ "has-ansi": "2.0.0",
+ "strip-ansi": "3.0.1",
+ "supports-color": "2.0.0"
+ }
+ },
+ "circular-json": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "2.0.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
+ "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "readable-stream": "2.3.3",
+ "typedarray": "0.0.6"
+ }
+ },
+ "contains-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "dev": true
+ },
+ "core-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "create-react-class": {
+ "version": "15.6.0",
+ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz",
+ "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=",
+ "dev": true,
+ "requires": {
+ "fbjs": "0.8.15",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1"
+ }
+ },
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "4.1.1",
+ "shebang-command": "1.2.0",
+ "which": "1.3.0"
+ }
+ },
+ "damerau-levenshtein": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
+ "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
+ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
+ "dev": true,
+ "requires": {
+ "foreach": "2.0.5",
+ "object-keys": "1.0.11"
+ }
+ },
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "dev": true,
+ "requires": {
+ "globby": "5.0.0",
+ "is-path-cwd": "1.0.0",
+ "is-path-in-cwd": "1.0.0",
+ "object-assign": "4.1.1",
+ "pify": "2.3.0",
+ "pinkie-promise": "2.0.1",
+ "rimraf": "2.6.2"
+ }
+ },
+ "doctrine": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
+ "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=",
+ "dev": true,
+ "requires": {
+ "esutils": "2.0.2",
+ "isarray": "1.0.0"
+ }
+ },
+ "dom-walk": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
+ "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
+ "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==",
+ "dev": true
+ },
+ "encoding": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "requires": {
+ "iconv-lite": "0.4.19"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
+ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.2.tgz",
+ "integrity": "sha512-dvhwFL3yjQxNNsOWx6exMlaDrRHCRGMQlnx5lsXDCZ/J7G/frgIIl94zhZSp/galVAYp7VzPi1OrAHta89/yGQ==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "1.1.1",
+ "function-bind": "1.1.1",
+ "has": "1.0.1",
+ "is-callable": "1.1.3",
+ "is-regex": "1.0.4"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
+ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
+ "dev": true,
+ "requires": {
+ "is-callable": "1.1.3",
+ "is-date-object": "1.0.1",
+ "is-symbol": "1.0.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint": {
+ "version": "4.7.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.7.2.tgz",
+ "integrity": "sha1-/29fUZOEiifum2J74+c/ucteZi4=",
+ "dev": true,
+ "requires": {
+ "ajv": "5.2.2",
+ "babel-code-frame": "6.26.0",
+ "chalk": "2.1.0",
+ "concat-stream": "1.6.0",
+ "cross-spawn": "5.1.0",
+ "debug": "3.0.1",
+ "doctrine": "2.0.0",
+ "eslint-scope": "3.7.1",
+ "espree": "3.5.1",
+ "esquery": "1.0.0",
+ "estraverse": "4.2.0",
+ "esutils": "2.0.2",
+ "file-entry-cache": "2.0.0",
+ "functional-red-black-tree": "1.0.1",
+ "glob": "7.1.2",
+ "globals": "9.18.0",
+ "ignore": "3.3.5",
+ "imurmurhash": "0.1.4",
+ "inquirer": "3.3.0",
+ "is-resolvable": "1.0.0",
+ "js-yaml": "3.10.0",
+ "json-stable-stringify": "1.0.1",
+ "levn": "0.3.0",
+ "lodash": "4.17.4",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.1",
+ "natural-compare": "1.4.0",
+ "optionator": "0.8.2",
+ "path-is-inside": "1.0.2",
+ "pluralize": "7.0.0",
+ "progress": "2.0.0",
+ "require-uncached": "1.0.3",
+ "semver": "5.4.1",
+ "strip-ansi": "4.0.0",
+ "strip-json-comments": "2.0.1",
+ "table": "4.0.1",
+ "text-table": "0.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
+ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
+ "dev": true,
+ "requires": {
+ "color-convert": "1.9.0"
+ }
+ },
+ "chalk": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
+ "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "3.2.0",
+ "escape-string-regexp": "1.0.5",
+ "supports-color": "4.4.0"
+ }
+ },
+ "debug": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.0.1.tgz",
+ "integrity": "sha512-6nVc6S36qbt/mutyt+UGMnawAMrPDZUPQjRZI3FS9tCtDRhvxJbK79unYBLPi+z5SLXQ3ftoVBFCblQtNSls8w==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "3.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
+ "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "2.0.0"
+ }
+ }
+ }
+ },
+ "eslint-config-airbnb": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-15.1.0.tgz",
+ "integrity": "sha512-m0q9fiMBzDAIbirlGnpJNWToIhdhJmXXnMG+IFflYzzod9231ZhtmGKegKg8E9T8F1YuVaDSU1FnCm5b9iXVhQ==",
+ "dev": true,
+ "requires": {
+ "eslint-config-airbnb-base": "11.3.2"
+ }
+ },
+ "eslint-config-airbnb-base": {
+ "version": "11.3.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.2.tgz",
+ "integrity": "sha512-/fhjt/VqzBA2SRsx7ErDtv6Ayf+XLw9LIOqmpBuHFCVwyJo2EtzGWMB9fYRFBoWWQLxmNmCpenNiH0RxyeS41w==",
+ "dev": true,
+ "requires": {
+ "eslint-restricted-globals": "0.1.1"
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.6.0.tgz",
+ "integrity": "sha1-8h2w67Q4rWePuYlGCXxLsZi+/Mw=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "5.0.1"
+ }
+ },
+ "eslint-import-resolver-node": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz",
+ "integrity": "sha512-yUtXS15gIcij68NmXmP9Ni77AQuCN0itXbCc/jWd8C6/yKZaSNXicpC8cgvjnxVdmfsosIXrjpzFq7GcDryb6A==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "resolve": "1.4.0"
+ }
+ },
+ "eslint-module-utils": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz",
+ "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "pkg-dir": "1.0.0"
+ }
+ },
+ "eslint-plugin-flowtype": {
+ "version": "2.35.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.35.1.tgz",
+ "integrity": "sha512-YTCeVsMOi3ga8PJjdAV97FaTNXNknzrO+4ZDMHJN65i4uMjL5KgfgQZpyVsBirWOfgXMKRduxpsyM64K/0LbXw==",
+ "dev": true,
+ "requires": {
+ "lodash": "4.17.4"
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz",
+ "integrity": "sha512-HGYmpU9f/zJaQiKNQOVfHUh2oLWW3STBrCgH0sHTX1xtsxYlH1zjLh8FlQGEIdZSdTbUMaV36WaZ6ImXkenGxQ==",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "1.1.1",
+ "contains-path": "0.1.0",
+ "debug": "2.6.9",
+ "doctrine": "1.5.0",
+ "eslint-import-resolver-node": "0.3.1",
+ "eslint-module-utils": "2.1.1",
+ "has": "1.0.1",
+ "lodash.cond": "4.5.2",
+ "minimatch": "3.0.4",
+ "read-pkg-up": "2.0.0"
+ },
+ "dependencies": {
+ "doctrine": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "dev": true,
+ "requires": {
+ "esutils": "2.0.2",
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "eslint-plugin-jsx-a11y": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz",
+ "integrity": "sha512-5I9SpoP7gT4wBFOtXT8/tXNPYohHBVfyVfO17vkbC7r9kEIxYJF12D3pKqhk8+xnk12rfxKClS3WCFpVckFTPQ==",
+ "dev": true,
+ "requires": {
+ "aria-query": "0.7.0",
+ "array-includes": "3.0.3",
+ "ast-types-flow": "0.0.7",
+ "axobject-query": "0.1.0",
+ "damerau-levenshtein": "1.0.4",
+ "emoji-regex": "6.5.1",
+ "jsx-ast-utils": "1.4.1"
+ }
+ },
+ "eslint-plugin-prettier": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.3.1.tgz",
+ "integrity": "sha512-AV8shBlGN9tRZffj5v/f4uiQWlP3qiQ+lh+BhTqRLuKSyczx+HRWVkVZaf7dOmguxghAH1wftnou/JUEEChhGg==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "1.1.2",
+ "jest-docblock": "21.1.0"
+ }
+ },
+ "eslint-plugin-react": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.3.0.tgz",
+ "integrity": "sha512-7L6QEOxm7XhcDoe+U9Qt7GJjU6KeQOX9jCLGE8EPGF6FQbwZ9LgcBzsjXIZv9oYvNQlvQZmLjJs76xEeWsI4QA==",
+ "dev": true,
+ "requires": {
+ "doctrine": "2.0.0",
+ "has": "1.0.1",
+ "jsx-ast-utils": "2.0.1",
+ "prop-types": "15.5.10"
+ },
+ "dependencies": {
+ "jsx-ast-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
+ "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
+ "dev": true,
+ "requires": {
+ "array-includes": "3.0.3"
+ }
+ }
+ }
+ },
+ "eslint-restricted-globals": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
+ "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=",
+ "dev": true
+ },
+ "eslint-scope": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
+ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
+ "dev": true,
+ "requires": {
+ "esrecurse": "4.2.0",
+ "estraverse": "4.2.0"
+ }
+ },
+ "espree": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.1.tgz",
+ "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=",
+ "dev": true,
+ "requires": {
+ "acorn": "5.1.2",
+ "acorn-jsx": "3.0.1"
+ }
+ },
+ "esprima": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
+ "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
+ "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=",
+ "dev": true,
+ "requires": {
+ "estraverse": "4.2.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz",
+ "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=",
+ "dev": true,
+ "requires": {
+ "estraverse": "4.2.0",
+ "object-assign": "4.1.1"
+ }
+ },
+ "estraverse": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "external-editor": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.5.tgz",
+ "integrity": "sha512-Msjo64WT5W+NhOpQXh0nOHm+n0RfU1QUwDnKYvJ8dEJ8zlwLrqXNTv5mSUTJpepf41PDJGyhueTw2vNZW+Fr/w==",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "0.4.19",
+ "jschardet": "1.5.1",
+ "tmp": "0.0.33"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
+ "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
+ },
+ "fast-diff": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
+ "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "fbjs": {
+ "version": "0.8.15",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.15.tgz",
+ "integrity": "sha1-TwaV/fzBbDfAsH+s7Iy0xAkWhbk=",
+ "requires": {
+ "core-js": "1.2.7",
+ "isomorphic-fetch": "2.2.1",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "promise": "7.3.1",
+ "setimmediate": "1.0.5",
+ "ua-parser-js": "0.7.14"
+ }
+ },
+ "figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+ "dev": true,
+ "requires": {
+ "flat-cache": "1.2.2",
+ "object-assign": "4.1.1"
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "2.1.0",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "flat-cache": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz",
+ "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=",
+ "dev": true,
+ "requires": {
+ "circular-json": "0.3.3",
+ "del": "2.2.2",
+ "graceful-fs": "4.1.11",
+ "write": "0.2.1"
+ }
+ },
+ "foreach": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "get-stdin": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
+ "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "1.0.0",
+ "inflight": "1.0.6",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "path-is-absolute": "1.0.1"
+ }
+ },
+ "global": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
+ "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
+ "dev": true,
+ "requires": {
+ "min-document": "2.19.0",
+ "process": "0.5.2"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+ "dev": true
+ },
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true,
+ "requires": {
+ "array-union": "1.0.2",
+ "arrify": "1.0.1",
+ "glob": "7.1.2",
+ "object-assign": "4.1.1",
+ "pify": "2.3.0",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+ "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
+ "dev": true,
+ "requires": {
+ "function-bind": "1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "2.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
+ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
+ "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
+ },
+ "ignore": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz",
+ "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==",
+ "dev": true
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "1.4.0",
+ "wrappy": "1.0.2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
+ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "3.0.0",
+ "chalk": "2.1.0",
+ "cli-cursor": "2.1.0",
+ "cli-width": "2.2.0",
+ "external-editor": "2.0.5",
+ "figures": "2.0.0",
+ "lodash": "4.17.4",
+ "mute-stream": "0.0.7",
+ "run-async": "2.3.0",
+ "rx-lite": "4.0.8",
+ "rx-lite-aggregates": "4.0.8",
+ "string-width": "2.1.1",
+ "strip-ansi": "4.0.0",
+ "through": "2.3.8"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
+ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
+ "dev": true,
+ "requires": {
+ "color-convert": "1.9.0"
+ }
+ },
+ "chalk": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
+ "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "3.2.0",
+ "escape-string-regexp": "1.0.5",
+ "supports-color": "4.4.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "3.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
+ "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "2.0.0"
+ }
+ }
+ }
+ },
+ "invariant": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
+ "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
+ "dev": true,
+ "requires": {
+ "loose-envify": "1.3.1"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "1.1.1"
+ }
+ },
+ "is-callable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
+ "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
+ "dev": true
+ },
+ "is-date-object": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
+ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz",
+ "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "1.0.2"
+ }
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+ "dev": true,
+ "requires": {
+ "has": "1.0.1"
+ }
+ },
+ "is-resolvable": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
+ "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=",
+ "dev": true,
+ "requires": {
+ "tryit": "1.0.3"
+ }
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ },
+ "is-symbol": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
+ "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isomorphic-fetch": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+ "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+ "requires": {
+ "node-fetch": "1.7.3",
+ "whatwg-fetch": "2.0.3"
+ }
+ },
+ "jest-docblock": {
+ "version": "21.1.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.1.0.tgz",
+ "integrity": "sha512-ai3olFJ/3cSd60klaRIcC/Cb44/RsJ69qS8uXxfWXEbnbDqjcbl5K8Z5ekfY15hgVZGSAiLz7pOwfjIBE/yJVw==",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
+ },
+ "js-yaml": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
+ "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
+ "dev": true,
+ "requires": {
+ "argparse": "1.0.9",
+ "esprima": "4.0.0"
+ }
+ },
+ "jschardet": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.5.1.tgz",
+ "integrity": "sha512-vE2hT1D0HLZCLLclfBSfkfTTedhVj0fubHpJBHKwwUWX0nSbhPAfk+SG9rTX95BYNmau8rGFfCeaT6T5OW1C2A==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+ "dev": true
+ },
+ "json-stable-stringify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+ "dev": true,
+ "requires": {
+ "jsonify": "0.0.0"
+ }
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "dev": true
+ },
+ "jsx-ast-utils": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz",
+ "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=",
+ "dev": true
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "1.1.2",
+ "type-check": "0.3.2"
+ }
+ },
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "parse-json": "2.2.0",
+ "pify": "2.3.0",
+ "strip-bom": "3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "2.0.0",
+ "path-exists": "3.0.0"
+ },
+ "dependencies": {
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ }
+ }
+ },
+ "lodash": {
+ "version": "4.17.4",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
+ "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
+ },
+ "lodash.cond": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
+ "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=",
+ "dev": true
+ },
+ "loose-envify": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
+ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+ "requires": {
+ "js-tokens": "3.0.2"
+ }
+ },
+ "lru-cache": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
+ "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "1.0.2",
+ "yallist": "2.1.2"
+ }
+ },
+ "mimic-fn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz",
+ "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=",
+ "dev": true
+ },
+ "min-document": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+ "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
+ "dev": true,
+ "requires": {
+ "dom-walk": "0.1.1"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "1.1.8"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "node-fetch": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+ "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "requires": {
+ "encoding": "0.1.12",
+ "is-stream": "1.1.0"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "2.5.0",
+ "is-builtin-module": "1.0.0",
+ "semver": "5.4.1",
+ "validate-npm-package-license": "3.0.1"
+ }
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "object-keys": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
+ "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1.0.2"
+ }
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "1.1.0"
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true,
+ "requires": {
+ "deep-is": "0.1.3",
+ "fast-levenshtein": "2.0.6",
+ "levn": "0.3.0",
+ "prelude-ls": "1.1.2",
+ "type-check": "0.3.2",
+ "wordwrap": "1.0.0"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
+ "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=",
+ "dev": true
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "1.1.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "1.3.1"
+ }
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
+ "requires": {
+ "pify": "2.3.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "2.0.4"
+ }
+ },
+ "pkg-dir": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
+ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
+ "dev": true,
+ "requires": {
+ "find-up": "1.1.2"
+ }
+ },
+ "pluralize": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+ "dev": true
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "prettier": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.7.0.tgz",
+ "integrity": "sha512-kIbA3UE9sYGiVCxlWg92EOHWZqte6EAkC7DS5je6NaL8g3Uw1rWe0eH+UX4Hy5OEiR9aql2vVMHeg6lR4YGxYg==",
+ "dev": true
+ },
+ "private": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz",
+ "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=",
+ "dev": true
+ },
+ "process": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
+ "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
+ "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
+ "dev": true
+ },
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "requires": {
+ "asap": "2.0.6"
+ }
+ },
+ "prop-types": {
+ "version": "15.5.10",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz",
+ "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=",
+ "requires": {
+ "fbjs": "0.8.15",
+ "loose-envify": "1.3.1"
+ }
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
+ "react": {
+ "version": "16.0.0-alpha.12",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.0.0-alpha.12.tgz",
+ "integrity": "sha1-jFlIUoFIXfMZtvd2gtjdBiHAgZQ=",
+ "dev": true,
+ "requires": {
+ "create-react-class": "15.6.0",
+ "fbjs": "0.8.15",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "prop-types": "15.5.10"
+ }
+ },
+ "react-deep-force-update": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz",
+ "integrity": "sha1-vNMUeAJ7ZLMznxCJIatSC0MT3Cw=",
+ "dev": true
+ },
+ "react-proxy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz",
+ "integrity": "sha1-nb/Z2SdSjDqp9ETkVYw3gwq4wmo=",
+ "dev": true,
+ "requires": {
+ "lodash": "4.17.4",
+ "react-deep-force-update": "1.1.1"
+ }
+ },
+ "react-transform-hmr": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz",
+ "integrity": "sha1-4aQL0Krvxy6N/Xp82gmvhQZjl7s=",
+ "dev": true,
+ "requires": {
+ "global": "4.3.2",
+ "react-proxy": "1.1.8"
+ }
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "2.0.0",
+ "normalize-package-data": "2.4.0",
+ "path-type": "2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "requires": {
+ "find-up": "2.1.0",
+ "read-pkg": "2.0.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "2.0.0"
+ }
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "1.0.7",
+ "safe-buffer": "5.1.1",
+ "string_decoder": "1.0.3",
+ "util-deprecate": "1.0.2"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
+ "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==",
+ "dev": true
+ },
+ "regenerator-transform": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
+ "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "private": "0.1.7"
+ }
+ },
+ "require-uncached": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+ "dev": true,
+ "requires": {
+ "caller-path": "0.1.0",
+ "resolve-from": "1.0.1"
+ }
+ },
+ "resolve": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
+ "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==",
+ "dev": true,
+ "requires": {
+ "path-parse": "1.0.5"
+ }
+ },
+ "resolve-from": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
+ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "2.0.1",
+ "signal-exit": "3.0.2"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "dev": true,
+ "requires": {
+ "glob": "7.1.2"
+ }
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "2.1.0"
+ }
+ },
+ "rx-lite": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
+ "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
+ "dev": true
+ },
+ "rx-lite-aggregates": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
+ "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+ "dev": true,
+ "requires": {
+ "rx-lite": "4.0.8"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
+ "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
+ "dev": true
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
+ "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
+ "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
+ "dev": true,
+ "requires": {
+ "spdx-license-ids": "1.2.2"
+ }
+ },
+ "spdx-expression-parse": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
+ "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=",
+ "dev": true
+ },
+ "spdx-license-ids": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
+ "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "2.0.0",
+ "strip-ansi": "4.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "3.0.0"
+ }
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "2.1.1"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ },
+ "table": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/table/-/table-4.0.1.tgz",
+ "integrity": "sha1-qBFsEz+sLGH0pCCrbN9cTWHw5DU=",
+ "dev": true,
+ "requires": {
+ "ajv": "4.11.8",
+ "ajv-keywords": "1.5.1",
+ "chalk": "1.1.3",
+ "lodash": "4.17.4",
+ "slice-ansi": "0.0.4",
+ "string-width": "2.1.1"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
+ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+ "dev": true,
+ "requires": {
+ "co": "4.6.0",
+ "json-stable-stringify": "1.0.1"
+ }
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "1.0.2"
+ }
+ },
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+ "dev": true
+ },
+ "tryit": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
+ "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "1.1.2"
+ }
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "ua-parser-js": {
+ "version": "0.7.14",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz",
+ "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o="
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
+ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "1.0.2",
+ "spdx-expression-parse": "1.0.4"
+ }
+ },
+ "whatwg-fetch": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
+ "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
+ },
+ "which": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
+ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
+ "dev": true,
+ "requires": {
+ "isexe": "2.0.0"
+ }
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+ "dev": true,
+ "requires": {
+ "mkdirp": "0.5.1"
+ }
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
index 2a845b90..111b5b8b 100644
--- a/package.json
+++ b/package.json
@@ -4,26 +4,40 @@
"type": "git",
"url": "https://github.com/HippoAR/react-native-arkit.git"
},
- "version": "0.0.6",
+ "version": "0.9.0",
"description": "React Native binding for iOS ARKit",
"author": "Zehao Li ",
"license": "MIT",
"homepage": "https://github.com/HippoAR/react-native-arkit",
- "keywords": [
- "react-native",
- "react",
- "native",
- "ARKit",
- "AR"
- ],
+ "keywords": ["react-native", "react", "native", "ARKit", "AR"],
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
+ "peerDependencies": {
+ "react-native": ">=0.46.0",
+ "react": "*",
+ "prop-types": "*"
+ },
"dependencies": {
- "prop-types": "^15.5.7"
+ "@panter/react-animation-frame": "^0.3.7",
+ "fast-deep-equal": "^1.0.0",
+ "lodash": "^4.17.4",
+ "prop-types": "^15.6.0"
},
- "peerDependencies": {
- "react": "^15.5.0"
+ "devDependencies": {
+ "babel-eslint": "^7.2.3",
+ "babel-preset-react-native": "^2.1.0",
+ "eslint": "^4.2.0",
+ "eslint-config-airbnb": "^15.0.1",
+ "eslint-config-prettier": "^2.1.1",
+ "eslint-plugin-flowtype": "^2.33.0",
+ "eslint-plugin-import": "^2.3.0",
+ "eslint-plugin-jsx-a11y": "^5.0.3",
+ "eslint-plugin-prettier": "^2.1.1",
+ "eslint-plugin-react": "^7.0.1",
+ "prettier": "^1.3.1",
+ "react": "16.2.0",
+ "prop-types": "^15.6.0"
}
}
diff --git a/screenshots/geometries.jpg b/screenshots/geometries.jpg
new file mode 100644
index 00000000..796eca55
Binary files /dev/null and b/screenshots/geometries.jpg differ
diff --git a/startup.js b/startup.js
new file mode 100644
index 00000000..6817eebb
--- /dev/null
+++ b/startup.js
@@ -0,0 +1,15 @@
+import { NativeModules } from 'react-native';
+
+const ARKitManager = NativeModules.ARKitManager;
+
+export default () => {
+ // when reloading the app, the scene should be cleared.
+ // on prod, this usually does not happen, but you can reload the app in develop mode
+ // without clearing, this would result in inconsistency
+ // do this only when arkit was already initialized
+ ARKitManager.isInitialized().then(isInitialized => {
+ if (isInitialized) {
+ ARKitManager.clearScene();
+ }
+ });
+};
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 00000000..9033fa07
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,1721 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@panter/react-animation-frame@^0.3.7":
+ version "0.3.7"
+ resolved "https://registry.yarnpkg.com/@panter/react-animation-frame/-/react-animation-frame-0.3.7.tgz#31a0d7683e4a8d2e1b29ff512cad36513bd43d00"
+ dependencies:
+ react "15.4.2"
+
+acorn-jsx@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+ dependencies:
+ acorn "^3.0.4"
+
+acorn@^3.0.4:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+
+acorn@^5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.2.tgz#911cb53e036807cf0fa778dc5d370fbd864246d7"
+
+ajv-keywords@^1.0.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
+
+ajv@^4.7.0:
+ version "4.11.8"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
+ dependencies:
+ co "^4.6.0"
+ json-stable-stringify "^1.0.1"
+
+ajv@^5.2.0:
+ version "5.2.2"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39"
+ dependencies:
+ co "^4.6.0"
+ fast-deep-equal "^1.0.0"
+ json-schema-traverse "^0.3.0"
+ json-stable-stringify "^1.0.1"
+
+ansi-escapes@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b"
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+
+ansi-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+
+ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+
+ansi-styles@^3.1.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88"
+ dependencies:
+ color-convert "^1.9.0"
+
+argparse@^1.0.7:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
+ dependencies:
+ sprintf-js "~1.0.2"
+
+aria-query@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-0.7.0.tgz#4af10a1e61573ddea0cf3b99b51c52c05b424d24"
+ dependencies:
+ ast-types-flow "0.0.7"
+
+array-includes@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
+ dependencies:
+ define-properties "^1.1.2"
+ es-abstract "^1.7.0"
+
+array-union@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ dependencies:
+ array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+
+arrify@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+
+asap@~2.0.3:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+
+ast-types-flow@0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
+
+axobject-query@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-0.1.0.tgz#62f59dbc59c9f9242759ca349960e7a2fe3c36c0"
+ dependencies:
+ ast-types-flow "0.0.7"
+
+babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+ dependencies:
+ chalk "^1.1.3"
+ esutils "^2.0.2"
+ js-tokens "^3.0.2"
+
+babel-eslint@^7.2.3:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827"
+ dependencies:
+ babel-code-frame "^6.22.0"
+ babel-traverse "^6.23.1"
+ babel-types "^6.23.0"
+ babylon "^6.17.0"
+
+babel-helper-builder-react-jsx@^6.24.1:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz#39ff8313b75c8b65dceff1f31d383e0ff2a408a0"
+ dependencies:
+ babel-runtime "^6.26.0"
+ babel-types "^6.26.0"
+ esutils "^2.0.2"
+
+babel-helper-call-delegate@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d"
+ dependencies:
+ babel-helper-hoist-variables "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-helper-define-map@^6.24.1:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f"
+ dependencies:
+ babel-helper-function-name "^6.24.1"
+ babel-runtime "^6.26.0"
+ babel-types "^6.26.0"
+ lodash "^4.17.4"
+
+babel-helper-function-name@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
+ dependencies:
+ babel-helper-get-function-arity "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-helper-get-function-arity@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-helper-hoist-variables@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-helper-optimise-call-expression@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-helper-replace-supers@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
+ dependencies:
+ babel-helper-optimise-call-expression "^6.24.1"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-messages@^6.23.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-check-es2015-constants@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-react-transform@2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz#515bbfa996893981142d90b1f9b1635de2995109"
+ dependencies:
+ lodash "^4.6.1"
+
+babel-plugin-syntax-async-functions@^6.5.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
+
+babel-plugin-syntax-class-properties@^6.5.0, babel-plugin-syntax-class-properties@^6.8.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de"
+
+babel-plugin-syntax-flow@^6.18.0, babel-plugin-syntax-flow@^6.5.0:
+ version "6.18.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d"
+
+babel-plugin-syntax-jsx@^6.5.0, babel-plugin-syntax-jsx@^6.8.0:
+ version "6.18.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
+
+babel-plugin-syntax-object-rest-spread@^6.8.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
+
+babel-plugin-syntax-trailing-function-commas@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
+
+babel-plugin-transform-class-properties@^6.5.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac"
+ dependencies:
+ babel-helper-function-name "^6.24.1"
+ babel-plugin-syntax-class-properties "^6.8.0"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-arrow-functions@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoping@^6.5.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f"
+ dependencies:
+ babel-runtime "^6.26.0"
+ babel-template "^6.26.0"
+ babel-traverse "^6.26.0"
+ babel-types "^6.26.0"
+ lodash "^4.17.4"
+
+babel-plugin-transform-es2015-classes@^6.5.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
+ dependencies:
+ babel-helper-define-map "^6.24.1"
+ babel-helper-function-name "^6.24.1"
+ babel-helper-optimise-call-expression "^6.24.1"
+ babel-helper-replace-supers "^6.24.1"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-computed-properties@^6.5.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-destructuring@^6.5.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-for-of@^6.5.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-function-name@^6.5.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
+ dependencies:
+ babel-helper-function-name "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-literals@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-modules-commonjs@^6.5.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a"
+ dependencies:
+ babel-plugin-transform-strict-mode "^6.24.1"
+ babel-runtime "^6.26.0"
+ babel-template "^6.26.0"
+ babel-types "^6.26.0"
+
+babel-plugin-transform-es2015-parameters@^6.5.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
+ dependencies:
+ babel-helper-call-delegate "^6.24.1"
+ babel-helper-get-function-arity "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-shorthand-properties@^6.5.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-spread@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-template-literals@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-flow-strip-types@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf"
+ dependencies:
+ babel-plugin-syntax-flow "^6.18.0"
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-object-assign@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz#f99d2f66f1a0b0d498e346c5359684740caa20ba"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-object-rest-spread@^6.5.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06"
+ dependencies:
+ babel-plugin-syntax-object-rest-spread "^6.8.0"
+ babel-runtime "^6.26.0"
+
+babel-plugin-transform-react-display-name@^6.5.0:
+ version "6.25.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-source@^6.5.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6"
+ dependencies:
+ babel-plugin-syntax-jsx "^6.8.0"
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx@^6.5.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz#840a028e7df460dfc3a2d29f0c0d91f6376e66a3"
+ dependencies:
+ babel-helper-builder-react-jsx "^6.24.1"
+ babel-plugin-syntax-jsx "^6.8.0"
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-regenerator@^6.5.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f"
+ dependencies:
+ regenerator-transform "^0.10.0"
+
+babel-plugin-transform-strict-mode@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-preset-react-native@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-react-native/-/babel-preset-react-native-2.1.0.tgz#9013ebd82da1c88102bf588810ff59e209ca2b8a"
+ dependencies:
+ babel-plugin-check-es2015-constants "^6.5.0"
+ babel-plugin-react-transform "2.0.2"
+ babel-plugin-syntax-async-functions "^6.5.0"
+ babel-plugin-syntax-class-properties "^6.5.0"
+ babel-plugin-syntax-flow "^6.5.0"
+ babel-plugin-syntax-jsx "^6.5.0"
+ babel-plugin-syntax-trailing-function-commas "^6.5.0"
+ babel-plugin-transform-class-properties "^6.5.0"
+ babel-plugin-transform-es2015-arrow-functions "^6.5.0"
+ babel-plugin-transform-es2015-block-scoping "^6.5.0"
+ babel-plugin-transform-es2015-classes "^6.5.0"
+ babel-plugin-transform-es2015-computed-properties "^6.5.0"
+ babel-plugin-transform-es2015-destructuring "^6.5.0"
+ babel-plugin-transform-es2015-for-of "^6.5.0"
+ babel-plugin-transform-es2015-function-name "^6.5.0"
+ babel-plugin-transform-es2015-literals "^6.5.0"
+ babel-plugin-transform-es2015-modules-commonjs "^6.5.0"
+ babel-plugin-transform-es2015-parameters "^6.5.0"
+ babel-plugin-transform-es2015-shorthand-properties "^6.5.0"
+ babel-plugin-transform-es2015-spread "^6.5.0"
+ babel-plugin-transform-es2015-template-literals "^6.5.0"
+ babel-plugin-transform-flow-strip-types "^6.5.0"
+ babel-plugin-transform-object-assign "^6.5.0"
+ babel-plugin-transform-object-rest-spread "^6.5.0"
+ babel-plugin-transform-react-display-name "^6.5.0"
+ babel-plugin-transform-react-jsx "^6.5.0"
+ babel-plugin-transform-react-jsx-source "^6.5.0"
+ babel-plugin-transform-regenerator "^6.5.0"
+ react-transform-hmr "^1.0.4"
+
+babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+ dependencies:
+ core-js "^2.4.0"
+ regenerator-runtime "^0.11.0"
+
+babel-template@^6.24.1, babel-template@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
+ dependencies:
+ babel-runtime "^6.26.0"
+ babel-traverse "^6.26.0"
+ babel-types "^6.26.0"
+ babylon "^6.18.0"
+ lodash "^4.17.4"
+
+babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
+ dependencies:
+ babel-code-frame "^6.26.0"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.26.0"
+ babel-types "^6.26.0"
+ babylon "^6.18.0"
+ debug "^2.6.8"
+ globals "^9.18.0"
+ invariant "^2.2.2"
+ lodash "^4.17.4"
+
+babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
+ dependencies:
+ babel-runtime "^6.26.0"
+ esutils "^2.0.2"
+ lodash "^4.17.4"
+ to-fast-properties "^1.0.3"
+
+babylon@^6.17.0, babylon@^6.18.0:
+ version "6.18.0"
+ resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+
+brace-expansion@^1.1.7:
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+builtin-modules@^1.0.0, builtin-modules@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+
+caller-path@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
+ dependencies:
+ callsites "^0.2.0"
+
+callsites@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
+
+chalk@^1.1.1, chalk@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+chalk@^2.0.0, chalk@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
+ dependencies:
+ ansi-styles "^3.1.0"
+ escape-string-regexp "^1.0.5"
+ supports-color "^4.0.0"
+
+circular-json@^0.3.1:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
+
+cli-cursor@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+ dependencies:
+ restore-cursor "^2.0.0"
+
+cli-width@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
+
+co@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+
+color-convert@^1.9.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
+ dependencies:
+ color-name "^1.1.1"
+
+color-name@^1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+
+concat-stream@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
+ dependencies:
+ inherits "^2.0.3"
+ readable-stream "^2.2.2"
+ typedarray "^0.0.6"
+
+contains-path@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
+
+core-js@^1.0.0:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
+
+core-js@^2.4.0:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
+
+core-util-is@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+
+cross-spawn@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
+ dependencies:
+ lru-cache "^4.0.1"
+ shebang-command "^1.2.0"
+ which "^1.2.9"
+
+damerau-levenshtein@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514"
+
+debug@^2.6.8:
+ version "2.6.8"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
+ dependencies:
+ ms "2.0.0"
+
+debug@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.0.1.tgz#0564c612b521dc92d9f2988f0549e34f9c98db64"
+ dependencies:
+ ms "2.0.0"
+
+deep-is@~0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+
+define-properties@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
+ dependencies:
+ foreach "^2.0.5"
+ object-keys "^1.0.8"
+
+del@^2.0.2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
+ dependencies:
+ globby "^5.0.0"
+ is-path-cwd "^1.0.0"
+ is-path-in-cwd "^1.0.0"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ rimraf "^2.2.8"
+
+doctrine@1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
+ dependencies:
+ esutils "^2.0.2"
+ isarray "^1.0.0"
+
+doctrine@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
+ dependencies:
+ esutils "^2.0.2"
+ isarray "^1.0.0"
+
+dom-walk@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
+
+emoji-regex@^6.1.0:
+ version "6.5.1"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2"
+
+encoding@^0.1.11:
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+ dependencies:
+ iconv-lite "~0.4.13"
+
+error-ex@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
+ dependencies:
+ is-arrayish "^0.2.1"
+
+es-abstract@^1.7.0:
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.8.2.tgz#25103263dc4decbda60e0c737ca32313518027ee"
+ dependencies:
+ es-to-primitive "^1.1.1"
+ function-bind "^1.1.1"
+ has "^1.0.1"
+ is-callable "^1.1.3"
+ is-regex "^1.0.4"
+
+es-to-primitive@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
+ dependencies:
+ is-callable "^1.1.1"
+ is-date-object "^1.0.1"
+ is-symbol "^1.0.1"
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+eslint-config-airbnb-base@^11.3.0:
+ version "11.3.2"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.2.tgz#8703b11abe3c88ac7ec2b745b7fdf52e00ae680a"
+ dependencies:
+ eslint-restricted-globals "^0.1.1"
+
+eslint-config-airbnb@^15.0.1:
+ version "15.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-15.1.0.tgz#fd432965a906e30139001ba830f58f73aeddae8e"
+ dependencies:
+ eslint-config-airbnb-base "^11.3.0"
+
+eslint-config-prettier@^2.1.1:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.5.0.tgz#9ecb9296bae4e2e59a3ce361a96c9f825fe67b75"
+ dependencies:
+ get-stdin "^5.0.1"
+
+eslint-import-resolver-node@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz#4422574cde66a9a7b099938ee4d508a199e0e3cc"
+ dependencies:
+ debug "^2.6.8"
+ resolve "^1.2.0"
+
+eslint-module-utils@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449"
+ dependencies:
+ debug "^2.6.8"
+ pkg-dir "^1.0.0"
+
+eslint-plugin-flowtype@^2.33.0:
+ version "2.35.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.35.1.tgz#9ad98181b467a3645fbd2a8d430393cc17a4ea63"
+ dependencies:
+ lodash "^4.15.0"
+
+eslint-plugin-import@^2.3.0:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f"
+ dependencies:
+ builtin-modules "^1.1.1"
+ contains-path "^0.1.0"
+ debug "^2.6.8"
+ doctrine "1.5.0"
+ eslint-import-resolver-node "^0.3.1"
+ eslint-module-utils "^2.1.1"
+ has "^1.0.1"
+ lodash.cond "^4.3.0"
+ minimatch "^3.0.3"
+ read-pkg-up "^2.0.0"
+
+eslint-plugin-jsx-a11y@^5.0.3:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz#5c96bb5186ca14e94db1095ff59b3e2bd94069b1"
+ dependencies:
+ aria-query "^0.7.0"
+ array-includes "^3.0.3"
+ ast-types-flow "0.0.7"
+ axobject-query "^0.1.0"
+ damerau-levenshtein "^1.0.0"
+ emoji-regex "^6.1.0"
+ jsx-ast-utils "^1.4.0"
+
+eslint-plugin-prettier@^2.1.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.3.1.tgz#e7a746c67e716f335274b88295a9ead9f544e44d"
+ dependencies:
+ fast-diff "^1.1.1"
+ jest-docblock "^21.0.0"
+
+eslint-plugin-react@^7.0.1:
+ version "7.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.3.0.tgz#ca9368da36f733fbdc05718ae4e91f778f38e344"
+ dependencies:
+ doctrine "^2.0.0"
+ has "^1.0.1"
+ jsx-ast-utils "^2.0.0"
+ prop-types "^15.5.10"
+
+eslint-restricted-globals@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
+
+eslint-scope@^3.7.1:
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
+ dependencies:
+ esrecurse "^4.1.0"
+ estraverse "^4.1.1"
+
+eslint@^4.2.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.7.0.tgz#d35fc07c472520be3de85b3da11e99c576afd515"
+ dependencies:
+ ajv "^5.2.0"
+ babel-code-frame "^6.22.0"
+ chalk "^2.1.0"
+ concat-stream "^1.6.0"
+ cross-spawn "^5.1.0"
+ debug "^3.0.1"
+ doctrine "^2.0.0"
+ eslint-scope "^3.7.1"
+ espree "^3.5.1"
+ esquery "^1.0.0"
+ estraverse "^4.2.0"
+ esutils "^2.0.2"
+ file-entry-cache "^2.0.0"
+ functional-red-black-tree "^1.0.1"
+ glob "^7.1.2"
+ globals "^9.17.0"
+ ignore "^3.3.3"
+ imurmurhash "^0.1.4"
+ inquirer "^3.0.6"
+ is-resolvable "^1.0.0"
+ js-yaml "^3.9.1"
+ json-stable-stringify "^1.0.1"
+ levn "^0.3.0"
+ lodash "^4.17.4"
+ minimatch "^3.0.2"
+ mkdirp "^0.5.1"
+ natural-compare "^1.4.0"
+ optionator "^0.8.2"
+ path-is-inside "^1.0.2"
+ pluralize "^7.0.0"
+ progress "^2.0.0"
+ require-uncached "^1.0.3"
+ semver "^5.3.0"
+ strip-ansi "^4.0.0"
+ strip-json-comments "~2.0.1"
+ table "^4.0.1"
+ text-table "~0.2.0"
+
+espree@^3.5.1:
+ version "3.5.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.1.tgz#0c988b8ab46db53100a1954ae4ba995ddd27d87e"
+ dependencies:
+ acorn "^5.1.1"
+ acorn-jsx "^3.0.0"
+
+esprima@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
+
+esquery@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa"
+ dependencies:
+ estraverse "^4.0.0"
+
+esrecurse@^4.1.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163"
+ dependencies:
+ estraverse "^4.1.0"
+ object-assign "^4.0.1"
+
+estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
+
+esutils@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+
+external-editor@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972"
+ dependencies:
+ iconv-lite "^0.4.17"
+ jschardet "^1.4.2"
+ tmp "^0.0.31"
+
+fast-deep-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
+
+fast-diff@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.1.tgz#0aea0e4e605b6a2189f0e936d4b7fbaf1b7cfd9b"
+
+fast-levenshtein@~2.0.4:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+
+fbjs@^0.8.16, fbjs@^0.8.4:
+ version "0.8.16"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
+ dependencies:
+ core-js "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.9"
+
+fbjs@^0.8.9:
+ version "0.8.15"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.15.tgz#4f0695fdfcc16c37c0b07facec8cb4c4091685b9"
+ dependencies:
+ core-js "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.9"
+
+figures@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+ dependencies:
+ escape-string-regexp "^1.0.5"
+
+file-entry-cache@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
+ dependencies:
+ flat-cache "^1.2.1"
+ object-assign "^4.0.1"
+
+find-up@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ dependencies:
+ path-exists "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+find-up@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ dependencies:
+ locate-path "^2.0.0"
+
+flat-cache@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96"
+ dependencies:
+ circular-json "^0.3.1"
+ del "^2.0.2"
+ graceful-fs "^4.1.2"
+ write "^0.2.1"
+
+foreach@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+
+function-bind@^1.0.2, function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+
+get-stdin@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
+
+glob@^7.0.3, glob@^7.0.5, glob@^7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+global@^4.3.0:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f"
+ dependencies:
+ min-document "^2.19.0"
+ process "~0.5.1"
+
+globals@^9.17.0, globals@^9.18.0:
+ version "9.18.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
+
+globby@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
+ dependencies:
+ array-union "^1.0.1"
+ arrify "^1.0.0"
+ glob "^7.0.3"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+graceful-fs@^4.1.2:
+ version "4.1.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-flag@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+
+has@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
+ dependencies:
+ function-bind "^1.0.2"
+
+hosted-git-info@^2.1.4:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c"
+
+iconv-lite@^0.4.17, iconv-lite@~0.4.13:
+ version "0.4.19"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
+
+ignore@^3.3.3:
+ version "3.3.5"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.5.tgz#c4e715455f6073a8d7e5dae72d2fc9d71663dba6"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@^2.0.3, inherits@~2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+
+inquirer@^3.0.6:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.2.3.tgz#1c7b1731cf77b934ec47d22c9ac5aa8fe7fbe095"
+ dependencies:
+ ansi-escapes "^2.0.0"
+ chalk "^2.0.0"
+ cli-cursor "^2.1.0"
+ cli-width "^2.0.0"
+ external-editor "^2.0.4"
+ figures "^2.0.0"
+ lodash "^4.3.0"
+ mute-stream "0.0.7"
+ run-async "^2.2.0"
+ rx-lite "^4.0.8"
+ rx-lite-aggregates "^4.0.8"
+ string-width "^2.1.0"
+ strip-ansi "^4.0.0"
+ through "^2.3.6"
+
+invariant@^2.2.2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
+ dependencies:
+ loose-envify "^1.0.0"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+
+is-builtin-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+ dependencies:
+ builtin-modules "^1.0.0"
+
+is-callable@^1.1.1, is-callable@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
+
+is-date-object@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
+
+is-fullwidth-code-point@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+
+is-path-cwd@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+
+is-path-in-cwd@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc"
+ dependencies:
+ is-path-inside "^1.0.0"
+
+is-path-inside@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f"
+ dependencies:
+ path-is-inside "^1.0.1"
+
+is-promise@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
+is-regex@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+ dependencies:
+ has "^1.0.1"
+
+is-resolvable@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62"
+ dependencies:
+ tryit "^1.0.1"
+
+is-stream@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+
+is-symbol@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
+
+isarray@^1.0.0, isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
+isomorphic-fetch@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
+ dependencies:
+ node-fetch "^1.0.1"
+ whatwg-fetch ">=0.10.0"
+
+jest-docblock@^21.0.0:
+ version "21.2.0"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
+
+js-tokens@^3.0.0, js-tokens@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+
+js-yaml@^3.9.1:
+ version "3.10.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc"
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
+jschardet@^1.4.2:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.5.1.tgz#c519f629f86b3a5bedba58a88d311309eec097f9"
+
+json-schema-traverse@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
+
+json-stable-stringify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+ dependencies:
+ jsonify "~0.0.0"
+
+jsonify@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+
+jsx-ast-utils@^1.4.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"
+
+jsx-ast-utils@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
+ dependencies:
+ array-includes "^3.0.3"
+
+levn@^0.3.0, levn@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ dependencies:
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+
+load-json-file@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ strip-bom "^3.0.0"
+
+locate-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ dependencies:
+ p-locate "^2.0.0"
+ path-exists "^3.0.0"
+
+lodash.cond@^4.3.0:
+ version "4.5.2"
+ resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
+
+lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.6.1:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
+ dependencies:
+ js-tokens "^3.0.0"
+
+lru-cache@^4.0.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55"
+ dependencies:
+ pseudomap "^1.0.2"
+ yallist "^2.1.2"
+
+mimic-fn@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
+
+min-document@^2.19.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
+ dependencies:
+ dom-walk "^0.1.0"
+
+minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+
+mkdirp@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ dependencies:
+ minimist "0.0.8"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+
+mute-stream@0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+
+node-fetch@^1.0.1:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
+ dependencies:
+ encoding "^0.1.11"
+ is-stream "^1.0.1"
+
+normalize-package-data@^2.3.2:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
+ dependencies:
+ hosted-git-info "^2.1.4"
+ is-builtin-module "^1.0.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+
+object-keys@^1.0.8:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ dependencies:
+ wrappy "1"
+
+onetime@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+ dependencies:
+ mimic-fn "^1.0.0"
+
+optionator@^0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+ dependencies:
+ deep-is "~0.1.3"
+ fast-levenshtein "~2.0.4"
+ levn "~0.3.0"
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+ wordwrap "~1.0.0"
+
+os-tmpdir@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+
+p-limit@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc"
+
+p-locate@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ dependencies:
+ p-limit "^1.1.0"
+
+parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ dependencies:
+ error-ex "^1.2.0"
+
+path-exists@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ dependencies:
+ pinkie-promise "^2.0.0"
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+
+path-is-inside@^1.0.1, path-is-inside@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+
+path-parse@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+
+path-type@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
+ dependencies:
+ pify "^2.0.0"
+
+pify@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+
+pkg-dir@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
+ dependencies:
+ find-up "^1.0.0"
+
+pluralize@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
+
+prelude-ls@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+
+prettier@^1.3.1:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.7.0.tgz#47481588f41f7c90f63938feb202ac82554e7150"
+
+private@^0.1.6:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
+
+process-nextick-args@~1.0.6:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+
+process@~0.5.1:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
+
+progress@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
+
+promise@^7.1.1:
+ version "7.3.1"
+ resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
+ dependencies:
+ asap "~2.0.3"
+
+prop-types@^15.5.10:
+ version "15.5.10"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
+ dependencies:
+ fbjs "^0.8.9"
+ loose-envify "^1.3.1"
+
+prop-types@^15.6.0:
+ version "15.6.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
+ dependencies:
+ fbjs "^0.8.16"
+ loose-envify "^1.3.1"
+ object-assign "^4.1.1"
+
+pseudomap@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+
+react-deep-force-update@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz#bcd31478027b64b3339f108921ab520b4313dc2c"
+
+react-proxy@^1.1.7:
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a"
+ dependencies:
+ lodash "^4.6.1"
+ react-deep-force-update "^1.0.0"
+
+react-transform-hmr@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz#e1a40bd0aaefc72e8dfd7a7cda09af85066397bb"
+ dependencies:
+ global "^4.3.0"
+ react-proxy "^1.1.7"
+
+react@15.4.2:
+ version "15.4.2"
+ resolved "https://registry.yarnpkg.com/react/-/react-15.4.2.tgz#41f7991b26185392ba9bae96c8889e7e018397ef"
+ dependencies:
+ fbjs "^0.8.4"
+ loose-envify "^1.1.0"
+ object-assign "^4.1.0"
+
+react@16.2.0:
+ version "16.2.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
+ dependencies:
+ fbjs "^0.8.16"
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.0"
+
+read-pkg-up@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
+ dependencies:
+ find-up "^2.0.0"
+ read-pkg "^2.0.0"
+
+read-pkg@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
+ dependencies:
+ load-json-file "^2.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^2.0.0"
+
+readable-stream@^2.2.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~1.0.6"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.0.3"
+ util-deprecate "~1.0.1"
+
+regenerator-runtime@^0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
+
+regenerator-transform@^0.10.0:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
+ dependencies:
+ babel-runtime "^6.18.0"
+ babel-types "^6.19.0"
+ private "^0.1.6"
+
+require-uncached@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
+ dependencies:
+ caller-path "^0.1.0"
+ resolve-from "^1.0.0"
+
+resolve-from@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
+
+resolve@^1.2.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86"
+ dependencies:
+ path-parse "^1.0.5"
+
+restore-cursor@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+ dependencies:
+ onetime "^2.0.0"
+ signal-exit "^3.0.2"
+
+rimraf@^2.2.8:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
+ dependencies:
+ glob "^7.0.5"
+
+run-async@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+ dependencies:
+ is-promise "^2.1.0"
+
+rx-lite-aggregates@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
+ dependencies:
+ rx-lite "*"
+
+rx-lite@*, rx-lite@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0:
+ version "5.4.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
+
+setimmediate@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+
+shebang-command@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+ dependencies:
+ shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+
+signal-exit@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+
+slice-ansi@0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
+
+spdx-correct@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
+ dependencies:
+ spdx-license-ids "^1.0.2"
+
+spdx-expression-parse@~1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c"
+
+spdx-license-ids@^1.0.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+
+string-width@^2.0.0, string-width@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+ dependencies:
+ is-fullwidth-code-point "^2.0.0"
+ strip-ansi "^4.0.0"
+
+string_decoder@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
+ dependencies:
+ safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ dependencies:
+ ansi-regex "^3.0.0"
+
+strip-bom@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+
+supports-color@^4.0.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e"
+ dependencies:
+ has-flag "^2.0.0"
+
+table@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435"
+ dependencies:
+ ajv "^4.7.0"
+ ajv-keywords "^1.0.0"
+ chalk "^1.1.1"
+ lodash "^4.0.0"
+ slice-ansi "0.0.4"
+ string-width "^2.0.0"
+
+text-table@~0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+
+through@^2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+
+tmp@^0.0.31:
+ version "0.0.31"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
+ dependencies:
+ os-tmpdir "~1.0.1"
+
+to-fast-properties@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+
+tryit@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
+
+type-check@~0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ dependencies:
+ prelude-ls "~1.1.2"
+
+typedarray@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+
+ua-parser-js@^0.7.9:
+ version "0.7.14"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.14.tgz#110d53fa4c3f326c121292bbeac904d2e03387ca"
+
+util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+
+validate-npm-package-license@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
+ dependencies:
+ spdx-correct "~1.0.0"
+ spdx-expression-parse "~1.0.0"
+
+whatwg-fetch@>=0.10.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
+
+which@^1.2.9:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
+ dependencies:
+ isexe "^2.0.0"
+
+wordwrap@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+
+write@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
+ dependencies:
+ mkdirp "^0.5.1"
+
+yallist@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"