diff --git a/.babelrc b/.babelrc index 88f0c0ac53f9a7036d9ad906f256c6668ca51d98..f1f9670135e206f6bb81491ac4facc2b9a230695 100644 --- a/.babelrc +++ b/.babelrc @@ -5,5 +5,9 @@ "exclude": ["transform-regenerator"], "useBuiltins": true }] + ], + "plugins": [ + [ "react-intl", { "messagesDir": "./public/messages", "enforceDescriptions": false } ], + [ "transform-react-jsx-img-import" ] ] } diff --git a/.eslintrc.json b/.eslintrc.json index f93928f772e507355bccc801455002a3b4ebef84..d39862227e78dc98b5d902fd0a1bf84a23fa76b4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,7 +4,8 @@ "sourceType": "module" }, "plugins": [ - "prettier" + "prettier", + "react" ], "rules": { "prettier/prettier": "error", @@ -12,6 +13,7 @@ "no-var": "error" }, "extends": [ - "prettier" + "prettier", + "plugin:react/recommended" ] -} \ No newline at end of file +} diff --git a/package.json b/package.json index e7e8f66aa2ce8788a68016f62f96b0a58ad18c50..2d4901d3943dd6a8d752ced976608016fe96ce45 100644 --- a/package.json +++ b/package.json @@ -21,22 +21,34 @@ "aframe-physics-system": "https://github.com/donmccurdy/aframe-physics-system", "aframe-teleport-controls": "https://github.com/netpro2k/aframe-teleport-controls#feature/teleport-origin", "aframe-xr": "github:brianpeiris/aframe-xr#3162aed", + "babel-plugin-react-intl": "^2.4.0", + "babel-plugin-transform-react-jsx-img-import": "^0.1.4", + "classnames": "^2.2.5", + "detect-browser": "^2.1.0", + "event-target-shim": "^3.0.1", + "jsonschema": "^1.2.2", "material-design-lite": "^1.3.0", - "minijanus": "^0.4.0", - "naf-janus-adapter": "^0.4.0", + "minijanus": "^0.5.0", + "mobile-detect": "^1.4.1", + "moving-average": "^1.0.0", + "naf-janus-adapter": "https://github.com/mozilla/naf-janus-adapter#feature/disconnect", "networked-aframe": "https://github.com/mozillareality/networked-aframe#mr-social-client/master", "nipplejs": "^0.6.7", "query-string": "^5.0.1", "raven-js": "^3.20.1", "react": "^16.1.1", "react-dom": "^16.1.1", + "react-intl": "^2.4.0", "react-router-dom": "^4.2.2", "super-hands": "https://github.com/infinitelee/aframe-super-hands-component#mr-social-client/master", + "uuid": "^3.2.1", "webrtc-adapter": "^6.0.2" }, "devDependencies": { "babel-core": "^6.26.0", + "babel-eslint": "^8.2.2", "babel-loader": "^7.1.3", + "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "cross-env": "^5.1.3", @@ -45,13 +57,16 @@ "eslint": "^4.10.0", "eslint-config-prettier": "^2.6.0", "eslint-plugin-prettier": "^2.3.1", + "eslint-plugin-react": "^7.7.0", "extract-text-webpack-plugin": "4.0.0-alpha.0", "file-loader": "^1.1.10", "html-loader": "^0.5.5", "html-webpack-plugin": "webpack-contrib/html-webpack-plugin", "lodash": "^4.17.5", + "node-sass": "^4.7.2", "prettier": "^1.7.0", "rimraf": "^2.6.2", + "sass-loader": "^6.0.7", "selfsigned": "^1.10.2", "style-loader": "^0.20.2", "webpack": "^4.0.1", diff --git a/src/assets/fonts/zilla-slab_latin-ext.woff2 b/src/assets/fonts/zilla-slab_latin-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..60259781df0d9a85f8e9f88ef7ed3dcf8b69b12b Binary files /dev/null and b/src/assets/fonts/zilla-slab_latin-ext.woff2 differ diff --git a/src/assets/fonts/zilla-slab_latin.woff2 b/src/assets/fonts/zilla-slab_latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..18b5d6e8f5f3bd37ffe7effde8414b7ff3ed0409 Binary files /dev/null and b/src/assets/fonts/zilla-slab_latin.woff2 differ diff --git a/src/assets/images/account.svg b/src/assets/images/account.svg new file mode 100755 index 0000000000000000000000000000000000000000..a39a815fd97c9157c2de36ea7fd7fc98888cb58f --- /dev/null +++ b/src/assets/images/account.svg @@ -0,0 +1,14 @@ +<svg width="16" height="16" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>account</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-42 105)"> +<g id="account"> +<g id="Vector"> +<use xlink:href="#path0_fill" transform="translate(42 -105)" fill="#FFFFFF"/> +</g> +</g> +</g> +<defs> +<path id="path0_fill" d="M 8 0C 9.06087 8.88178e-16 10.0783 0.421427 10.8284 1.17157C 11.5786 1.92172 12 2.93913 12 4C 12 5.06087 11.5786 6.07828 10.8284 6.82843C 10.0783 7.57857 9.06087 8 8 8C 6.93913 8 5.92172 7.57857 5.17157 6.82843C 4.42143 6.07828 4 5.06087 4 4C 4 2.93913 4.42143 1.92172 5.17157 1.17157C 5.92172 0.421427 6.93913 8.88178e-16 8 0L 8 0ZM 8 10C 12.42 10 16 11.79 16 14L 16 16L 0 16L 0 14C 0 11.79 3.58 10 8 10Z"/> +</defs> +</svg> diff --git a/src/assets/images/daydream_entry.svg b/src/assets/images/daydream_entry.svg new file mode 100755 index 0000000000000000000000000000000000000000..dc91f66a5e722e7fa4356b23c8b5da25ea58e87d --- /dev/null +++ b/src/assets/images/daydream_entry.svg @@ -0,0 +1,77 @@ +<svg width="94" height="94" viewBox="0 0 94 94" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>daydream_entry</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-659 -395)"> +<g id="daydream_entry"> +<g id="Oval Copy"> +<use xlink:href="#path0_fill" transform="translate(661 397)" fill="#D8D8D8" fill-opacity="0.01"/> +<use xlink:href="#path1_stroke" transform="translate(661 397)" fill="#FFFFFF"/> +</g> +<g id="daydream logo"> +<g id="change this to change color"> +<use xlink:href="#path2_fill" transform="translate(682.904 416.825)" fill="url(#paint2_radial)"/> +</g> +<g id="white gradients"> +<g id="Path 2 Copy 11" opacity="0.65"> +<use xlink:href="#path3_fill" transform="translate(682.931 423.817)" fill="url(#paint3_radial)"/> +</g> +<g id="Path 2 Copy 6" opacity="0.65"> +<use xlink:href="#path4_fill" transform="matrix(0.497686 -0.867357 0.864683 0.502317 679 452.35)" fill="url(#paint4_radial)"/> +</g> +<g id="Path 2 Copy 7" opacity="0.65"> +<use xlink:href="#path5_fill" transform="matrix(-0.497686 -0.867357 0.864683 -0.502317 701.592 470)" fill="url(#paint5_radial)"/> +</g> +<g id="Path 2 Copy 8" opacity="0.65"> +<use xlink:href="#path6_fill" transform="matrix(-1 1.23223e-16 -1.21711e-16 -1 728.052 459.16)" fill="url(#paint6_radial)"/> +</g> +<g id="Path 2 Copy 9" opacity="0.65"> +<use xlink:href="#path7_fill" transform="matrix(-0.497686 0.867357 -0.864683 -0.502317 732 430.682)" fill="url(#paint7_radial)"/> +</g> +<g id="Path 2 Copy 10" opacity="0.65"> +<use xlink:href="#path8_fill" transform="matrix(0.497686 0.867357 -0.864683 0.502317 709.534 413)" fill="url(#paint8_radial)"/> +</g> +</g> +</g> +</g> +</g> +<defs> +<radialGradient id="paint2_radial" cx="0.5" cy="0.5" r="0.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(59.6722 79.9412 -73.0523 65.2993 11.4524 -69.2011)"> +<stop offset="0" stop-color="#5C5C5C"/> +<stop offset="0.412807" stop-color="#EDEDED"/> +<stop offset="1" stop-color="#5E5E5E"/> +</radialGradient> +<radialGradient id="paint3_radial" cx="0.5" cy="0.5" r="0.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.88312 -21.1218 21.1955 -4.86615 -1.78495 30.4596)"> +<stop offset="0" stop-color="#FFFFFF"/> +<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.01"/> +</radialGradient> +<radialGradient id="paint4_radial" cx="0.5" cy="0.5" r="0.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.90994 -20.9673 21.3119 -4.83057 -1.79475 30.2369)"> +<stop offset="0" stop-color="#FFFFFF"/> +<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.01"/> +</radialGradient> +<radialGradient id="paint5_radial" cx="0.5" cy="0.5" r="0.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.90233 -20.9533 21.2788 -4.82733 -1.79197 30.2166)"> +<stop offset="0" stop-color="#FFFFFF"/> +<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.01"/> +</radialGradient> +<radialGradient id="paint6_radial" cx="0.5" cy="0.5" r="0.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.86849 -21.032 21.1319 -4.84546 -1.7796 30.3301)"> +<stop offset="0" stop-color="#FFFFFF"/> +<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.01"/> +</radialGradient> +<radialGradient id="paint7_radial" cx="0.5" cy="0.5" r="0.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.8983 -21.0767 21.2613 -4.85577 -1.7905 30.3946)"> +<stop offset="0" stop-color="#FFFFFF"/> +<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.01"/> +</radialGradient> +<radialGradient id="paint8_radial" cx="0.5" cy="0.5" r="0.5" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.88634 -21.1239 21.2094 -4.86663 -1.78613 30.4626)"> +<stop offset="0" stop-color="#FFFFFF"/> +<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.01"/> +</radialGradient> +<path id="path0_fill" fill-rule="evenodd" d="M 45 90C 69.8528 90 90 69.8528 90 45C 90 20.1472 69.8528 0 45 0C 20.1472 0 0 20.1472 0 45C 0 69.8528 20.1472 90 45 90Z"/> +<path id="path1_stroke" d="M 45 91.5C 70.6812 91.5 91.5 70.6812 91.5 45L 88.5 45C 88.5 69.0244 69.0244 88.5 45 88.5L 45 91.5ZM 91.5 45C 91.5 19.3188 70.6812 -1.5 45 -1.5L 45 1.5C 69.0244 1.5 88.5 20.9756 88.5 45L 91.5 45ZM 45 -1.5C 19.3188 -1.5 -1.5 19.3188 -1.5 45L 1.5 45C 1.5 20.9756 20.9756 1.5 45 1.5L 45 -1.5ZM -1.5 45C -1.5 70.6812 19.3188 91.5 45 91.5L 45 88.5C 20.9756 88.5 1.5 69.0244 1.5 45L -1.5 45Z"/> +<path id="path2_fill" fill-rule="evenodd" d="M 24.0352 16.2517L 24.0362 16.2536L 29.7039 12.0451L 24.0352 16.2517ZM 33.2408 9.42045L 33.3011 9.37578C 32.06 1.60484 24.3347 -1.25267 19.4698 0.495649C 16.7573 1.47041 14.5183 4.04674 14.868 7.7953C 15.6821 8.10795 16.5948 8.56465 17.536 9.16258C 16.8441 8.73008 16.1155 8.34044 15.352 7.99818C 15.3281 7.98729 15.3024 7.97584 15.2753 7.96409L 15.2316 7.94517L 15.1892 7.92717C 15.0808 7.88136 14.9644 7.83424 14.8737 7.79756L 16.0569 19.2312L 16.0547 19.2311L 14.8688 7.80232C 7.55981 4.99833 1.0708 10.1695 0.143126 15.283C -0.374147 18.134 0.798705 21.4164 4.19989 22.986C 4.29376 22.9102 4.39099 22.8341 4.49145 22.7579C 4.39184 22.8341 4.284 22.92 4.20044 22.9868L 14.6263 27.6703L 4.20117 22.9903C -1.8667 27.9573 -0.632447 36.2375 3.30481 39.6027C 5.49817 41.4773 8.87268 41.9886 11.9222 39.8159C 11.9226 39.8182 11.9229 39.8205 11.9233 39.8228C 13.1672 47.5898 20.8544 50.7252 25.7183 48.9773C 28.4307 48.0025 30.636 45.3177 30.2863 41.5692L 30.2842 41.5684L 29.1584 30.1621C 29.9005 30.1975 30.6449 30.1873 31.3873 30.131C 30.6648 30.1875 29.9228 30.2008 29.1659 30.1629L 30.2872 41.5648C 37.5962 44.3688 44.1041 39.1928 45.0319 34.0793C 45.5486 31.2308 44.3566 28.0236 40.9621 26.4526L 40.9681 26.4478L 40.9768 26.4518C 47.0447 21.4848 45.8285 13.2045 41.8912 9.8394C 39.6967 7.9637 36.3506 7.2043 33.2998 9.38106L 33.2996 9.37947L 33.2408 9.42045ZM 11.8336 39.0404C 11.7841 38.4644 11.762 37.8917 11.7664 37.3237C 11.7582 37.9294 11.7823 38.5047 11.8336 39.0404ZM 13.5902 29.5318C 13.8549 28.9848 14.1464 28.451 14.4634 27.9321C 14.1392 28.4578 13.8491 28.9924 13.5902 29.5318ZM 22.9946 35.9856C 22.3098 35.117 21.691 34.1657 21.1791 33.1383L 21.1803 33.1375C 21.6982 34.1405 22.3046 35.0942 22.9946 35.9856ZM 29.8655 41.3963L 29.8961 41.4097L 29.9335 41.426C 29.4347 41.2141 28.9065 40.9503 28.3627 40.6368C 28.8473 40.9107 29.3484 41.1643 29.8655 41.3963ZM 40.5527 26.7651C 39.4503 27.5638 38.2872 28.2194 37.0853 28.7352C 38.4524 28.1575 39.6294 27.4643 40.5527 26.7651ZM 33.2643 14.5439L 33.2563 14.5952L 33.2476 14.6505C 33.3387 14.0502 33.4002 13.4419 33.4306 12.8275C 33.4058 13.3863 33.3538 13.9605 33.2643 14.5439ZM 33.327 9.67726C 33.3475 9.88878 33.3664 10.1061 33.383 10.3291C 33.3741 10.2272 33.3643 10.1252 33.3537 10.0231C 33.3511 9.99919 33.3485 9.97062 33.3459 9.93886L 33.3433 9.90513L 33.3386 9.8408L 33.3318 9.74559L 33.327 9.67726ZM 23.8401 15.8842C 23.3004 14.8945 22.6726 13.955 21.9626 13.08C 22.6559 13.925 23.2943 14.8602 23.8401 15.8842ZM 15.604 19.2154C 14.4546 19.1897 13.3005 19.2735 12.1589 19.469C 13.2774 19.2655 14.4367 19.1677 15.604 19.2154ZM 5.24286 22.2272C 5.18664 22.2647 5.13061 22.3025 5.07471 22.3407C 5.03424 22.3684 4.99383 22.3963 4.95349 22.4243C 4.88074 22.4749 4.80829 22.5261 4.73602 22.578C 4.69299 22.6088 4.65002 22.6399 4.60718 22.6712L 4.60419 22.6734C 4.80511 22.5245 5.01837 22.3754 5.24286 22.2272Z"/> +<path id="path3_fill" fill-rule="evenodd" d="M 16.0295 12.229L 14.8425 0.790123C 7.53357 -2.01386 1.04458 3.15736 0.116862 8.2708C -0.400378 11.1218 0.772477 14.4042 4.17365 15.9738C 6.59782 14.0154 11.2827 11.8803 16.0295 12.229Z"/> +<path id="path4_fill" fill-rule="evenodd" d="M 16.1176 12.1553L 14.9278 0.782117C 7.58484 -2.00893 1.04493 3.18264 0.112904 8.27249C -0.406741 11.1103 0.84878 14.2947 4.26577 15.857C 6.697 13.8998 11.129 11.9068 16.1176 12.1553Z"/> +<path id="path5_fill" fill-rule="evenodd" d="M 16.0926 12.1431L 14.9659 0.790397C 7.62292 -2.00065 1.04732 3.10738 0.115298 8.19723C -0.404347 11.035 0.809439 14.2841 4.22643 15.8464C 6.63823 13.9 11.2588 11.8587 16.0926 12.1431Z"/> +<path id="path6_fill" fill-rule="evenodd" d="M 15.9815 12.1915L 14.8602 0.789647C 7.55119 -2.01434 1.04328 3.16169 0.115562 8.27513C -0.401678 11.1261 0.792924 14.3363 4.1941 15.9059C 6.54445 13.9641 10.9322 11.9386 15.9815 12.1915Z"/> +<path id="path7_fill" fill-rule="evenodd" d="M 16.0794 12.148L 14.9513 0.783652C 7.60839 -2.0074 1.05938 3.16856 0.127349 8.25841C -0.392295 11.0962 0.632515 14.3774 4.0495 15.9397C 5.24452 15.0732 6.74795 14.0894 8.52884 13.3998C 10.7325 12.5465 13.2716 12.0251 16.0794 12.148Z"/> +<path id="path8_fill" fill-rule="evenodd" d="M 16.0401 12.2374L 14.7485 0.773668C 7.40559 -2.01738 1.05413 3.26113 0.122106 8.35098C -0.397539 11.1888 0.705495 14.4131 4.12248 15.9754C 6.50059 14.048 11.0115 11.9513 16.0401 12.2374Z"/> +</defs> +</svg> diff --git a/src/assets/images/desktop_screen_entry.svg b/src/assets/images/desktop_screen_entry.svg new file mode 100755 index 0000000000000000000000000000000000000000..fbc322893f6fadd710782074e4f2781130ef1747 --- /dev/null +++ b/src/assets/images/desktop_screen_entry.svg @@ -0,0 +1,24 @@ +<svg width="94" height="94" viewBox="0 0 94 94" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>desktop_screen_entry</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-659 -277)"> +<g id="desktop_screen_entry"> +<g id="Oval Copy"> +<use xlink:href="#path0_fill" transform="translate(661 279)" fill="#D8D8D8" fill-opacity="0.01"/> +<use xlink:href="#path1_stroke" transform="translate(661 279)" fill="#FFFFFF"/> +</g> +<g id="Rectangle 3"> +<use xlink:href="#path2_fill" transform="translate(696 341)" fill="#FFFFFF"/> +</g> +<g id="desktop"> +<use xlink:href="#path3_fill" transform="translate(685 307)" fill="#FFFFFF"/> +</g> +</g> +</g> +<defs> +<path id="path0_fill" fill-rule="evenodd" d="M 45 90C 69.8528 90 90 69.8528 90 45C 90 20.1472 69.8528 0 45 0C 20.1472 0 0 20.1472 0 45C 0 69.8528 20.1472 90 45 90Z"/> +<path id="path1_stroke" d="M 45 91.5C 70.6812 91.5 91.5 70.6812 91.5 45L 88.5 45C 88.5 69.0244 69.0244 88.5 45 88.5L 45 91.5ZM 91.5 45C 91.5 19.3188 70.6812 -1.5 45 -1.5L 45 1.5C 69.0244 1.5 88.5 20.9756 88.5 45L 91.5 45ZM 45 -1.5C 19.3188 -1.5 -1.5 19.3188 -1.5 45L 1.5 45C 1.5 20.9756 20.9756 1.5 45 1.5L 45 -1.5ZM -1.5 45C -1.5 70.6812 19.3188 91.5 45 91.5L 45 88.5C 20.9756 88.5 1.5 69.0244 1.5 45L -1.5 45Z"/> +<path id="path2_fill" d="M 0 0L 20 0L 20 3L 0 3L 0 0Z"/> +<path id="path3_fill" fill-rule="evenodd" d="M 3 0C 1.34314 0 0 1.34314 0 3L 0 31C 0 32.6569 1.34314 34 3 34L 38 34C 39.6569 34 41 32.6569 41 31L 41 3C 41 1.34314 39.6569 0 38 0L 3 0ZM 36 6L 5 6L 5 28L 36 28L 36 6Z"/> +</defs> +</svg> diff --git a/src/assets/images/gearvr_entry.svg b/src/assets/images/gearvr_entry.svg new file mode 100755 index 0000000000000000000000000000000000000000..c4ac081d1c451345486b7cd575e147825583f75f --- /dev/null +++ b/src/assets/images/gearvr_entry.svg @@ -0,0 +1,27 @@ +<svg width="94" height="94" viewBox="0 0 94 94" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>gearvr_entry</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-659 -395)"> +<g id="gearvr_entry"> +<g id="Oval Copy"> +<use xlink:href="#path0_fill" transform="translate(661 397)" fill="#D8D8D8" fill-opacity="0.01"/> +<use xlink:href="#path1_stroke" transform="translate(661 397)" fill="#FFFFFF"/> +</g> +<g id="Rectangle 3" opacity="0.995697"> +<use xlink:href="#path2_fill" transform="translate(683 429)" fill="#D8D8D8" fill-opacity="0.01"/> +<mask id="mask0_outline_ins"> +<use xlink:href="#path2_fill" fill="white" transform="translate(683 429)"/> +</mask> +<g mask="url(#mask0_outline_ins)"> +<use xlink:href="#path3_stroke_2x" transform="translate(683 429)" fill="#FFFFFF"/> +</g> +</g> +</g> +</g> +<defs> +<path id="path0_fill" fill-rule="evenodd" d="M 45 90C 69.8528 90 90 69.8528 90 45C 90 20.1472 69.8528 0 45 0C 20.1472 0 0 20.1472 0 45C 0 69.8528 20.1472 90 45 90Z"/> +<path id="path1_stroke" d="M 45 91.5C 70.6812 91.5 91.5 70.6812 91.5 45L 88.5 45C 88.5 69.0244 69.0244 88.5 45 88.5L 45 91.5ZM 91.5 45C 91.5 19.3188 70.6812 -1.5 45 -1.5L 45 1.5C 69.0244 1.5 88.5 20.9756 88.5 45L 91.5 45ZM 45 -1.5C 19.3188 -1.5 -1.5 19.3188 -1.5 45L 1.5 45C 1.5 20.9756 20.9756 1.5 45 1.5L 45 -1.5ZM -1.5 45C -1.5 70.6812 19.3188 91.5 45 91.5L 45 88.5C 20.9756 88.5 1.5 69.0244 1.5 45L -1.5 45Z"/> +<path id="path2_fill" fill-rule="evenodd" d="M 0 14C 0 6.26801 6.26801 0 14 0L 31 0C 38.732 0 45 6.26801 45 14C 45 21.732 38.732 28 31 28L 14 28C 6.26801 28 0 21.732 0 14Z"/> +<path id="path3_stroke_2x" d="M 14 9L 31 9L 31 -9L 14 -9L 14 9ZM 31 19L 14 19L 14 37L 31 37L 31 19ZM 14 19C 11.2386 19 9 16.7614 9 14L -9 14C -9 26.7025 1.29745 37 14 37L 14 19ZM 36 14C 36 16.7614 33.7614 19 31 19L 31 37C 43.7025 37 54 26.7025 54 14L 36 14ZM 31 9C 33.7614 9 36 11.2386 36 14L 54 14C 54 1.29745 43.7025 -9 31 -9L 31 9ZM 14 -9C 1.29745 -9 -9 1.29745 -9 14L 9 14C 9 11.2386 11.2386 9 14 9L 14 -9Z"/> +</defs> +</svg> diff --git a/src/assets/images/generic_vr_entry.svg b/src/assets/images/generic_vr_entry.svg new file mode 100755 index 0000000000000000000000000000000000000000..6d0a21948a016438edf526bcae6e88f9dc8a8d94 --- /dev/null +++ b/src/assets/images/generic_vr_entry.svg @@ -0,0 +1,20 @@ +<svg width="94" height="94" viewBox="0 0 94 94" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>generic_vr_entry</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-659 -395)"> +<g id="generic_vr_entry"> +<g id="Oval Copy"> +<use xlink:href="#path0_fill" transform="translate(661 397)" fill="#D8D8D8" fill-opacity="0.01"/> +<use xlink:href="#path1_stroke" transform="translate(661 397)" fill="#FFFFFF"/> +</g> +<g id="Imported Layers"> +<use xlink:href="#path2_fill" transform="translate(683 429)" fill="#FFFFFF"/> +</g> +</g> +</g> +<defs> +<path id="path0_fill" fill-rule="evenodd" d="M 45 90C 69.8528 90 90 69.8528 90 45C 90 20.1472 69.8528 0 45 0C 20.1472 0 0 20.1472 0 45C 0 69.8528 20.1472 90 45 90Z"/> +<path id="path1_stroke" d="M 45 91.5C 70.6812 91.5 91.5 70.6812 91.5 45L 88.5 45C 88.5 69.0244 69.0244 88.5 45 88.5L 45 91.5ZM 91.5 45C 91.5 19.3188 70.6812 -1.5 45 -1.5L 45 1.5C 69.0244 1.5 88.5 20.9756 88.5 45L 91.5 45ZM 45 -1.5C 19.3188 -1.5 -1.5 19.3188 -1.5 45L 1.5 45C 1.5 20.9756 20.9756 1.5 45 1.5L 45 -1.5ZM -1.5 45C -1.5 70.6812 19.3188 91.5 45 91.5L 45 88.5C 20.9756 88.5 1.5 69.0244 1.5 45L -1.5 45Z"/> +<path id="path2_fill" fill-rule="evenodd" d="M 42.1704 1.9159e-15L 2.71227 1.9159e-15C 1.24057 1.9159e-15 0 1.22604 0 2.73805L 0 25.2622C 0 26.7742 1.24057 28 2.77108 28L 13.5496 28C 14.7053 28 15.6958 27.3015 16.112 26.3081L 19.2462 18.8289C 19.7747 17.5677 21.0324 16.6805 22.5 16.6805C 23.9676 16.6805 25.2253 17.5677 25.7538 18.8289L 28.888 26.3081C 29.3042 27.3015 30.2947 28 31.3916 28L 42.1704 28C 43.7594 28 45 26.7742 45 25.2622L 45 2.73805C 45 1.22604 43.7594 1.9159e-15 42.1704 1.9159e-15ZM 12.2939 18.9274C 9.53693 18.9274 7.30457 16.7226 7.30457 14C 7.30457 11.2774 9.53693 9.07287 12.2939 9.07287C 15.0507 9.07287 17.2785 11.2774 17.2785 14C 17.2785 16.7226 15.0459 18.9274 12.2939 18.9274ZM 32.7086 18.9251C 29.9531 18.9251 27.7215 16.7214 27.7215 14C 27.7215 11.2789 29.9531 9.07514 32.7086 9.07514C 35.4641 9.07514 37.6957 11.2789 37.6957 14C 37.6957 16.7214 35.4641 18.9251 32.7086 18.9251Z"/> +</defs> +</svg> diff --git a/src/assets/images/level_fill.png b/src/assets/images/level_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..99f77b5655e6a50e0444364a3c2cfb4882b3b2d9 Binary files /dev/null and b/src/assets/images/level_fill.png differ diff --git a/src/assets/images/level_fill@2x.png b/src/assets/images/level_fill@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..477d9801bb6d33737b571fce454ff265ff79e77c Binary files /dev/null and b/src/assets/images/level_fill@2x.png differ diff --git a/src/assets/images/mic_denied.png b/src/assets/images/mic_denied.png new file mode 100644 index 0000000000000000000000000000000000000000..b6697e643bcdd328611f96c768c664a746c871c7 Binary files /dev/null and b/src/assets/images/mic_denied.png differ diff --git a/src/assets/images/mic_denied@2x.png b/src/assets/images/mic_denied@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..601c8684f3afdbdf30ef7932c89081caeebf0692 Binary files /dev/null and b/src/assets/images/mic_denied@2x.png differ diff --git a/src/assets/images/mic_granted.png b/src/assets/images/mic_granted.png new file mode 100644 index 0000000000000000000000000000000000000000..8b63010075d391879a5cffeccc273f9afb0d38d5 Binary files /dev/null and b/src/assets/images/mic_granted.png differ diff --git a/src/assets/images/mic_granted@2x.png b/src/assets/images/mic_granted@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..734ad47e1c7185357ce11719901849055f213c21 Binary files /dev/null and b/src/assets/images/mic_granted@2x.png differ diff --git a/src/assets/images/mic_level.png b/src/assets/images/mic_level.png new file mode 100644 index 0000000000000000000000000000000000000000..5be15458d9ed41c46f861d8dd8435a11e452f80c Binary files /dev/null and b/src/assets/images/mic_level.png differ diff --git a/src/assets/images/mic_level@2x.png b/src/assets/images/mic_level@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..94739aa1977cc5d5317eeb770905ed212ff248b4 Binary files /dev/null and b/src/assets/images/mic_level@2x.png differ diff --git a/src/assets/images/mic_small.png b/src/assets/images/mic_small.png new file mode 100644 index 0000000000000000000000000000000000000000..c6831f4f320d34f3550fdb0afc64845ed38ab83b Binary files /dev/null and b/src/assets/images/mic_small.png differ diff --git a/src/assets/images/mic_small@2x.png b/src/assets/images/mic_small@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b94cede41804adacdf7e268e6082baffca4403cb Binary files /dev/null and b/src/assets/images/mic_small@2x.png differ diff --git a/src/assets/images/mobile_screen_entry.svg b/src/assets/images/mobile_screen_entry.svg new file mode 100755 index 0000000000000000000000000000000000000000..8c249bfd80edfd5ca80461790ff76cdf594e87cd --- /dev/null +++ b/src/assets/images/mobile_screen_entry.svg @@ -0,0 +1,20 @@ +<svg width="94" height="94" viewBox="0 0 94 94" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>mobile_screen_entry</title> +<desc>Created using Figma</desc> +<g id="Canvas" transform="translate(-659 -277)"> +<g id="mobile_screen_entry"> +<g id="Oval Copy"> +<use xlink:href="#path0_fill" transform="translate(661 279)" fill="#D8D8D8" fill-opacity="0.01"/> +<use xlink:href="#path1_stroke" transform="translate(661 279)" fill="#FFFFFF"/> +</g> +<g id="Phone Copy"> +<use xlink:href="#path2_fill" transform="translate(693 305)" fill="#FFFFFF"/> +</g> +</g> +</g> +<defs> +<path id="path0_fill" fill-rule="evenodd" d="M 45 90C 69.8528 90 90 69.8528 90 45C 90 20.1472 69.8528 0 45 0C 20.1472 0 0 20.1472 0 45C 0 69.8528 20.1472 90 45 90Z"/> +<path id="path1_stroke" d="M 45 91.5C 70.6812 91.5 91.5 70.6812 91.5 45L 88.5 45C 88.5 69.0244 69.0244 88.5 45 88.5L 45 91.5ZM 91.5 45C 91.5 19.3188 70.6812 -1.5 45 -1.5L 45 1.5C 69.0244 1.5 88.5 20.9756 88.5 45L 91.5 45ZM 45 -1.5C 19.3188 -1.5 -1.5 19.3188 -1.5 45L 1.5 45C 1.5 20.9756 20.9756 1.5 45 1.5L 45 -1.5ZM -1.5 45C -1.5 70.6812 19.3188 91.5 45 91.5L 45 88.5C 20.9756 88.5 1.5 69.0244 1.5 45L -1.5 45Z"/> +<path id="path2_fill" fill-rule="evenodd" d="M 5 0C 2.23859 0 0 2.23859 0 5L 0 33C 0 35.7614 2.23859 38 5 38L 20 38C 22.7614 38 25 35.7614 25 33L 25 5C 25 2.23859 22.7614 0 20 0L 5 0ZM 22 4L 3 4L 3 29L 22 29L 22 4Z"/> +</defs> +</svg> diff --git a/src/assets/images/package.json b/src/assets/images/package.json new file mode 100644 index 0000000000000000000000000000000000000000..0a5693e4ee3946b74a1a1e9632f9abb5ddbd5d35 --- /dev/null +++ b/src/assets/images/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "moving-average": "^1.0.0" + } +} diff --git a/src/assets/images/speaker_level.png b/src/assets/images/speaker_level.png new file mode 100644 index 0000000000000000000000000000000000000000..9ccedcc0350f90c95744d928128594829b5f5b90 Binary files /dev/null and b/src/assets/images/speaker_level.png differ diff --git a/src/assets/images/speaker_level@2x.png b/src/assets/images/speaker_level@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a807745cbcaaf823e6e8e99deda15459d1ed1d9a Binary files /dev/null and b/src/assets/images/speaker_level@2x.png differ diff --git a/src/assets/images/warning_icon.png b/src/assets/images/warning_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d0394f96acda411c490f5ae224df90c6c2fdd802 Binary files /dev/null and b/src/assets/images/warning_icon.png differ diff --git a/src/assets/images/warning_icon@2x.png b/src/assets/images/warning_icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..14c27504afa3e5aaa2e76f25f023162a19d2c7dd Binary files /dev/null and b/src/assets/images/warning_icon@2x.png differ diff --git a/src/assets/loading.gif b/src/assets/loading.gif deleted file mode 100644 index 60f15ef2e34263e13f13450e264f1b7220b710ab..0000000000000000000000000000000000000000 Binary files a/src/assets/loading.gif and /dev/null differ diff --git a/src/assets/sfx/tone.ogg b/src/assets/sfx/tone.ogg new file mode 100755 index 0000000000000000000000000000000000000000..c1d5d5f765d600523a9cd920f08c26ab92489ae3 Binary files /dev/null and b/src/assets/sfx/tone.ogg differ diff --git a/src/assets/stylesheets/audio.scss b/src/assets/stylesheets/audio.scss new file mode 100644 index 0000000000000000000000000000000000000000..93e819f7a356f8b946c1292ff0ccddb5c0c03d5a --- /dev/null +++ b/src/assets/stylesheets/audio.scss @@ -0,0 +1,134 @@ +.audio-setup-panel { + display: flex; + flex-direction: column; + flex: 10 1 auto; + justify-content: flex-start; + align-items: center; +} + +.audio-setup-panel__title { + @extend %top-title; +} + +.audio-setup-panel__subtitle { + @extend %top-subtitle; +} + +.audio-setup-panel__device-chooser { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; +} + +.audio-setup-panel__enter-button { + @extend %bottom-button; +} + +.audio-setup-panel__device-chooser__dropdown { + @extend %rounded-border; + @extend %default-font; + + background-color: black; + padding: 6px; + color: white; + font-size: 1.1em; + width: 90%; +} + +.audio-setup-panel__device-chooser__mic-icon { + position: absolute; + left: 7.5%; + top: 10px; +} + +.audio-setup-panel__levels { + margin-top: 20px; + margin-bottom: 20px; + display: flex; + justify-content: space-evenly; + align-items: center; + width: 100%; +} + +.audio-setup-panel__levels__mic { + position:relative; + width: 111px; + height: 111px; +} + +.audio-setup-panel__levels__mic_icon { + position: absolute; + top: 0; + left: 0; + z-index: 2; + min-width: 111px; + min-height: 111px; +} + +.audio-setup-panel__levels__speaker { + position:relative; + width: 111px; + height: 111px; +} + +.audio-setup-panel__levels__speaker_icon { + position: absolute; + top: 0; + left: 0; + z-index: 2; + min-width: 111px; + min-height: 111px; +} + +.audio-setup-panel__levels__level { + position: absolute; + top: 0; + left: 0; + opacity: 1.0; + z-index: 1; +} + +.audio-setup-panel__hmd-mic-warning { + margin: 20px; +} + +.audio-setup-panel__hmd-mic-warning__label { + vertical-align: middle; + margin-left: 5px; +} + +.audio-setup-panel__hmd-mic-warning__icon { + vertical-align: middle; +} + +.mic-grant-panel { + display: flex; + flex-direction: column; + flex: 10 1 auto; + justify-content: flex-start; + align-items: center; +} + +.mic-grant-panel__title { + @extend %top-title; +} + +.mic-grant-panel__subtitle { + @extend %top-subtitle; +} + +.mic-grant-panel__icon { + flex: 10; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; +} + +.mic-grant-panel__next { + @extend %bottom-button; + margin: auto; + flex: 1 1 20px; +} diff --git a/src/assets/stylesheets/entry.scss b/src/assets/stylesheets/entry.scss new file mode 100644 index 0000000000000000000000000000000000000000..d30ffc98bc332d450d24dc29f3e2ebbcf5d21772 --- /dev/null +++ b/src/assets/stylesheets/entry.scss @@ -0,0 +1,53 @@ +.entry-dialog { + display: flex; + flex-direction: column; + height: 100%; +} + +.entry-button--bolded { + font-weight: bold; +} + +.entry-button__subtitle { + font-size: 0.7em; + color: $light-text; +} + +.entry-panel { + display: flex; + flex-direction: column; + flex: 10 1 auto; + justify-content: center; +} + +.entry-panel__secondary { + width: 100%; + text-align: center; + margin-top: 10px; + cursor: pointer; + color: $grey-text; +} + +.entry-button { + display: flex; + margin-left: 40px; + margin-top: 10px; + margin-bottom: 10px; + cursor: pointer; +} + +.entry-button__icon { + flex: 1 1 90px; + min-width: 90px; + min-height: 90px; +} + +.entry-button__label { + flex: 10 1 auto; + margin-left: 20px; + font-size: 1.5em; + display: flex; + flex-direction: column; + justify-content: center; +} + diff --git a/src/assets/stylesheets/exited.scss b/src/assets/stylesheets/exited.scss new file mode 100644 index 0000000000000000000000000000000000000000..341d382fecbe94680d7ffe8a4709c484074028f3 --- /dev/null +++ b/src/assets/stylesheets/exited.scss @@ -0,0 +1,41 @@ +.exited-panel { + background-color: black; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.exited-panel__title { + font-size: 1.2em; +} + +.exited-panel__subtitle { + font-size: 0.8em; +} + +.autoexit-panel { + display: flex; + flex-direction: column; + flex: 10 1 auto; + justify-content: flex-start; + align-items: center; + padding: 15px; +} + +.autoexit-panel__title { + @extend %top-title; +} + +.autoexit-panel__subtitle { + @extend %top-subtitle; +} + +.autoexit-panel__cancel-button { + @extend %bottom-button; + margin-top: 20px; + cursor: pointer; +} + diff --git a/src/assets/stylesheets/fonts.scss b/src/assets/stylesheets/fonts.scss new file mode 100644 index 0000000000000000000000000000000000000000..1ec1b6c12a0854b202b74f2a2f3d60ce9a3d517b --- /dev/null +++ b/src/assets/stylesheets/fonts.scss @@ -0,0 +1,17 @@ +/* latin-ext */ +@font-face { + font-family: 'Zilla Slab'; + font-style: normal; + font-weight: 400; + src: local('Zilla Slab'), local('ZillaSlab-Regular'), url('../fonts/zilla-slab_latin-ext.woff2') format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ +@font-face { + font-family: 'Zilla Slab'; + font-style: normal; + font-weight: 400; + src: local('Zilla Slab'), local('ZillaSlab-Regular'), url('../fonts/zilla-slab_latin.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/src/assets/stylesheets/loader.scss b/src/assets/stylesheets/loader.scss new file mode 100644 index 0000000000000000000000000000000000000000..400636ca8f4b7fb541ed62359b8f0b30238bb63e --- /dev/null +++ b/src/assets/stylesheets/loader.scss @@ -0,0 +1,84 @@ +.loader-wrap { + position: relative; + width: 100px; + height: 90px; +} + +.loading-panel { + background-color: black; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.loading-panel__title { + @extend %default-font; + font-size: 1.2em; +} + +.loader-center, +.loader:before, +.loader:after { + background: #555; + -webkit-animation: loader-animation 1s infinite ease-in-out; + animation: loader-animation 1s infinite ease-in-out; + width: 0.6em; + height: 1em; + border-radius: 5px; +} +.loader { + color: #555; + text-indent: -9999em; + margin-left: 4em; + margin-top: 4em; + font-size: 11px; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); +} +.loader-center, +.loader:before, +.loader:after { + position: absolute; + top: 0; + content: ''; +} +.loader-center { + -webkit-animation-delay: -0.16s; + animation-delay: -0.16s; +} +.loader:before { + left: -1.2em; + -webkit-animation-delay: -0.32s; + animation-delay: -0.32s; +} +.loader:after { + left: 1.2em; +} +@-webkit-keyframes loader-animation { + 0%, + 80%, + 100% { + height: 2em; + top: 0; + } + 40% { + height: 3.5em; + top: -0.75em; + } +} +@keyframes loader-animation { + 0%, + 80%, + 100% { + height: 2em; + top: 0; + } + 40% { + height: 3.5em; + top: -0.75em; + } +} diff --git a/src/assets/stylesheets/profile.scss b/src/assets/stylesheets/profile.scss new file mode 100644 index 0000000000000000000000000000000000000000..05b2e5d801f42cee41a9e245eb57daed200798cc --- /dev/null +++ b/src/assets/stylesheets/profile.scss @@ -0,0 +1,86 @@ +.profile-entry { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + justify-content: center; + align-items: center; + display: flex; + pointer-events: auto; +} + +.profile-entry__box { + height: 150px; + border-radius: 8px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 25px; + flex: 1 1 100%; +} + +.profile-entry__box--darkened { + background-color: $darkest-transparent; +} + +.profile-entry__subtitle { + width: 100%; + text-align: center; + font-size: 1.2em; + color: $grey-text; +} + +.profile-entry__form-field-text { + @extend %rounded-border; + @extend %default-font; + + color: $light-text; + font-size: 1.2em; + background-color: transparent; + line-height: 2.0em; + padding-left: 1.25em; + padding-right: 1.25em; +} + +.profile-entry__form-submit { + @extend %default-font; + border: none; + + margin: 8px; + width: 100px; + line-height: 1.5em; + font-size: 1.0em; + + background-color: transparent; + font-weight: bold; + color: white; + cursor: pointer; +} + +.profile-info-header { + display: flex; + flex: 1 1 40px; + width: 100%; +} + +.profile-info-header__icon { + cursor: pointer; + width: 20px; + height: 20px; + padding: 15px; +} + +.profile-info-header__profile_display_name { + cursor: pointer; + flex: 6 1 auto; + font-size: 1.2em; + line-height: 50px; +} + +.profile-info-header__app_name { + font-size: 1.8em; + padding-right: 18px; + line-height: 50px; +} diff --git a/src/assets/stylesheets/room.scss b/src/assets/stylesheets/room.scss new file mode 100644 index 0000000000000000000000000000000000000000..8f3cfb7b5ebf1553653649575d03c5e0a215956c --- /dev/null +++ b/src/assets/stylesheets/room.scss @@ -0,0 +1,19 @@ +@import 'fonts'; +@import 'shared'; + +@import 'loader'; +@import 'ui-root'; +@import 'exited'; +@import 'profile'; +@import 'entry'; +@import 'audio'; + +.a-enter-vr { + display: none; +} + +.rs-base { + top: auto; + bottom: 20px; +} + diff --git a/src/assets/stylesheets/shared.scss b/src/assets/stylesheets/shared.scss new file mode 100644 index 0000000000000000000000000000000000000000..a353915f5e8ec2e09a5c9ff79cadbd5aecd1732d --- /dev/null +++ b/src/assets/stylesheets/shared.scss @@ -0,0 +1,35 @@ +$dark-transparent: rgba(0, 0, 0, 0.4); +$darker-transparent: rgba(0, 0, 0, 0.6); +$darkest-transparent: rgba(0, 0, 0, 0.95); +$grey-text: rgba(192, 192, 192, 1.0); +$light-text: rgba(240, 240, 240, 1.0); +$dark-grey: rgba(128, 128, 128, 1.0); +$darker-grey: rgba(64, 64, 64, 1.0); + +%default-font { + font-family: 'Zilla Slab', sans-serif; +} + +%rounded-border { + border: 3px solid white; + box-sizing: border-box; + border-radius: 14px; +} + +%bottom-button { + font-size: 1em; + font-weight: bold; + margin-top: auto; + margin-bottom: 30px; + cursor: pointer; +} + +%top-title { + font-size: 1.3em; +} + +%top-subtitle { + font-size: 1.0em; + padding-top: 4px; +} + diff --git a/src/assets/stylesheets/ui-root.scss b/src/assets/stylesheets/ui-root.scss new file mode 100644 index 0000000000000000000000000000000000000000..9d252f04d8ccb94b203a5cd2ef46ec2caf062d2b --- /dev/null +++ b/src/assets/stylesheets/ui-root.scss @@ -0,0 +1,51 @@ +.ui { + @extend %default-font; + + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + pointer-events: none; + color: white; +} + +.blurred { + filter: blur(5px); +} + +.ui-dialog { + display: grid; + grid-template-columns: 1fr 20px minmax(200px, 400px) 20px 1fr; + grid-template-rows: 1fr 20px minmax(400px, 600px) 20px 1fr; + width: 100%; + height: 100%; +} + +.ui-dialog--darkened { + background-color: $dark-transparent; +} + +.ui-dialog-box { + grid-column: 3; + grid-row : 3; + position: relative; +} + +.ui-dialog-box-contents { + background-color: $darker-transparent; + border-radius: 8px; + pointer-events: auto; + width: 100%; + height: 100%; +} + +.ui-dialog-box--backgrounded { +} + +.ui-dialog-box-contents--backgrounded { + filter: blur(1px); + opacity: 0.7; + pointer-events: none; +} + diff --git a/src/assets/translations.data.json b/src/assets/translations.data.json new file mode 100644 index 0000000000000000000000000000000000000000..e10a95ae40bb533716f17b93f15d0f196a6de13e --- /dev/null +++ b/src/assets/translations.data.json @@ -0,0 +1,35 @@ +{ + "en": + { + "entry.screen-prefix": "Enter on ", + "entry.desktop-screen": "Screen", + "entry.mobile-screen": "Phone", + "entry.generic-prefix": "Enter in ", + "entry.generic-medium": "VR", + "entry.gearvr-prefix": "Enter on ", + "entry.gearvr-medium": "GearVR", + "entry.cardboard": "Enter on Google Cardboard", + "entry.daydream-prefix": "Enter on ", + "entry.daydream-medium": "Daydream", + "entry.daydream-via-chrome": "Using Google Chrome", + "profile.save": "SAVE", + "profile.display_name.validation_warning": "Alphanumerics and hyphens. At least 3 characters, no more than 32", + "profile.header": "Your identity", + "audio.title": "Test your audio", + "audio.subtitle-desktop": "Confirm HMD speaker output", + "audio.subtitle-mobile": "Earphones are recommended", + "audio.enter-now": "ENTER NOW", + "audio.hmd-mic-warning": "Your HMD mic is not chosen", + "audio.grant-title": "Grant mic permissions", + "audio.grant-subtitle": "Mic access needed to be heard by others", + "audio.granted-title": "Mic permissions granted", + "audio.granted-subtitle": "You can still mute yourself in-game", + "audio.grant-next": " ", + "audio.granted-next": "NEXT", + "exit.subtitle": "Your session has ended.", + "autoexit.title": "Auto-ending session in ", + "autoexit.title_units": " seconds", + "autoexit.subtitle": "You have started another session.", + "autoexit.cancel": "CANCEL" + } +} diff --git a/src/react-components/auto-exit-warning.js b/src/react-components/auto-exit-warning.js new file mode 100644 index 0000000000000000000000000000000000000000..8663dcca2277d42456511adb212238d3bcdb936f --- /dev/null +++ b/src/react-components/auto-exit-warning.js @@ -0,0 +1,26 @@ +import React, { Component } from "react"; +import { FormattedMessage } from "react-intl"; +import PropTypes from "prop-types"; + +const AutoExitWarning = props => ( + <div className="autoexit-panel"> + <div className="autoexit-panel__title"> + <FormattedMessage id="autoexit.title" /> + <span>{props.secondsRemaining}</span> + <FormattedMessage id="autoexit.title_units" /> + </div> + <div className="autoexit-panel__subtitle"> + <FormattedMessage id="autoexit.subtitle" /> + </div> + <div className="autoexit-panel__cancel-button" onClick={props.onCancel}> + <FormattedMessage id="autoexit.cancel" /> + </div> + </div> +); + +AutoExitWarning.propTypes = { + secondsRemaining: PropTypes.number, + onCancel: PropTypes.func +}; + +export default AutoExitWarning; diff --git a/src/react-components/entry-buttons.js b/src/react-components/entry-buttons.js new file mode 100644 index 0000000000000000000000000000000000000000..e567c6e1111f362ab4175087794af1f38a6a1c24 --- /dev/null +++ b/src/react-components/entry-buttons.js @@ -0,0 +1,82 @@ +import React, { Component } from "react"; +import { FormattedMessage } from "react-intl"; +import PropTypes from "prop-types"; +import MobileDetect from 'mobile-detect'; + +import MobileScreenEntryImg from '../assets/images/mobile_screen_entry.svg'; +import DesktopScreenEntryImg from '../assets/images/desktop_screen_entry.svg'; +import GenericVREntryImg from '../assets/images/generic_vr_entry.svg'; +import GearVREntryImg from '../assets/images/gearvr_entry.svg'; +import DaydreamEntyImg from '../assets/images/daydream_entry.svg'; + +const mobiledetect = new MobileDetect(navigator.userAgent); + +const EntryButton = (props) => ( + <div className="entry-button" onClick={ props.onClick }> + <img src={props.iconSrc} className="entry-button__icon"/> + <div className="entry-button__label"> + <div className="entry-button__label__contents"> + <span> + <FormattedMessage id={ props.prefixMessageId }/> + </span> + <span className="entry-button--bolded"> + <FormattedMessage id={ props.mediumMessageId }/> + </span> + { props.subtitle && (<div className="entry-button__subtitle">{props.subtitle}</div>) } + </div> + </div> + </div> +); + +EntryButton.propTypes = { + onClick: PropTypes.func, + iconSrc: PropTypes.string, + prefixMessageId: PropTypes.string, + mediumMessageId: PropTypes.string, + subtitle: PropTypes.string +} + +export const TwoDEntryButton = (props) => { + const entryButtonProps = { + ...props, + iconSrc: mobiledetect.mobile() ? MobileScreenEntryImg : DesktopScreenEntryImg, + prefixMessageId: "entry.screen-prefix", + mediumMessageId: mobiledetect.mobile() ? "entry.mobile-screen" : "entry.desktop-screen" + }; + + return (<EntryButton {...entryButtonProps}/>); +} + +export const GenericEntryButton = (props) => { + const entryButtonProps = { + ...props, + iconSrc: GenericVREntryImg, + prefixMessageId: "entry.generic-prefix", + mediumMessageId: "entry.generic-medium" + }; + + return (<EntryButton {...entryButtonProps}/>); +}; + +export const GearVREntryButton = (props) => { + const entryButtonProps = { + ...props, + iconSrc: GearVREntryImg, + prefixMessageId: "entry.gearvr-prefix", + mediumMessageId: "entry.gearvr-medium" + }; + + return (<EntryButton {...entryButtonProps}/>); +}; + +export const DaydreamEntryButton = (props) => { + const entryButtonProps = { + ...props, + iconSrc: DaydreamEntyImg, + prefixMessageId: "entry.daydream-prefix", + mediumMessageId: "entry.daydream-medium" + }; + + return (<EntryButton {...entryButtonProps}/>); +}; + diff --git a/src/react-components/profile-entry-panel.js b/src/react-components/profile-entry-panel.js new file mode 100644 index 0000000000000000000000000000000000000000..28c8cd7d9ce8c8bf9902f6a654c835bbd7fe95ea --- /dev/null +++ b/src/react-components/profile-entry-panel.js @@ -0,0 +1,65 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl, FormattedMessage } from 'react-intl'; +import { SCHEMA } from "../storage/store"; + +class ProfileEntryPanel extends Component { + static propTypes = { + store: PropTypes.object, + messages: PropTypes.object, + finished: PropTypes.func + } + + constructor(props) { + super(props); + window.store = this.props.store; + this.state = {name: this.props.store.state.profile.display_name}; + this.props.store.addEventListener("statechanged", this.storeUpdated); + } + + storeUpdated = () => { + this.setState({name: this.props.store.state.profile.display_name}); + } + + saveName = (e) => { + e.preventDefault(); + this.props.store.update({ profile: { display_name: this.nameInput.value } }); + this.props.finished(); + } + + componentDidMount() { + // stop propagation so that avatar doesn't move when wasd'ing during text input. + this.nameInput.addEventListener('keydown', e => e.stopPropagation()); + this.nameInput.addEventListener('keypress', e => e.stopPropagation()); + this.nameInput.addEventListener('keyup', e => e.stopPropagation()); + } + + componentWillUnmount() { + this.props.store.removeEventListener('statechanged', this.storeUpdated); + } + + render () { + const { formatMessage } = this.props.intl; + + return ( + <div className="profile-entry"> + <form onSubmit={this.saveName}> + <div className="profile-entry__box profile-entry__box--darkened"> + <div className="profile-entry__subtitle"> + <FormattedMessage id="profile.header"/> + </div> + <input + className="profile-entry__form-field-text" + value={this.state.name} onChange={(e) => this.setState({name: e.target.value})} + required pattern={SCHEMA.definitions.profile.properties.display_name.pattern} + title={formatMessage({ id: "profile.display_name.validation_warning" })} + ref={inp => this.nameInput = inp}/> + <input className="profile-entry__form-submit" type="submit" value={formatMessage({ id: "profile.save" }) }/> + </div> + </form> + </div> + ); + } +} + +export default injectIntl(ProfileEntryPanel); diff --git a/src/react-components/profile-info-header.js b/src/react-components/profile-info-header.js new file mode 100644 index 0000000000000000000000000000000000000000..6b9e82bb124e931dbf59b0867eaa3e36aae431f2 --- /dev/null +++ b/src/react-components/profile-info-header.js @@ -0,0 +1,19 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; + +export const ProfileInfoHeader = props => ( + <div className="profile-info-header"> + <img src="../assets/images/account.svg" onClick={props.onClick} className="profile-info-header__icon" /> + <div className="profile-info-header__profile_display_name" onClick={props.onClick}> + {props.name} + </div> + <div className="profile-info-header__app_name"> + <b>moz://a</b> duck + </div> + </div> +); + +ProfileInfoHeader.propTypes = { + onClick: PropTypes.func, + name: PropTypes.string +}; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js new file mode 100644 index 0000000000000000000000000000000000000000..e2f4c2a1629def812e484f2617b07d3c61548d2a --- /dev/null +++ b/src/react-components/ui-root.js @@ -0,0 +1,523 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { VR_DEVICE_AVAILABILITY } from "../utils/vr-caps-detect"; +import queryString from "query-string"; +import { SCHEMA } from "../storage/store"; +import MobileDetect from 'mobile-detect'; +import { IntlProvider, FormattedMessage, addLocaleData } from 'react-intl'; +import en from 'react-intl/locale-data/en'; +import MovingAverage from 'moving-average'; + +import AutoExitWarning from './auto-exit-warning'; +import { TwoDEntryButton, GenericEntryButton, GearVREntryButton, DaydreamEntryButton } from './entry-buttons.js'; +import { ProfileInfoHeader } from './profile-info-header.js'; +import ProfileEntryPanel from './profile-entry-panel'; + +const mobiledetect = new MobileDetect(navigator.userAgent); + +const lang = ((navigator.languages && navigator.languages[0]) || + navigator.language || navigator.userLanguage).toLowerCase().split(/[_-]+/)[0]; + +import localeData from '../assets/translations.data.json'; +addLocaleData([...en]); + +const messages = localeData[lang] || localeData.en; + +const ENTRY_STEPS = { + start: "start", + mic_grant: "mic_grant", + mic_granted: "mic_granted", + audio_setup: "audio_setup", + finished: "finished" +} + +const HMD_MIC_REGEXES = [/\Wvive\W/i, /\Wrift\W/i]; + +async function grantedMicLabels() { + const mediaDevices = await navigator.mediaDevices.enumerateDevices(); + return mediaDevices.filter(d => d.label && d.kind === "audioinput").map(d => d.label); +} + +async function hasGrantedMicPermissions() { + const micLabels = await grantedMicLabels(); + return micLabels.length > 0; +} + +// This is a list of regexes that match the microphone labels of HMDs. +// +// If entering VR mode, and if any of these regexes match an audio device, +// the user will be prevented from entering VR until one of those devices is +// selected as the microphone. +// +// Note that this doesn't have to be exhaustive: if no devices match any regex +// then we rely upon the user to select the proper mic. +const VR_DEVICE_MIC_LABEL_REGEXES = []; + +const AUTO_EXIT_TIMER_SECONDS = 10; + +class UIRoot extends Component { + static propTypes = { + enterScene: PropTypes.func, + concurrentLoadDetector: PropTypes.object, + disableAutoExitOnConcurrentLoad: PropTypes.bool, + forcedVREntryType: PropTypes.string, + store: PropTypes.object, + scene: PropTypes.object + } + + state = { + availableVREntryTypes: null, + entryStep: ENTRY_STEPS.start, + enterInVR: false, + + shareScreen: false, + mediaStream: null, + + toneInterval: null, + tonePlaying: false, + + micLevel: 0, + micDevices: [], + micUpdateInterval: null, + + profileNamePending: "Hello", + + autoExitTimerStartedAt: null, + autoExitTimerInterval: null, + secondsRemainingBeforeAutoExit: Infinity, + + sceneLoaded: false, + exited: false, + + showProfileEntry: false + } + + componentDidMount() { + this.setupTestTone(); + this.props.concurrentLoadDetector.addEventListener("concurrentload", this.onConcurrentLoad); + this.micLevelMovingAverage = MovingAverage(100); + this.props.scene.addEventListener("loaded", this.onSceneLoaded); + } + + componentWillUnmount() { + this.props.scene.removeEventListener("loaded", this.onSceneLoaded); + } + + onSceneLoaded = () => { + this.setState({ sceneLoaded: true }); + } + + handleForcedVREntryType = () => { + if (!this.props.forcedVREntryType) return; + + if (this.props.forcedVREntryType === "daydream") { + this.enterDaydream(); + } else if (this.props.forcedVREntryType === "gearvr") { + this.enterGearVR(); + } + } + + setupTestTone = () => { + const toneClip = document.querySelector("#test-tone"); + const toneLength = 1800; + const toneDelay = 5000; + + const toneIndicatorLoop = () => { + this.setState({ tonePlaying: false }); + + setTimeout(() => { + this.setState({ tonePlaying: true }); + setTimeout(() => { this.setState({ tonePlaying: false }); }, toneLength) + }, toneDelay); + }; + + toneClip.addEventListener("seeked", toneIndicatorLoop); + toneClip.addEventListener("playing", toneIndicatorLoop); + } + + startTestTone = () => { + const toneClip = document.querySelector("#test-tone"); + toneClip.loop = true; + toneClip.play(); + } + + stopTestTone = () => { + const toneClip = document.querySelector("#test-tone") + toneClip.pause(); + toneClip.currentTime = 0; + + this.setState({ tonePlaying: false }) + } + + onConcurrentLoad = () => { + if (this.props.disableAutoExitOnConcurrentLoad) return; + + const autoExitTimerInterval = setInterval(() => { + let secondsRemainingBeforeAutoExit = Infinity; + + if (this.state.autoExitTimerStartedAt) { + const secondsSinceStart = (new Date() - this.state.autoExitTimerStartedAt) / 1000; + secondsRemainingBeforeAutoExit = Math.max(0, Math.floor(AUTO_EXIT_TIMER_SECONDS - secondsSinceStart)); + } + + this.setState({ secondsRemainingBeforeAutoExit }); + this.checkForAutoExit(); + }, 500); + + this.setState({ autoExitTimerStartedAt: new Date(), autoExitTimerInterval }) + } + + checkForAutoExit = () => { + if (this.state.secondsRemainingBeforeAutoExit !== 0) return; + this.endAutoExitTimer(); + this.exit(); + } + + exit = () => { + this.props.exitScene(); + this.setState({ exited: true }); + } + + isWaitingForAutoExit = () => { + return this.state.secondsRemainingBeforeAutoExit <= AUTO_EXIT_TIMER_SECONDS; + } + + endAutoExitTimer = () => { + clearInterval(this.state.autoExitTimerInterval); + this.setState({ autoExitTimerStartedAt: null, autoExitTimerInterval: null, secondsRemainingBeforeAutoExit: Infinity }); + } + + performDirectEntryFlow = async (enterInVR) => { + this.startTestTone(); + + this.setState({ enterInVR }) + + const hasGrantedMic = await hasGrantedMicPermissions(); + + if (hasGrantedMic) { + await this.setMediaStreamToDefault(); + await this.beginAudioSetup(); + } else { + this.stopTestTone(); + this.setState({ entryStep: ENTRY_STEPS.mic_grant }); + } + } + + enter2D = async () => { + await this.performDirectEntryFlow(false); + } + + enterVR = async () => { + await this.performDirectEntryFlow(true); + } + + enterGearVR = async () => { + this.exit(); + + // Launch via Oculus Browser + const qs = queryString.parse(document.location.search); + qs.vr_entry_type = "gearvr"; // Auto-choose 'gearvr' after landing in Oculus Browser + + const ovrwebUrl = `ovrweb://${document.location.protocol || "http:"}//${document.location.host}${document.location.pathname || ""}?${queryString.stringify(qs)}#{document.location.hash || ""}`; + + document.location = ovrwebUrl; + } + + enterDaydream = async () => { + const loc = document.location; + + if (this.state.availableVREntryTypes.daydream == VR_DEVICE_AVAILABILITY.maybe) { + this.exit(); + + // We are not in mobile chrome, so launch into chrome via an Intent URL + const qs = queryString.parse(document.location.search); + qs.vr_entry_type = "daydream"; // Auto-choose 'daydream' after landing in chrome + + const intentUrl = `intent://${document.location.host}${document.location.pathname || ""}?${queryString.stringify(qs)}#Intent;scheme=${(document.location.protocol || "http:").replace(":", "")};action=android.intent.action.VIEW;package=com.android.chrome;end;`; + document.location = intentUrl; + } else { + await this.performDirectEntryFlow(true); + } + } + + mediaVideoConstraint = () => { + return this.state.shareScreen ? { mediaSource: "screen", height: 720, frameRate: 30 } : false; + } + + micDeviceChanged = async (ev) => { + const constraints = { audio: { deviceId: { exact: [ev.target.value] } }, video: this.mediaVideoConstraint() }; + await this.setupNewMediaStream(constraints); + } + + setMediaStreamToDefault = async () => { + await this.setupNewMediaStream({ audio: true, video: false }); + } + + setupNewMediaStream = async (constraints) => { + const AudioContext = window.AudioContext || window.webkitAudioContext; + const audioContext = new AudioContext(); + + if (this.state.mediaStream) { + clearInterval(this.state.micUpdateInterval); + + const previousStream = this.state.mediaStream; + + for (const tracks of [previousStream.getAudioTracks(), previousStream.getVideoTracks()]) { + for (const track of tracks) { + track.stop(); + } + } + } + + const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); + + const source = audioContext.createMediaStreamSource(mediaStream); + const analyzer = audioContext.createAnalyser(); + const levels = new Uint8Array(analyzer.fftSize); + + source.connect(analyzer); + + const micUpdateInterval = setInterval(() => { + analyzer.getByteTimeDomainData(levels); + + let v = 0; + + for (let x = 0; x < levels.length; x++) { + v = Math.max(levels[x] - 127, v); + } + + const level = v / 128.0 ; + this.micLevelMovingAverage.push(Date.now(), level); + this.setState({ micLevel: this.micLevelMovingAverage.movingAverage() }) + }, 50); + + this.setState({ mediaStream, micUpdateInterval }); + } + + onMicGrantButton = async () => { + if (this.state.entryStep == ENTRY_STEPS.mic_grant) { + await this.setMediaStreamToDefault(); + this.setState({ entryStep: ENTRY_STEPS.mic_granted }); + } else { + this.startTestTone(); + await this.beginAudioSetup(); + } + } + + onProfileFinished = () => { + this.setState({ showProfileEntry: false }) + } + + beginAudioSetup = async () => { + await this.fetchMicDevices(); + this.setState({ entryStep: ENTRY_STEPS.audio_setup }); + } + + fetchMicDevices = async () => { + const mediaDevices = await navigator.mediaDevices.enumerateDevices(); + this.setState({ micDevices: mediaDevices.filter(d => d.kind === "audioinput").map(d => ({ deviceId: d.deviceId, label: d.label }))}); + } + + shouldShowHmdMicWarning = () => { + if (mobiledetect.mobile()) return false; + if (!this.state.enterInVR) return false; + if (!this.hasHmdMicrophone()) return false; + + return !(HMD_MIC_REGEXES.find(r => this.selectedMicLabel().match(r))); + } + + hasHmdMicrophone = () => { + return !!(this.state.micDevices.find(d => HMD_MIC_REGEXES.find(r => d.label.match(r)))); + } + + selectedMicLabel = () => { + return (this.state.mediaStream + && this.state.mediaStream.getAudioTracks().length > 0 + && this.state.mediaStream.getAudioTracks()[0].label) || ""; + } + + selectedMicDeviceId = () => { + return this.state.micDevices.filter(d => d.label === this.selectedMicLabel).map(d => d.deviceId)[0]; + } + + onAudioReadyButton = () => { + this.props.enterScene(this.state.mediaStream, this.state.enterInVR); + + const mediaStream = this.state.mediaStream; + + if (mediaStream) { + if (mediaStream.getAudioTracks().length > 0) { + console.log(`Using microphone: ${mediaStream.getAudioTracks()[0].label}`) + } + + if (mediaStream.getVideoTracks().length > 0) { + console.log('Screen sharing enabled.') + } + } + + this.stopTestTone(); + this.setState({ entryStep: ENTRY_STEPS.finished }); + } + + render() { + if (!this.props.scene.hasLoaded || !this.state.availableVREntryTypes) { + return ( + <IntlProvider locale={lang} messages={messages}> + <div className="loading-panel"> + <div className="loader-wrap"> + <div className="loader"> + <div className="loader-center"/> + </div> + </div> + <div className="loading-panel__title"> + <b>moz://a</b> duck + </div> + </div> + </IntlProvider> + ); + } + + if (this.state.exited) { + return ( + <IntlProvider locale={lang} messages={messages}> + <div className="exited-panel"> + <div className="loading-panel__title"> + <b>moz://a</b> duck + </div> + <div className="loading-panel__subtitle"> + <FormattedMessage id="exit.subtitle"/> + </div> + </div> + </IntlProvider> + ); + } + + const daydreamMaybeSubtitle = messages["entry.daydream-via-chrome"]; + + const entryPanel = this.state.entryStep === ENTRY_STEPS.start + ? ( + <div className="entry-panel"> + <TwoDEntryButton onClick={this.enter2D}/> + { this.state.availableVREntryTypes.generic !== VR_DEVICE_AVAILABILITY.no && <GenericEntryButton onClick={this.enterVR}/> } + { this.state.availableVREntryTypes.gearvr !== VR_DEVICE_AVAILABILITY.no && <GearVREntryButton onClick={this.enterGearVR}/> } + { this.state.availableVREntryTypes.daydream !== VR_DEVICE_AVAILABILITY.no && + <DaydreamEntryButton + onClick={this.enterDaydream} + subtitle={this.state.availableVREntryTypes.daydream == VR_DEVICE_AVAILABILITY.maybe ? daydreamMaybeSubtitle : "" }/> } + { this.state.availableVREntryTypes.cardboard !== VR_DEVICE_AVAILABILITY.no && + (<div className="entry-panel__secondary" onClick={this.enterVR}><FormattedMessage id="entry.cardboard"/></div>) } + </div> + ) : null; + + const micPanel = this.state.entryStep === ENTRY_STEPS.mic_grant || this.state.entryStep == ENTRY_STEPS.mic_granted + ? ( + <div className="mic-grant-panel"> + <div className="mic-grant-panel__title"> + <FormattedMessage id={ this.state.entryStep == ENTRY_STEPS.mic_grant ? "audio.grant-title" : "audio.granted-title" }/> + </div> + <div className="mic-grant-panel__subtitle"> + <FormattedMessage id={ this.state.entryStep == ENTRY_STEPS.mic_grant ? "audio.grant-subtitle" : "audio.granted-subtitle" }/> + </div> + <div className="mic-grant-panel__icon"> + { this.state.entryStep == ENTRY_STEPS.mic_grant ? + (<img onClick={this.onMicGrantButton} src="../assets/images/mic_denied.png" srcSet="../assets/images/mic_denied@2x.png 2x" className="mic-grant-panel__icon"/>) : + (<img onClick={this.onMicGrantButton} src="../assets/images/mic_granted.png" srcSet="../assets/images/mic_granted@2x.png 2x" className="mic-grant-panel__icon"/>)} + </div> + <div className="mic-grant-panel__next" onClick={this.onMicGrantButton}> + <FormattedMessage id={ this.state.entryStep == ENTRY_STEPS.mic_grant ? "audio.grant-next" : "audio.granted-next" }/> + </div> + </div> + ) : null; + + const maxLevelHeight = 111; + const micClip = { clip: `rect(${maxLevelHeight - Math.floor(this.state.micLevel * maxLevelHeight)}px, 111px, 111px, 0px)` }; + const speakerClip = { clip: `rect(${this.state.tonePlaying ? 0 : maxLevelHeight}px, 111px, 111px, 0px)` }; + + const audioSetupPanel = this.state.entryStep === ENTRY_STEPS.audio_setup + ? ( + <div className="audio-setup-panel"> + <div className="audio-setup-panel__title"> + <FormattedMessage id="audio.title"/> + </div> + <div className="audio-setup-panel__subtitle"> + { (mobiledetect.mobile() || this.state.enterInVR) && (<FormattedMessage id={ mobiledetect.mobile() ? "audio.subtitle-mobile" : "audio.subtitle-desktop" }/>) } + </div> + <div className="audio-setup-panel__levels"> + <div className="audio-setup-panel__levels__mic"> + <img src="../assets/images/mic_level.png" srcSet="../assets/images/mic_level@2x.png 2x" className="audio-setup-panel__levels__mic_icon"/> + <img src="../assets/images/level_fill.png" srcSet="../assets/images/level_fill@2x.png 2x" className="audio-setup-panel__levels__level" style={ micClip }/> + </div> + <div className="audio-setup-panel__levels__speaker"> + <img src="../assets/images/speaker_level.png" srcSet="../assets/images/speaker_level@2x.png 2x" className="audio-setup-panel__levels__speaker_icon"/> + <img src="../assets/images/level_fill.png" srcSet="../assets/images/level_fill@2x.png 2x" className="audio-setup-panel__levels__level" style={ speakerClip }/> + </div> + </div> + <div className="audio-setup-panel__device-chooser"> + <select className="audio-setup-panel__device-chooser__dropdown" value={this.selectedMicDeviceId()} onChange={this.micDeviceChanged}> + { this.state.micDevices.map(d => (<option key={ d.deviceId } value={ d.deviceId }> {d.label}</option>)) } + </select> + <div className="audio-setup-panel__device-chooser__mic-icon"> + <img src="../assets/images/mic_small.png" srcSet="../assets/images/mic_small@2x.png 2x"/> + </div> + </div> + { this.shouldShowHmdMicWarning() && + (<div className="audio-setup-panel__hmd-mic-warning"> + <img src="../assets/images/warning_icon.png" srcSet="../assets/images/warning_icon@2x.png 2x" + className="audio-setup-panel__hmd-mic-warning__icon"/> + <span className="audio-setup-panel__hmd-mic-warning__label"> + <FormattedMessage id="audio.hmd-mic-warning"/> + </span> + </div>) } + <div className="audio-setup-panel__enter-button" onClick={this.onAudioReadyButton}> + <FormattedMessage id="audio.enter-now"/> + </div> + </div> + ) : null; + + const dialogContents = this.isWaitingForAutoExit() ? + (<AutoExitWarning secondsRemaining={this.state.secondsRemainingBeforeAutoExit} onCancel={this.endAutoExitTimer} />) : + ( + <div className="entry-dialog"> + <ProfileInfoHeader name={this.props.store.state.profile.display_name} onClick={(() => this.setState({showProfileEntry: true })) }/> + {entryPanel} + {micPanel} + {audioSetupPanel} + </div> + ); + + const dialogClassNames = classNames({ + 'ui-dialog': true, + 'ui-dialog--darkened': this.state.entryStep !== ENTRY_STEPS.finished + }); + + const dialogBoxClassNames = classNames({ 'ui-dialog-box': true }); + + const dialogBoxContentsClassNames = classNames({ + 'ui-dialog-box-contents': true, + 'ui-dialog-box-contents--backgrounded': this.state.showProfileEntry + }); + + return ( + <IntlProvider locale={lang} messages={messages}> + <div className={dialogClassNames}> + { + (this.state.entryStep !== ENTRY_STEPS.finished || this.isWaitingForAutoExit()) && + ( + <div className={dialogBoxClassNames}> + <div className={dialogBoxContentsClassNames}> + {dialogContents} + </div> + + {this.state.showProfileEntry && ( + <ProfileEntryPanel finished={this.onProfileFinished} store={this.props.store}/>)} + </div> + ) + } + </div> + </IntlProvider> + ); + } +} + +export default UIRoot; diff --git a/src/room.css b/src/room.css index 9023e6171f5c6519c94840e7583ecc7baf38e9b8..6d852a32c777b81248d1f1a3fe30f4fb8ca8f4d5 100644 --- a/src/room.css +++ b/src/room.css @@ -1,18 +1,3 @@ -.a-enter-vr { - top: 90px; - bottom: auto; -} - -#loader { - position: fixed; - width: 100vw; - height: 100vh; - z-index: 10001; - background: #eaeaea no-repeat url("./assets/loading.gif") center center; - opacity: 0.9; -} - - .a-canvas.a-grab-cursor:hover { cursor: none; } diff --git a/src/room.html b/src/room.html index 3102445e849f3e7d77f8c0d79d79da8224e55868..096d8b5755ab276d4ea09b907a506f9b66f1066e 100644 --- a/src/room.html +++ b/src/room.html @@ -14,14 +14,10 @@ </head> <body> - <div id="loader"></div> + <audio id="test-tone" src="./assets/sfx/tone.ogg"></audio> + <a-scene physics - networked-scene="adapter: janus; - audio: true; - debug: true; - onConnect: App.onConnect; - connectOnLoad: false;" mute-mic="eventSrc: a-scene; toggleEvents: action_mute" 2d-mute-state-indicator light="defaultLightsEnabled: false"> @@ -301,6 +297,8 @@ class="collidable" ></a-plane> </a-scene> + + <div id="ui-root" class="ui"></div> </body> </html> diff --git a/src/room.js b/src/room.js index 92adbce7b00821fcd5660f3737fe92731336c64f..0ff220f9329e9504e1a7afcbf486e384a467e633 100644 --- a/src/room.js +++ b/src/room.js @@ -1,4 +1,4 @@ -import "./room.css"; +import "./assets/stylesheets/room.scss"; import queryString from "query-string"; import { patchWebGLRenderingContext } from "./utils/webgl"; @@ -40,6 +40,10 @@ import "./components/hide-when-quality"; import "./components/animation-mixer"; import "./components/loop-animation"; +import ReactDOM from "react-dom"; +import React from "react"; +import UIRoot from "./react-components/ui-root"; + import "./systems/personal-space-bubble"; import "./gltf-component-mappings"; @@ -68,19 +72,29 @@ import "./components/super-spawner"; import "./components/super-cursor"; import "./components/event-repeater"; -import { promptForName, getCookie, parseJwt } from "./utils/identity"; import registerNetworkSchemas from "./network-schemas"; -import { inGameActions, config } from "./input-mappings"; +import { inGameActions, config as inputConfig } from "./input-mappings"; import registerTelemetry from "./telemetry"; +import Store from "./storage/store"; + +import { generateDefaultProfile } from "./utils/identity.js"; +import { getAvailableVREntryTypes } from "./utils/vr-caps-detect.js"; +import ConcurrentLoadDetector from "./utils/concurrent-load-detector.js"; + +registerTelemetry(); AFRAME.registerInputBehaviour("vive_trackpad_dpad4", vive_trackpad_dpad4); AFRAME.registerInputBehaviour("oculus_touch_joystick_dpad4", oculus_touch_joystick_dpad4); AFRAME.registerInputActivator("pressedmove", PressedMove); AFRAME.registerInputActivator("reverseY", ReverseY); -AFRAME.registerInputActions(inGameActions, "default"); -AFRAME.registerInputMappings(config); +AFRAME.registerInputMappings(inputConfig, true); -registerTelemetry(); +const store = new Store(); +const concurrentLoadDetector = new ConcurrentLoadDetector(); +concurrentLoadDetector.start(); + +// Always layer in any new default profile bits +store.update({ profile: { ...generateDefaultProfile(), ...(store.state.profile || {}) }}) async function shareMedia(audio, video) { const constraints = { @@ -108,8 +122,30 @@ async function shareMedia(audio, video) { } } -async function onSceneLoad() { +async function exitScene() { + const scene = document.querySelector("a-scene"); + scene.renderer.animate(null); // Stop animation loop, TODO A-Frame should do this + document.body.removeChild(scene); +} + +function setNameTagFromStore() { + const myNametag = document.querySelector("#player-rig .nametag"); + myNametag.setAttribute("text", "value", store.state.profile.display_name); +} + +async function enterScene(mediaStream, enterInVR) { const scene = document.querySelector("a-scene"); + document.querySelector("a-scene canvas").classList.remove("blurred") + scene.setAttribute("networked-scene", "adapter: janus; audio: true; debug: true; connectOnLoad: false;"); + registerNetworkSchemas(); + + if (enterInVR) { + scene.enterVR(); + } + + AFRAME.registerInputActions(inGameActions, "default"); + + const qs = queryString.parse(location.search); scene.setAttribute("networked-scene", { room: qs.room && !isNaN(parseInt(qs.room)) ? parseInt(qs.room) : 1, @@ -125,22 +161,8 @@ async function onSceneLoad() { playerRig.setAttribute("virtual-gamepad-controls", {}); } - let username; - const jwt = getCookie("jwt"); - if (jwt) { - //grab name from jwt - const data = parseJwt(jwt); - username = data.typ.name; - } - - if (qs.name) { - username = qs.name; //always override with name from querystring if available - } else { - username = promptForName(username); // promptForName is blocking - } - - const myNametag = document.querySelector("#player-rig .nametag"); - myNametag.setAttribute("text", "value", username); + setNameTagFromStore(); + store.addEventListener('statechanged', setNameTagFromStore); const avatarScale = parseInt(qs.avatarScale, 10); @@ -149,6 +171,8 @@ async function onSceneLoad() { } let sharingScreen = false; + + // TODO remove scene.addEventListener("action_share_screen", () => { sharingScreen = !sharingScreen; shareMedia(true, sharingScreen); @@ -161,20 +185,64 @@ async function onSceneLoad() { scene.components["networked-scene"].connect(); - await shareMedia(true, sharingScreen); + if (mediaStream) { + NAF.connection.adapter.setLocalMediaStream(mediaStream); + + const hasVideo = !!(mediaStream.getVideoTracks().length > 0); + + const id = `${NAF.clientId}-screen`; + let entity = document.getElementById(id); + if (entity) { + entity.setAttribute("visible", hasVideo); + } else if (hasVideo) { + const sceneEl = document.querySelector("a-scene"); + entity = document.createElement("a-entity"); + entity.id = id; + entity.setAttribute("offset-relative-to", { + target: "#head", + offset: "0 0 -2", + on: "action_share_screen" + }); + entity.setAttribute("networked", { template: "#video-template" }); + sceneEl.appendChild(entity); + } + } } } function onConnect() { - document.getElementById("loader").style.display = "none"; } -document.addEventListener("DOMContentLoaded", () => { - registerNetworkSchemas(); +function mountUI(scene) { + const qs = queryString.parse(location.search); + const disableAutoExitOnConcurrentLoad = qs.allow_multi === "true" + let forcedVREntryType = null; - const scene = document.querySelector("a-scene"); + if (qs.vr_entry_type) { + forcedVREntryType = qs.vr_entry_type; + } + const uiRoot = ReactDOM.render(<UIRoot {...{ + scene, + enterScene, + exitScene, + concurrentLoadDetector, + disableAutoExitOnConcurrentLoad, + forcedVREntryType, + store + }} />, document.getElementById("ui-root")); + + getAvailableVREntryTypes().then(availableVREntryTypes => { + uiRoot.setState({ availableVREntryTypes }); + uiRoot.handleForcedVREntryType(); + }); +} + +const onReady = () => { + const scene = document.querySelector("a-scene"); + document.querySelector("a-scene canvas").classList.add("blurred"); window.APP.scene = scene; + mountUI(scene); +}; - scene.addEventListener("loaded", onSceneLoad); -}); +document.addEventListener("DOMContentLoaded", onReady); diff --git a/src/storage/store.js b/src/storage/store.js new file mode 100644 index 0000000000000000000000000000000000000000..5b00b7ac8f5570c28651587997ae4c0546a3b722 --- /dev/null +++ b/src/storage/store.js @@ -0,0 +1,71 @@ +import uuid from "uuid/v4"; +import { Validator } from "jsonschema"; + +const LOCAL_STORE_KEY = "___mozilla_duck"; +const STORE_STATE_CACHE_KEY = Symbol(); +const validator = new Validator(); +import { EventTarget } from "event-target-shim" + +// Durable (via local-storage) schema-enforced state that is meant to be consumed via forward data flow. +// (Think flux but with way less incidental complexity, at least for now :)) +export const SCHEMA = { + id: "/MozillaDuckStore", + + definitions: { + profile: { + type: "object", + additionalProperties: false, + properties: { + display_name: { type: "string", pattern: "^[A-Za-z0-9-]{3,32}$" }, + } + } + }, + + type: "object", + + properties: { + id: { type: "string", pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" }, + profile: { "$ref": "#/definitions/profile" }, + }, + + additionalProperties: false +} + +export default class Store extends EventTarget { + constructor() { + super(); + + if (localStorage.getItem(LOCAL_STORE_KEY) === null) { + localStorage.setItem(LOCAL_STORE_KEY, JSON.stringify({ id: uuid() })); + } + } + + get state() { + if (!this.hasOwnProperty(STORE_STATE_CACHE_KEY)) { + this[STORE_STATE_CACHE_KEY] = JSON.parse(localStorage.getItem(LOCAL_STORE_KEY)); + } + + return this[STORE_STATE_CACHE_KEY]; + } + + update(newState) { + if (newState.id) { + throw "Store id is immutable."; + } + + const finalState = { ...this.state, ...newState }; + const isValid = validator.validate(finalState, SCHEMA).valid; + + if (!isValid) { + throw `Write of ${JSON.stringify(finalState)} to store failed schema validation.`; + return; + } + + localStorage.setItem(LOCAL_STORE_KEY, JSON.stringify(finalState)); + delete this[STORE_STATE_CACHE_KEY]; + + this.dispatchEvent(new CustomEvent("statechanged")); + + return finalState; + } +} diff --git a/src/utils/concurrent-load-detector.js b/src/utils/concurrent-load-detector.js new file mode 100644 index 0000000000000000000000000000000000000000..f05f619a3f51fab56a33acaec2eeb3b75e48ccea --- /dev/null +++ b/src/utils/concurrent-load-detector.js @@ -0,0 +1,44 @@ +// Detects if another instance of ConcurrentLoadDetector is start()'ed by in the same local storage +// context with the same instance key. Once a duplicate run is detected this will not fire any additional +// events. + +const LOCAL_STORE_KEY = "___concurrent_load_detector"; +import { EventTarget } from "event-target-shim" + +export default class ConcurrentLoadDetector extends EventTarget { + constructor(instanceKey) { + super(); + + this.interval = null; + this.startedAt = null; + this.instanceKey = instanceKey || "global"; + } + + start = () => { + this.startedAt = new Date(); + localStorage.setItem(this.localStorageKey(), JSON.stringify({ started_at: this.startedAt })); + + // Check for concurrent load every second + this.interval = setInterval(this._step, 1000); + } + + stop = () => { + if (this.interval) { + clearInterval(this.interval); + } + } + + localStorageKey = () => { + return `${LOCAL_STORE_KEY}_${this.instanceKey}`; + } + + _step = () => { + const currentState = JSON.parse(localStorage.getItem(this.localStorageKey())); + const maxStartedAt = new Date(currentState.started_at); + + if (maxStartedAt.getTime() !== this.startedAt.getTime()) { + this.dispatchEvent(new CustomEvent("concurrentload")); + this.stop(); + } + } +} diff --git a/src/utils/identity.js b/src/utils/identity.js index 54539286368182b31b49ae77a4e0d42898d78931..72cabdf008bc2574b4408d62c8f7f17f30fc9a59 100644 --- a/src/utils/identity.js +++ b/src/utils/identity.js @@ -161,32 +161,7 @@ const names = [ "yonath" ]; -export function generateName() { +export function generateDefaultProfile() { const name = names[Math.floor(Math.random() * names.length)]; - return name.replace(/^./, name[0].toUpperCase()); -} - -export function promptForName(username) { - if (!username) username = generateName(); - - do { - username = prompt("Choose a username", username); - } while (!(username && username.length)); - return username; -} - -export function getCookie(name) { - var value = "; " + document.cookie; - var parts = value.split("; " + name + "="); - if (parts.length == 2) - return parts - .pop() - .split(";") - .shift(); -} - -export function parseJwt(token) { - var base64Url = token.split(".")[1]; - var base64 = base64Url.replace("-", "+").replace("_", "/"); - return JSON.parse(window.atob(base64)); + return { display_name: name.replace(/^./, name[0].toUpperCase()) }; } diff --git a/src/utils/vr-caps-detect.js b/src/utils/vr-caps-detect.js new file mode 100644 index 0000000000000000000000000000000000000000..d0c96d8fbff5b277622a28cd55cb649fc4054efd --- /dev/null +++ b/src/utils/vr-caps-detect.js @@ -0,0 +1,86 @@ +const { detect } = require("detect-browser"); +const browser = detect(); + +// Precision on device detection is fuzzy -- we can sometimes know if a device is definitely +// available, or definitely *not* available, and assume it may be available otherwise. +export const VR_DEVICE_AVAILABILITY = { + yes: "yes", // Implies VR can be launched into on this device immediately, in this browser + no: "no", // Implies this VR device is definitely not supported regardless of browser + maybe: "maybe" // Implies this device may support this VR platform, but may not be installed or in a compatible browser +}; + +function isMaybeGearVRCompatibleDevice() { + return navigator.userAgent.match(/\WAndroid\W/); +} + +function isMaybeDaydreamCompatibleDevice() { + return navigator.userAgent.match(/\WAndroid\W/); +} + +// Blacklist of VR device name regex matchers that we do not want to consider as valid VR devices +// that can be entered into as a "generic" entry flow. +const GENERIC_ENTRY_TYPE_DEVICE_BLACKLIST = [/cardboard/i]; + +// Tries to determine VR entry compatibility regardless of the current browser. +// +// For each VR "entry type", returns VR_DEVICE_AVAILABILITY.yes if that type can be launched into directly from this browser +// on this device, returns VR_DEVICE_AVAILABILITY.no if that type is known to not be possible to ever use on this device no matter what, +// and VR_DEVICE_AVAILABILITY.maybe if the device potentially could support that type if a different browser was running, or if +// the software was setup, an HMD was purchased, etc. +// +// When "yes" or "maybe", we should present the option. If "maybe", when chosen try to get them into a compatible browser. +// Once in a compatible browser, we should assume it will work (if it doesn't, it's because they don't have the headset, +// haven't installed the software, our guess about their phone was wrong, etc.) +// +// At the time of this writing there are three VR "entry types" that will be validated by this method: +// +// - generic: Generic WebVR (platform/OS agnostic indicator if a general 'Enter VR' option should be presented.) +// - daydream: Google Daydream +// - gearvr: Oculus GearVR +// +export async function getAvailableVREntryTypes() { + const isWebVRCapableBrowser = !!navigator.getVRDisplays; + const isSamsungBrowser = browser.name === "chrome" && navigator.userAgent.match(/SamsungBrowser/); + const isDaydreamCapableBrowser = !!(isWebVRCapableBrowser && browser.name === "chrome" && !isSamsungBrowser); + + let generic = VR_DEVICE_AVAILABILITY.no; + let cardboard = VR_DEVICE_AVAILABILITY.no; + + // We only consider GearVR support as "maybe" and never "yes". The only browser + // that will detect GearVR outside of VR is Samsung Internet, and we'd prefer to launch into Oculus + // Browser for now since Samsung Internet requires an additional WebVR installation + flag, so return "maybe". + const gearvr = isMaybeGearVRCompatibleDevice() ? VR_DEVICE_AVAILABILITY.maybe : VR_DEVICE_AVAILABILITY.no; + + // For daydream detection, we first check if they are on an Android compatible device, and assume they + // may support daydream *unless* this browser has WebVR capabilities, in which case we can do better. + let daydream = isMaybeDaydreamCompatibleDevice() ? VR_DEVICE_AVAILABILITY.maybe : VR_DEVICE_AVAILABILITY.no; + + if (isWebVRCapableBrowser) { + const displays = await navigator.getVRDisplays(); + + // Generic is supported for non-blacklisted devices and presentable HMDs. + generic = displays.find( + d => d.capabilities.canPresent && !GENERIC_ENTRY_TYPE_DEVICE_BLACKLIST.find(r => d.displayName.match(r)) + ) + ? VR_DEVICE_AVAILABILITY.yes + : VR_DEVICE_AVAILABILITY.no; + + cardboard = displays.find(d => d.capabilities.canPresent && d.displayName.match(/\Wcardboard\W/i)) + ? VR_DEVICE_AVAILABILITY.yes + : VR_DEVICE_AVAILABILITY.no; + + // For daydream detection, in a WebVR browser we can increase confidence in daydream compatibility. + const hasDaydreamWebVRDevice = displays.find(d => d.displayName.match(/\Wdaydream\W/i)); + + if (hasDaydreamWebVRDevice) { + // If we detected daydream via WebVR + daydream = VR_DEVICE_AVAILABILITY.yes; + generic = VR_DEVICE_AVAILABILITY.no; + } else if (isDaydreamCapableBrowser) { + // If we didn't detect daydream in a daydream capable browser, we definitely can't run daydream at all. + daydream = VR_DEVICE_AVAILABILITY.no; + } + } + + return { generic, gearvr, daydream, cardboard }; +} diff --git a/webpack.config.js b/webpack.config.js index dea5786e6a71e1a1abc6beb07431f28094096bbc..4996b8925731f3e5912b954dafa5c6b1dc2a66c2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -122,7 +122,8 @@ const config = { "a-asset-item:src", "a-progressive-asset:src", "a-progressive-asset:high-src", - "a-progressive-asset:low-src" + "a-progressive-asset:low-src", + "audio:src" ], // You can get transformed asset urls in an html template using ${require("pathToFile.ext")} interpolate: "require" @@ -133,7 +134,26 @@ const config = { include: [path.resolve(__dirname, "src")], // Exclude JS assets in node_modules because they are already transformed and often big. exclude: [path.resolve(__dirname, "node_modules")], - loader: "babel-loader" + loader: "babel-loader", + query: { + plugins: ["transform-class-properties", "transform-object-rest-spread"] + } + }, + { + test: /\.scss$/, + loader: ExtractTextPlugin.extract({ + fallback: "style-loader", + use: [ + { + loader: "css-loader", + options: { + name: "[path][name]-[hash].[ext]", + minimize: process.env.NODE_ENV === "production" + } + }, + "sass-loader" + ] + }) }, { test: /\.css$/, @@ -142,13 +162,14 @@ const config = { use: { loader: "css-loader", options: { + name: "[path][name]-[hash].[ext]", minimize: process.env.NODE_ENV === "production" } } }) }, { - test: /\.(png|jpg|gif|glb)$/, + test: /\.(png|jpg|gif|glb|ogg|woff2|svg)$/, use: { loader: "file-loader", options: { diff --git a/yarn.lock b/yarn.lock index 8fb5a456961d97ae9975a5df0ad769a7b415e695..7a0884a0ae0198f5d8f079ca59c2141f478966b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,75 @@ # yarn lockfile v1 +"@babel/code-frame@7.0.0-beta.40", "@babel/code-frame@^7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz#37e2b0cf7c56026b4b21d3927cadf81adec32ac6" + dependencies: + "@babel/highlight" "7.0.0-beta.40" + +"@babel/generator@7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.40.tgz#ab61f9556f4f71dbd1138949c795bb9a21e302ea" + dependencies: + "@babel/types" "7.0.0-beta.40" + jsesc "^2.5.1" + lodash "^4.2.0" + source-map "^0.5.0" + trim-right "^1.0.1" + +"@babel/helper-function-name@7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.40.tgz#9d033341ab16517f40d43a73f2d81fc431ccd7b6" + dependencies: + "@babel/helper-get-function-arity" "7.0.0-beta.40" + "@babel/template" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.40" + +"@babel/helper-get-function-arity@7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.40.tgz#ac0419cf067b0ec16453e1274f03878195791c6e" + dependencies: + "@babel/types" "7.0.0-beta.40" + +"@babel/highlight@7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.40.tgz#b43d67d76bf46e1d10d227f68cddcd263786b255" + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +"@babel/template@7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.40.tgz#034988c6424eb5c3268fe6a608626de1f4410fc8" + dependencies: + "@babel/code-frame" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.40" + babylon "7.0.0-beta.40" + lodash "^4.2.0" + +"@babel/traverse@^7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.40.tgz#d140e449b2e093ef9fe1a2eecc28421ffb4e521e" + dependencies: + "@babel/code-frame" "7.0.0-beta.40" + "@babel/generator" "7.0.0-beta.40" + "@babel/helper-function-name" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.40" + babylon "7.0.0-beta.40" + debug "^3.0.1" + globals "^11.1.0" + invariant "^2.2.0" + lodash "^4.2.0" + +"@babel/types@7.0.0-beta.40", "@babel/types@^7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.40.tgz#25c3d7aae14126abe05fcb098c65a66b6d6b8c14" + dependencies: + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^2.0.0" + JSONStream@^1.0.3: version "1.3.2" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" @@ -54,14 +123,10 @@ acorn@^4.0.3: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" -acorn@^5.0.0, acorn@^5.4.0: +acorn@^5.0.0, acorn@^5.2.1, acorn@^5.4.0, acorn@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" -acorn@^5.2.1, acorn@^5.4.1: - version "5.5.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" - aframe-billboard-component@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/aframe-billboard-component/-/aframe-billboard-component-1.0.0.tgz#10ce2482729eef7386c5844d65917581a62d3adc" @@ -152,6 +217,10 @@ alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" @@ -368,6 +437,10 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + async@0.2.x: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" @@ -441,6 +514,17 @@ babel-core@^6.26.0: slash "^1.0.0" source-map "^0.5.6" +babel-eslint@^8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.2.tgz#1102273354c6f0b29b4ea28a65f97d122296b68b" + dependencies: + "@babel/code-frame" "^7.0.0-beta.40" + "@babel/traverse" "^7.0.0-beta.40" + "@babel/types" "^7.0.0-beta.40" + babylon "^7.0.0-beta.40" + eslint-scope "~3.7.1" + eslint-visitor-keys "^1.0.0" + babel-generator@^6.26.0: version "6.26.1" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" @@ -600,6 +684,14 @@ babel-plugin-check-es2015-constants@^6.22.0: dependencies: babel-runtime "^6.22.0" +babel-plugin-react-intl@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/babel-plugin-react-intl/-/babel-plugin-react-intl-2.4.0.tgz#292fca8030603a9e0476973290836aa0c7da17e2" + dependencies: + babel-runtime "^6.2.0" + intl-messageformat-parser "^1.2.0" + mkdirp "^0.5.1" + babel-plugin-syntax-async-functions@^6.8.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" @@ -894,6 +986,13 @@ babel-plugin-transform-react-display-name@^6.23.0: dependencies: babel-runtime "^6.22.0" +babel-plugin-transform-react-jsx-img-import@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-img-import/-/babel-plugin-transform-react-jsx-img-import-0.1.4.tgz#589b98d462299e235577f2d5371b439453196eac" + dependencies: + babel-types "^6.10.0" + lodash "^4.13.1" + babel-plugin-transform-react-jsx-self@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e" @@ -1049,7 +1148,7 @@ babel-register@^6.26.0, babel-register@^6.9.0: mkdirp "^0.5.1" source-map-support "^0.4.15" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@^6.18.0, babel-runtime@^6.2.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: @@ -1080,7 +1179,7 @@ babel-traverse@^6.24.1, babel-traverse@^6.26.0: invariant "^2.2.2" lodash "^4.17.4" -babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: +babel-types@^6.10.0, babel-types@^6.19.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: @@ -1089,6 +1188,10 @@ babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: lodash "^4.17.4" to-fast-properties "^1.0.3" +babylon@7.0.0-beta.40, babylon@^7.0.0-beta.40: + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.40.tgz#91fc8cd56d5eb98b28e6fde41045f2957779940a" + babylon@^6.17.3, babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" @@ -1581,6 +1684,10 @@ camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -1606,6 +1713,10 @@ caniuse-lite@^1.0.30000792: version "0.6.2" resolved "https://codeload.github.com/donmccurdy/cannon.js/tar.gz/022e8ba53fa83abf0ad8a0e4fd08623123838a17" +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -1721,6 +1832,10 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" + clean-css@4.1.x: version "4.1.9" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.9.tgz#35cee8ae7687a49b98034f70de00c4edd3826301" @@ -1772,6 +1887,15 @@ clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" +clone-deep@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713" + dependencies: + for-own "^1.0.0" + is-plain-object "^2.0.4" + kind-of "^6.0.0" + shallow-clone "^1.0.0" + clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" @@ -1889,7 +2013,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.14.x, commander@~2.14.1: +commander@2.14.x, commander@^2.9.0, commander@~2.14.1: version "2.14.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" @@ -1935,7 +2059,7 @@ 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.4.7, concat-stream@^1.5.0, concat-stream@^1.6.0: +concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: @@ -1951,14 +2075,6 @@ concat-stream@~1.5.0, concat-stream@~1.5.1: readable-stream "~2.0.0" typedarray "~0.0.5" -concat-stream@~1.6.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26" - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - connect-history-api-fallback@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a" @@ -2066,6 +2182,13 @@ cross-env@^5.1.3: cross-spawn "^5.1.0" is-windows "^1.0.0" +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -2246,7 +2369,7 @@ debug@2.6.9, debug@^2.0.0, debug@^2.1.0, debug@^2.2.0, debug@^2.3.3, debug@^2.6. dependencies: ms "2.0.0" -debug@^3.1.0: +debug@^3.0.1, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -2367,6 +2490,10 @@ destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" +detect-browser@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-2.1.0.tgz#df35462901dfd92b8f37c2fa457d6e1f57b5e8eb" + detect-conflict@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/detect-conflict/-/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e" @@ -2433,7 +2560,7 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" -doctrine@^2.1.0: +doctrine@^2.0.2, doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: @@ -2705,7 +2832,16 @@ eslint-plugin-prettier@^2.3.1: fast-diff "^1.1.1" jest-docblock "^21.0.0" -eslint-scope@^3.7.1: +eslint-plugin-react@^7.7.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz#f606c719dbd8a1a2b3d25c16299813878cca0160" + dependencies: + doctrine "^2.0.2" + has "^1.0.1" + jsx-ast-utils "^2.0.1" + prop-types "^15.6.0" + +eslint-scope@^3.7.1, eslint-scope@~3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" dependencies: @@ -2801,6 +2937,10 @@ etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" +event-target-shim@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-3.0.1.tgz#a4a62f0795e5b65363e86c6780413224d1eea688" + eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" @@ -3129,6 +3269,10 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" +for-in@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" + for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -3139,6 +3283,12 @@ for-own@^0.1.4: dependencies: for-in "^1.0.1" +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + dependencies: + for-in "^1.0.1" + foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" @@ -3255,6 +3405,22 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +gaze@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" + dependencies: + globule "^1.0.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" @@ -3327,7 +3493,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@^6.0.1: +glob@^6.0.1, glob@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" dependencies: @@ -3337,7 +3503,7 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.0, glob@^7.1.2: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.0, glob@^7.1.2, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -3373,7 +3539,7 @@ global@^4.3.2: min-document "^2.19.0" process "~0.5.1" -globals@^11.0.1: +globals@^11.0.1, globals@^11.1.0: version "11.3.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.3.0.tgz#e04fdb7b9796d8adac9c8f64c14837b2313378b0" @@ -3413,6 +3579,14 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +globule@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.0.tgz#1dc49c6822dd9e8a2fa00ba2a295006e8664bd09" + dependencies: + glob "~7.1.1" + lodash "~4.17.4" + minimatch "~3.0.2" + got@^7.0.0, got@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" @@ -3450,6 +3624,15 @@ har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + har-validator@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" @@ -3765,6 +3948,10 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" @@ -3879,7 +4066,27 @@ interpret@^1.0.0, interpret@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -invariant@^2.2.1, invariant@^2.2.2: +intl-format-cache@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.1.0.tgz#04a369fecbfad6da6005bae1f14333332dcf9316" + +intl-messageformat-parser@1.4.0, intl-messageformat-parser@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz#b43d45a97468cadbe44331d74bb1e8dea44fc075" + +intl-messageformat@^2.0.0, intl-messageformat@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-2.2.0.tgz#345bcd46de630b7683330c2e52177ff5eab484fc" + dependencies: + intl-messageformat-parser "1.4.0" + +intl-relativeformat@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#010f1105802251f40ac47d0e3e1a201348a255df" + dependencies: + intl-messageformat "^2.0.0" + +invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688" dependencies: @@ -4031,6 +4238,20 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + +is-my-json-valid@^2.12.4: + version "2.17.2" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" @@ -4095,6 +4316,10 @@ is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + is-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" @@ -4197,7 +4422,7 @@ jest-docblock@^21.0.0: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" -js-base64@^2.1.9: +js-base64@^2.1.8, js-base64@^2.1.9: version "2.4.3" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582" @@ -4247,6 +4472,10 @@ jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" +jsesc@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe" + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" @@ -4295,6 +4524,14 @@ jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsonschema@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.2.tgz#83ab9c63d65bf4d596f91d81195e78772f6452bc" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -4304,6 +4541,12 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jsx-ast-utils@^2.0.1: + 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" + killable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b" @@ -4440,7 +4683,7 @@ loader-utils@^0.2.16: json5 "^0.5.0" object-assign "^4.0.1" -loader-utils@^1.0.2, loader-utils@^1.1.0: +loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" dependencies: @@ -4455,10 +4698,18 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" +lodash.clonedeep@^4.3.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + lodash.endswith@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09" @@ -4479,15 +4730,23 @@ lodash.memoize@~3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" +lodash.mergewith@^4.6.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" + lodash.startswith@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c" +lodash.tail@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: +lodash@^4.0.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.4: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" @@ -4639,7 +4898,7 @@ memory-fs@^0.4.0, memory-fs@~0.4.1: errno "^0.1.3" readable-stream "^2.0.1" -meow@^3.3.0: +meow@^3.3.0, meow@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" dependencies: @@ -4737,9 +4996,13 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" -minijanus@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/minijanus/-/minijanus-0.4.0.tgz#4d08529da795886b1aab6714ee7c9ff122c8c802" +minijanus@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/minijanus/-/minijanus-0.5.0.tgz#78e1429bb5d83cb3957a538335d2ae901bf614fa" + +"minijanus@https://github.com/mozilla/minijanus.js#master": + version "0.5.0" + resolved "https://github.com/mozilla/minijanus.js#497f4dd80fdb92e247238e638daed14ae6623575" minimalistic-assert@^1.0.0: version "1.0.0" @@ -4749,7 +5012,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -4789,12 +5052,23 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mixin-object@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" + dependencies: + for-in "^0.1.3" + is-extendable "^0.1.1" + mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, 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" +mobile-detect@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mobile-detect/-/mobile-detect-1.4.1.tgz#f4b67c49bb84bf0437f72e3067deb1c60ad7b23c" + module-deps@^4.0.8: version "4.1.1" resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd" @@ -4846,6 +5120,10 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +moving-average@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/moving-average/-/moving-average-1.0.0.tgz#b1247ba8dd2d7927c619f1eac8036cf933d65adc" + ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -4886,14 +5164,14 @@ mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" -naf-janus-adapter@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/naf-janus-adapter/-/naf-janus-adapter-0.4.0.tgz#22f14212a14d9e3d30c8d9441978704ff58392f4" +"naf-janus-adapter@https://github.com/mozilla/naf-janus-adapter#feature/disconnect": + version "0.4.1" + resolved "https://github.com/mozilla/naf-janus-adapter#4a4532014d6489403cf7e451790925ce747f8e41" dependencies: debug "^3.1.0" - minijanus "^0.4.0" + minijanus "https://github.com/mozilla/minijanus.js#master" -nan@^2.3.0: +nan@^2.3.0, nan@^2.3.2: version "2.9.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.1.tgz#af88fcaee5292992c5b755121ceeaa74536fc228" @@ -4968,6 +5246,24 @@ node-forge@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300" +node-gyp@^3.3.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + minimatch "^3.0.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "2" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + node-libs-browser@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" @@ -5012,6 +5308,30 @@ node-pre-gyp@^0.6.39: tar "^2.2.1" tar-pack "^3.4.0" +node-sass@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e" + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash.assign "^4.2.0" + lodash.clonedeep "^4.3.2" + lodash.mergewith "^4.6.0" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.3.2" + node-gyp "^3.3.1" + npmlog "^4.0.0" + request "~2.79.0" + sass-graph "^2.2.4" + stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + nomnom@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" @@ -5019,6 +5339,12 @@ nomnom@^1.8.1: chalk "~0.4.0" underscore "~1.6.0" +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -5060,7 +5386,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.0.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: @@ -5216,6 +5542,12 @@ os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + os-locale@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" @@ -5232,7 +5564,7 @@ os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -osenv@^0.1.4: +osenv@0, osenv@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" dependencies: @@ -5900,6 +6232,10 @@ qs@6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" +qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" + qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" @@ -5990,6 +6326,15 @@ react-dom@^16.1.1: object-assign "^4.1.1" prop-types "^15.6.0" +react-intl@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.4.0.tgz#66c14dc9df9a73b2fbbfbd6021726e80a613eb15" + dependencies: + intl-format-cache "^2.0.5" + intl-messageformat "^2.1.0" + intl-relativeformat "^2.0.0" + invariant "^2.1.1" + react-router-dom@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d" @@ -6262,6 +6607,31 @@ replace-ext@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" +request@2, request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + request@2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" @@ -6441,6 +6811,25 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +sass-graph@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^7.0.0" + +sass-loader@^6.0.7: + version "6.0.7" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.7.tgz#dd2fdb3e7eeff4a53f35ba6ac408715488353d00" + dependencies: + clone-deep "^2.0.1" + loader-utils "^1.0.1" + lodash.tail "^4.1.1" + neo-async "^2.5.0" + pify "^3.0.0" + sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -6456,6 +6845,13 @@ scoped-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8" +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + dependencies: + js-base64 "^2.1.8" + source-map "^0.4.2" + sdp@^2.3.0, sdp@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.6.0.tgz#b5260be98fa55289e925a7badcb46655d868215b" @@ -6474,6 +6870,10 @@ selfsigned@^1.10.2, selfsigned@^1.9.1: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + send@0.16.1: version "0.16.1" resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" @@ -6595,6 +6995,14 @@ sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: inherits "^2.0.1" safe-buffer "^5.0.1" +shallow-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571" + dependencies: + is-extendable "^0.1.1" + kind-of "^5.0.0" + mixin-object "^2.0.1" + shasum@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" @@ -6782,10 +7190,16 @@ source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" -source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.3: +source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -6881,14 +7295,14 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.3.1 < 2", statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - -statuses@~1.3.1: +"statuses@>= 1.3.1 < 2", statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + stdout-stream@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" @@ -7132,7 +7546,7 @@ tar-pack@^3.4.0: tar "^2.2.1" uid-number "^0.0.6" -tar@^2.2.1: +tar@^2.0.0, tar@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" dependencies: @@ -7232,6 +7646,10 @@ 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" +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" @@ -7271,11 +7689,17 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -tty-browserify@0.0.0: +"true-case-path@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + dependencies: + glob "^6.0.4" + +tty-browserify@0.0.0, tty-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" -tty-browserify@0.0.1, tty-browserify@~0.0.0: +tty-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" @@ -7285,6 +7709,10 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -7503,7 +7931,7 @@ utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" -uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: +uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" @@ -7801,11 +8229,15 @@ whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@^1.2.14, which@^1.2.4, which@^1.2.9: +which@1, which@^1.2.14, which@^1.2.4, which@^1.2.9: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: @@ -7888,6 +8320,12 @@ yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + dependencies: + camelcase "^3.0.0" + yargs-parser@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" @@ -7912,6 +8350,24 @@ yargs@9.0.1: y18n "^3.2.1" yargs-parser "^7.0.0" +yargs@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0" + yargs@~1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b"