diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000000000000000000000000000000000..70fa045e816539292b653dad3df26c6f68b99be0 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +!.eslintrc.js +src/vendor/* diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..1cadfd103fd1910f79fb0e7928b0663b9e6a248a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,23 @@ +module.exports = { + parser: "babel-eslint", + env: { + browser: true, + es6: true, + node: true + }, + globals: { + THREE: true, + AFRAME: true, + NAF: true + }, + plugins: ["prettier", "react"], + rules: { + "prettier/prettier": "error", + "prefer-const": "error", + "no-var": "error", + "no-throw-literal": "error", + // Light console usage is useful but remove debug logs before merging to master. + "no-console": "off" + }, + extends: ["prettier", "plugin:react/recommended", "eslint:recommended"] +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 8ab63d8cc0f6d8854b53e96c03a7889080ecb650..0000000000000000000000000000000000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parser": "babel-eslint", - "plugins": [ - "prettier", - "react" - ], - "rules": { - "prettier/prettier": "error", - "prefer-const": "error", - "no-var": "error" - }, - "extends": [ - "prettier", - "plugin:react/recommended" - ] -} diff --git a/.prettierignore b/.prettierignore index edded1b3e9cda6cef1bf33b9c8e08aa12f1f84a7..4bd6eed9e8bcf23ca49b895c57f0ba6d6290e14b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ package.json *.gltf +src/vendor/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..73a40d0761a7b442cd2435b7ac37a59f5c13a875 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: node +cache: yarn +before_install: + - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.5.1 + - export PATH="$HOME/.yarn/bin:$PATH" +install: yarn +script: yarn lint diff --git a/package.json b/package.json index 5baab2abb42b937e578c0b379d96fcc81f9f9e39..93154d2d912d0d40d3ff8a067326064c206d46b5 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "postinstall": "node ./scripts/postinstall.js", "start": "cross-env NODE_ENV=development webpack-dev-server", "build": "rimraf ./public && cross-env NODE_ENV=production webpack --mode=production", - "prettier": "prettier --write src/**/*.js" + "prettier": "prettier --write '*.js' 'src/**/*.js'", + "lint": "eslint '*.js' 'src/**/*.js'" }, "dependencies": { "@fortawesome/fontawesome": "^1.1.5", @@ -63,7 +64,7 @@ "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", + "html-webpack-plugin": "^3.1.0", "lodash": "^4.17.5", "node-sass": "^4.7.2", "prettier": "^1.7.0", diff --git a/src/activators/pressedmove.js b/src/activators/pressedmove.js index c035383186e0997908be2eae5868a1a97e546dd4..fb7d7470723246b687049eafeb83d51e84f0e8bd 100644 --- a/src/activators/pressedmove.js +++ b/src/activators/pressedmove.js @@ -18,10 +18,10 @@ PressedMove.prototype = { this.onActivate(event); } }, - onButtonDown: function(event) { + onButtonDown: function() { this.pressed = true; }, - onButtonUp: function(event) { + onButtonUp: function() { this.pressed = false; }, diff --git a/src/activators/shortpress.js b/src/activators/shortpress.js index 7f47450bef78161554a89569d4a610d7f7691877..fd9b8da0a212dc6acfba55509072d9c46c079305 100644 --- a/src/activators/shortpress.js +++ b/src/activators/shortpress.js @@ -16,13 +16,12 @@ function ShortPress(el, button, onActivate) { ShortPress.prototype = { onButtonDown(event) { - var self = this; - this.pressTimer = window.setTimeout(function() { - self.onActivate(event); + this.pressTimer = window.setTimeout(() => { + this.onActivate(event); }, this.timeOut); }, - onButtonUp(event) { + onButtonUp() { clearTimeout(this.pressTimer); }, diff --git a/src/assets/avatars/avatars.js b/src/assets/avatars/avatars.js index f7a5500048710c5bf0910e5bc781000764871312..7934ecb66c1439b04e892367dc3655d1f3826abe 100644 --- a/src/assets/avatars/avatars.js +++ b/src/assets/avatars/avatars.js @@ -1,65 +1,65 @@ export const avatars = [ { - "id": "botdefault", - "models": { - "low": `${ require("./BotDefault_Avatar_Unlit.glb") }`, - "high": `${ require("./BotDefault_Avatar.glb") }` + id: "botdefault", + models: { + low: `${require("./BotDefault_Avatar_Unlit.glb")}`, + high: `${require("./BotDefault_Avatar.glb")}` } }, { - "id": "botbobo", - "models": { - "low": `${ require("./BotBobo_Avatar_Unlit.glb") }`, - "high": `${ require("./BotBobo_Avatar.glb") }` + id: "botbobo", + models: { + low: `${require("./BotBobo_Avatar_Unlit.glb")}`, + high: `${require("./BotBobo_Avatar.glb")}` } }, { - "id": "botdom", - "models": { - "low": `${ require("./BotDom_Avatar_Unlit.glb") }`, - "high": `${ require("./BotDom_Avatar.glb") }` + id: "botdom", + models: { + low: `${require("./BotDom_Avatar_Unlit.glb")}`, + high: `${require("./BotDom_Avatar.glb")}` } }, { - "id": "botgreg", - "models": { - "low": `${ require("./BotGreg_Avatar_Unlit.glb") }`, - "high": `${ require("./BotGreg_Avatar.glb") }` + id: "botgreg", + models: { + low: `${require("./BotGreg_Avatar_Unlit.glb")}`, + high: `${require("./BotGreg_Avatar.glb")}` } }, { - "id": "botguest", - "models": { - "low": `${ require("./BotGuest_Avatar_Unlit.glb") }`, - "high": `${ require("./BotGuest_Avatar.glb") }` + id: "botguest", + models: { + low: `${require("./BotGuest_Avatar_Unlit.glb")}`, + high: `${require("./BotGuest_Avatar.glb")}` } }, { - "id": "botjim", - "models": { - "low": `${ require("./BotJim_Avatar_Unlit.glb") }`, - "high": `${ require("./BotJim_Avatar.glb") }` + id: "botjim", + models: { + low: `${require("./BotJim_Avatar_Unlit.glb")}`, + high: `${require("./BotJim_Avatar.glb")}` } }, { - "id": "botpinky", - "models": { - "low": `${ require("./BotPinky_Avatar_Unlit.glb") }`, - "high": `${ require("./BotPinky_Avatar.glb") }` + id: "botpinky", + models: { + low: `${require("./BotPinky_Avatar_Unlit.glb")}`, + high: `${require("./BotPinky_Avatar.glb")}` } }, { - "id": "botrobert", - "models": { - "low": `${ require("./BotRobert_Avatar_Unlit.glb") }`, - "high": `${ require("./BotRobert_Avatar.glb") }` + id: "botrobert", + models: { + low: `${require("./BotRobert_Avatar_Unlit.glb")}`, + high: `${require("./BotRobert_Avatar.glb")}` } }, { - "id": "botwoody", - "models": { - "low": `${ require("./BotWoody_Avatar_Unlit.glb") }`, - "high": `${ require("./BotWoody_Avatar.glb") }` + id: "botwoody", + models: { + low: `${require("./BotWoody_Avatar_Unlit.glb")}`, + high: `${require("./BotWoody_Avatar.glb")}` } } ]; diff --git a/src/react-components/2d-hud.css b/src/assets/stylesheets/2d-hud.css similarity index 79% rename from src/react-components/2d-hud.css rename to src/assets/stylesheets/2d-hud.css index 556431ac6ca824e0bbcf4cf4d5644e05be4dc309..a8324a5bb8b054c4da44f82aadfe96647c5cb45b 100644 --- a/src/react-components/2d-hud.css +++ b/src/assets/stylesheets/2d-hud.css @@ -30,14 +30,16 @@ width: 48px; height: 48px; background-size: 100%; - background-image: url(../assets/hud/avatar.jpg); + background-image: url(../hud/avatar.jpg); } :local(.mic) { display: flex; width: 48px; height: 48px; - mask: url(../assets/hud/unmuted.png); + -webkit-mask: url(../hud/unmuted.png); + -webkit-mask-size: 48px; + mask: url(../hud/unmuted.png); mask-size: 48px; background-color: white; cursor: pointer; @@ -56,7 +58,9 @@ } :local(.mic.muted) { - mask: url(../assets/hud/muted.png); + -webkit-mask: url(../hud/muted.png); + -webkit-mask-size: 48px; + mask: url(../hud/muted.png); mask-size: 48px; } diff --git a/src/avatar-selector.js b/src/avatar-selector.js index 6acfe154caebb1c9f799631940aeb918e945fd7e..ea3ff71a0f1f225cb9ae9f68a813167125bc3207 100644 --- a/src/avatar-selector.js +++ b/src/avatar-selector.js @@ -1,7 +1,7 @@ import ReactDOM from "react-dom"; import React from "react"; import queryString from "query-string"; -import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl"; +import { IntlProvider, addLocaleData } from "react-intl"; import en from "react-intl/locale-data/en"; import "./assets/stylesheets/avatar-selector.scss"; @@ -12,13 +12,15 @@ import "./components/audio-feedback"; import "./components/loop-animation"; import "./elements/a-progressive-asset"; import "./gltf-component-mappings"; -import { avatars } from "./assets/avatars/avatars.js"; -import { avatarIds } from "./utils/identity"; +import { avatars } from "./assets/avatars/avatars"; +import registerTelemetry from "./telemetry"; import { App } from "./App"; import AvatarSelector from "./react-components/avatar-selector"; import localeData from "./assets/translations.data.json"; +registerTelemetry(); + window.APP = new App(); const hash = queryString.parse(location.hash); const isMobile = AFRAME.utils.device.isMobile(); diff --git a/src/behaviours/oculus-touch-joystick-dpad4.js b/src/behaviours/oculus-touch-joystick-dpad4.js index 758fb603c70faceb932539d722e08976e603046d..bf397ba2fbda2357c7fdf1cfc0310c8aa1ba548c 100644 --- a/src/behaviours/oculus-touch-joystick-dpad4.js +++ b/src/behaviours/oculus-touch-joystick-dpad4.js @@ -1,4 +1,4 @@ -import { angleTo4Direction, angleTo8Direction } from "../utils/dpad"; +import { angleTo4Direction } from "../utils/dpad"; // @TODO specify 4 or 8 direction function oculus_touch_joystick_dpad4(el, outputPrefix) { @@ -15,11 +15,8 @@ oculus_touch_joystick_dpad4.prototype = { emitDPad4: function(event) { const x = event.detail.axis[0]; const y = event.detail.axis[1]; - const inCenter = - Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; - const current = inCenter - ? "center" - : this.angleToDirection(Math.atan2(x, -y)); + const inCenter = Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; + const current = inCenter ? "center" : this.angleToDirection(Math.atan2(x, -y)); if (current !== this.previous) { this.previous = current; event.target.emit(`${this.outputPrefix}_dpad4_${current}`); diff --git a/src/behaviours/vive-trackpad-dpad4.js b/src/behaviours/vive-trackpad-dpad4.js index 3f4e3a275a4c6bad6a74aa6ef3c8836bb3a26500..99bdf8873d4cbcce3541691e6073af71c153db2b 100644 --- a/src/behaviours/vive-trackpad-dpad4.js +++ b/src/behaviours/vive-trackpad-dpad4.js @@ -1,4 +1,4 @@ -import { angleTo4Direction, angleTo8Direction } from "../utils/dpad"; +import { angleTo4Direction } from "../utils/dpad"; function vive_trackpad_dpad4(el, outputPrefix) { this.outputPrefix = outputPrefix; @@ -16,20 +16,17 @@ function vive_trackpad_dpad4(el, outputPrefix) { } vive_trackpad_dpad4.prototype = { - press: function(_) { + press: function() { this.pressed = true; }, - unpress: function(_) { + unpress: function() { this.pressed = false; }, emitDPad4: function(event) { const x = event.detail.axis[0]; const y = event.detail.axis[1]; - const inCenter = - Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; - const direction = inCenter - ? "center" - : angleTo4Direction(Math.atan2(x, -y)); + const inCenter = Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; + const direction = inCenter ? "center" : angleTo4Direction(Math.atan2(x, -y)); const pressed = this.pressed ? "pressed_" : ""; const current = `${pressed + direction}`; // e.g. "pressed_north" diff --git a/src/components/bone-mute-state-indicator.js b/src/components/bone-mute-state-indicator.js index 7c6dc5913e971a3a3981f2d8af7ed3e151dcf0e8..2d79b3f9c37feee028b89afbe6eaffe7bbd654d8 100644 --- a/src/components/bone-mute-state-indicator.js +++ b/src/components/bone-mute-state-indicator.js @@ -12,12 +12,8 @@ AFRAME.registerComponent("bone-mute-state-indicator", { init() { this.onStateToggled = this.onStateToggled.bind(this); this.el.addEventListener("model-loaded", () => { - this.unmutedBone = this.el.object3D.getObjectByName( - this.data.unmutedBoneName - ); - this.mutedBone = this.el.object3D.getObjectByName( - this.data.mutedBoneName - ); + this.unmutedBone = this.el.object3D.getObjectByName(this.data.unmutedBoneName); + this.mutedBone = this.el.object3D.getObjectByName(this.data.mutedBoneName); console.log(this.unmutedBone, this.mutedBone); this.modelLoaded = true; diff --git a/src/components/character-controller.js b/src/components/character-controller.js index 20553d36ce9c06ca84806fd5b6adc7350ddf2aaa..1249dff3b0fbb3231bb52bd373e6cd91b7f3a544 100644 --- a/src/components/character-controller.js +++ b/src/components/character-controller.js @@ -60,11 +60,11 @@ AFRAME.registerComponent("character-controller", { this.angularVelocity = event.detail.value; }, - snapRotateLeft: function(event) { + snapRotateLeft: function() { this.pendingSnapRotationMatrix.copy(this.leftRotationMatrix); }, - snapRotateRight: function(event) { + snapRotateRight: function() { this.pendingSnapRotationMatrix.copy(this.rightRotationMatrix); }, @@ -79,9 +79,6 @@ AFRAME.registerComponent("character-controller", { const rotationInvMatrix = new THREE.Matrix4(); const pivotRotationMatrix = new THREE.Matrix4(); const pivotRotationInvMatrix = new THREE.Matrix4(); - const position = new THREE.Vector3(); - const currentPosition = new THREE.Vector3(); - const movementVector = new THREE.Vector3(); return function(t, dt) { const deltaSeconds = dt / 1000; diff --git a/src/components/debug.js b/src/components/debug.js index 77202f03295be677b9156e1ff16f11100ab19351..6448bf236966876e05069f96192153cf0f8400b6 100644 --- a/src/components/debug.js +++ b/src/components/debug.js @@ -1,25 +1,30 @@ AFRAME.registerComponent("lifecycle-checker", { schema: { + name: { type: "string" }, tick: { default: false } }, init: function() { - console.log("init", this.el); + this.log("init"); }, update: function() { - console.log("update", this.el); + this.log("update"); }, tick: function() { if (this.data.tick) { - console.log("tick", this.el); + this.log("tick"); } }, remove: function() { - console.log("remove", this.el); + this.log("remove"); }, pause: function() { - console.log("pause", this.el); + this.log("pause"); }, play: function() { - console.log("play", this.el); + this.log("play"); + }, + + log: function(method) { + console.info(`lifecycle-checker:${this.data.name} ${method}`); } }); diff --git a/src/components/event-repeater.js b/src/components/event-repeater.js index f099038b48ca692f1e7443e650b59cdfaeb7481e..64e28720698d8aaa26a8f0ce1525301cebd1ee2f 100644 --- a/src/components/event-repeater.js +++ b/src/components/event-repeater.js @@ -26,5 +26,4 @@ AFRAME.registerComponent("event-repeater", { _handleEvent: function(event, e) { this.el.emit(event, e.details); } - }); diff --git a/src/components/gltf-bundle.js b/src/components/gltf-bundle.js index 791fa7eef9076b44eda996192181dc488e755846..85238043fb2319dd1a6e55bc953186890fd2e721 100644 --- a/src/components/gltf-bundle.js +++ b/src/components/gltf-bundle.js @@ -17,8 +17,8 @@ AFRAME.registerComponent("gltf-bundle", { for (let i = 0; i < bundleJson.assets.length; i++) { const asset = bundleJson.assets[i]; const src = asset.src; - const gltfEl = document.createElement("a-gltf-entity"); - gltfEl.setAttribute("src", src); + const gltfEl = document.createElement("a-entity"); + gltfEl.setAttribute("gltf-model-plus", { src }); gltfEl.setAttribute("position", "0 0 0"); loaded.push(new Promise(resolve => gltfEl.addEventListener("model-loaded", resolve))); this.el.appendChild(gltfEl); diff --git a/src/elements/a-gltf-entity.js b/src/components/gltf-model-plus.js similarity index 56% rename from src/elements/a-gltf-entity.js rename to src/components/gltf-model-plus.js index 33489e2bc1792aa2b9dd4bddca360caaa39c509b..f1993414fc6ec0029e5b918d452c7a009ab6c3fd 100644 --- a/src/elements/a-gltf-entity.js +++ b/src/components/gltf-model-plus.js @@ -1,6 +1,6 @@ const GLTFCache = {}; -AFRAME.AGLTFEntity = { +AFRAME.GLTFModelPlus = { defaultInflator(el, componentName, componentData) { if (!AFRAME.components[componentName]) { throw new Error(`Inflator failed. "${componentName}" component does not exist.`); @@ -14,8 +14,8 @@ AFRAME.AGLTFEntity = { } }, registerComponent(componentKey, componentName, inflator) { - AFRAME.AGLTFEntity.components[componentKey] = { - inflator: inflator || AFRAME.AGLTFEntity.defaultInflator, + AFRAME.GLTFModelPlus.components[componentKey] = { + inflator: inflator || AFRAME.GLTFModelPlus.defaultInflator, componentName }; }, @@ -128,7 +128,7 @@ const inflateEntities = function(parentEl, node) { if (entityComponents) { for (const prop in entityComponents) { if (entityComponents.hasOwnProperty(prop)) { - const { inflator, componentName } = AFRAME.AGLTFEntity.components[prop]; + const { inflator, componentName } = AFRAME.GLTFModelPlus.components[prop]; if (inflator) { inflator(el, componentName, entityComponents[prop]); @@ -190,138 +190,94 @@ function cachedLoadGLTF(src, onProgress) { }); } -AFRAME.registerElement("a-gltf-entity", { - prototype: Object.create(AFRAME.AEntity.prototype, { - load: { - async value() { - if (this.hasLoaded || !this.parentEl) { - return; - } - - // The code above and below this are from AEntity.prototype.load, we need to monkeypatch in gltf loading mid function - this.loadTemplates(); - await this.applySrc(this.getAttribute("src")); - // - - AFRAME.ANode.prototype.load.call(this, () => { - // Check if entity was detached while it was waiting to load. - if (!this.parentEl) { - return; - } +AFRAME.registerComponent("gltf-model-plus", { + schema: { + src: { type: "string" }, + inflate: { default: false } + }, - this.updateComponents(); - if (this.isScene || this.parentEl.isPlaying) { - this.play(); - } - }); - } - }, - - loadTemplates: { - value() { - this.templates = []; - this.querySelectorAll(":scope > template").forEach(templateEl => - this.templates.push({ - selector: templateEl.getAttribute("data-selector"), - templateRoot: document.importNode( - templateEl.firstElementChild || templateEl.content.firstElementChild, - true - ) - }) - ); - } - }, - - applySrc: { - async value(src) { - try { - // If the src attribute is a selector, get the url from the asset item. - if (src && src.charAt(0) === "#") { - const assetEl = document.getElementById(src.substring(1)); - if (!assetEl) { - console.warn(`Attempted to use non-existent asset ${src} as src for`, this); - return; - } - - const fallbackSrc = assetEl.getAttribute("src"); - const highSrc = assetEl.getAttribute("high-src"); - const lowSrc = assetEl.getAttribute("low-src"); - - if (highSrc && window.APP.quality === "high") { - src = highSrc; - } else if (lowSrc && window.APP.quality === "low") { - src = lowSrc; - } else { - src = fallbackSrc; - } - } + init() { + this.loadTemplates(); + }, - if (src === this.lastSrc) return; - this.lastSrc = src; + update() { + this.applySrc(this.data.src); + }, - if (!src) { - if (this.inflatedEl) { - console.warn("gltf-entity set to an empty source, unloading inflated model."); - this.removeInflatedEl(); - } - return; - } + loadTemplates() { + this.templates = []; + this.el.querySelectorAll(":scope > template").forEach(templateEl => + this.templates.push({ + selector: templateEl.getAttribute("data-selector"), + templateRoot: document.importNode(templateEl.firstElementChild || templateEl.content.firstElementChild, true) + }) + ); + }, - const model = await cachedLoadGLTF(src); + async applySrc(src) { + try { + // If the src attribute is a selector, get the url from the asset item. + if (src && src.charAt(0) === "#") { + const assetEl = document.getElementById(src.substring(1)); + + const fallbackSrc = assetEl.getAttribute("src"); + const highSrc = assetEl.getAttribute("high-src"); + const lowSrc = assetEl.getAttribute("low-src"); + + if (highSrc && window.APP.quality === "high") { + src = highSrc; + } else if (lowSrc && window.APP.quality === "low") { + src = lowSrc; + } else { + src = fallbackSrc; + } + } - // If we started loading something else already - // TODO: there should be a way to cancel loading instead - if (src != this.lastSrc) return; + if (src === this.lastSrc) return; + this.lastSrc = src; - // If we had inflated something already before, clean that up + if (!src) { + if (this.inflatedEl) { + console.warn("gltf-model-plus set to an empty source, unloading inflated model."); this.removeInflatedEl(); + } + return; + } - this.model = model.scene || model.scenes[0]; - this.model.animations = model.animations; + const model = await cachedLoadGLTF(src); - this.setObject3D("mesh", this.model); + // If we started loading something else already + // TODO: there should be a way to cancel loading instead + if (src != this.lastSrc) return; - if (this.getAttribute("inflate")) { - this.inflatedEl = inflateEntities(this, this.model); - // TODO: Still don't fully understand the lifecycle here and how it differs between browsers, we should dig in more - // Wait one tick for the appended custom elements to be connected before attaching templates - await nextTick(); - if (src != this.lastSrc) return; // TODO: there must be a nicer pattern for this - this.templates.forEach(attachTemplate.bind(null, this)); - } + // If we had inflated something already before, clean that up + this.removeInflatedEl(); - this.emit("model-loaded", { format: "gltf", model: this.model }); - } catch (e) { - console.error("Failed to load glTF model", e.message, this); - this.emit("model-error", { format: "gltf", src }); - } - } - }, + this.model = model.scene || model.scenes[0]; + this.model.animations = model.animations; - removeInflatedEl: { - value() { - if (this.inflatedEl) { - this.inflatedEl.parentNode.removeChild(this.inflatedEl); - delete this.inflatedEl; - } - } - }, + this.el.setObject3D("mesh", this.model); - attributeChangedCallback: { - value(attr, oldVal, newVal) { - if (attr === "src") { - this.applySrc(newVal); - } + if (this.data.inflate) { + this.inflatedEl = inflateEntities(this.el, this.model); + // TODO: Still don't fully understand the lifecycle here and how it differs between browsers, we should dig in more + // Wait one tick for the appended custom elements to be connected before attaching templates + await nextTick(); + if (src != this.lastSrc) return; // TODO: there must be a nicer pattern for this + this.templates.forEach(attachTemplate.bind(null, this.el)); } - }, - setAttribute: { - value(attr, arg1, arg2) { - if (attr === "src") { - this.applySrc(arg1); - } - AFRAME.AEntity.prototype.setAttribute.call(this, attr, arg1, arg2); - } + this.el.emit("model-loaded", { format: "gltf", model: this.model }); + } catch (e) { + console.error("Failed to load glTF model", e.message, this); + this.emit("model-error", { format: "gltf", src }); } - }) + }, + + removeInflatedEl() { + if (this.inflatedEl) { + this.inflatedEl.parentNode.removeChild(this.inflatedEl); + delete this.inflatedEl; + } + } }); diff --git a/src/components/haptic-feedback.js b/src/components/haptic-feedback.js index 1ef413d38939a183c25438694cebaec934d9cf23..35a79cd9555e2864e5c496f671fcb9ddd8831345 100644 --- a/src/components/haptic-feedback.js +++ b/src/components/haptic-feedback.js @@ -18,9 +18,9 @@ AFRAME.registerComponent("haptic-feedback", { }, getActuator() { - return new Promise((resolve, reject) => { + return new Promise(resolve => { const tryGetActivator = () => { - var trackedControls = this.el.components["tracked-controls"]; + const trackedControls = this.el.components["tracked-controls"]; if ( trackedControls && trackedControls.controller && @@ -44,7 +44,7 @@ AFRAME.registerComponent("haptic-feedback", { }, pulse: function(event) { - let { intensity } = event.detail; + const { intensity } = event.detail; if (!strengthForIntensity[intensity]) { console.warn(`Invalid intensity : ${intensity}`); return; diff --git a/src/components/ik-controller.js b/src/components/ik-controller.js index 0a1990bd8ede46f4e5bbdde5cfced182ed4f5a3d..4fd89465e1d4ce97c24276b5d23d89eda5a25163 100644 --- a/src/components/ik-controller.js +++ b/src/components/ik-controller.js @@ -142,7 +142,6 @@ AFRAME.registerComponent("ik-controller", { cameraYRotation, cameraYQuaternion, invHipsQuaternion, - headQuaternion, leftHand, rightHand, rootToChest, diff --git a/src/components/in-world-hud.js b/src/components/in-world-hud.js index e69cfd7374eaa1ca1972b3cb00784d837dc987b8..2c10aad240d6ee2d21ea8d536751b0fc3785a463 100644 --- a/src/components/in-world-hud.js +++ b/src/components/in-world-hud.js @@ -96,7 +96,7 @@ AFRAME.registerComponent("in-world-hud", { this.el.sceneEl.removeEventListener("micAudio", this.onAudioFrequencyChange); }, - tick: function(t, dt) { + tick: function() { if (!this.analyser) return; this.analyser.getByteFrequencyData(this.levels); diff --git a/src/components/mute-mic.js b/src/components/mute-mic.js index 272b40e3b41f5d4aa49d7107b6aa41785b8d5596..00729ba1904002a88729e21ed82f69dbb872dda9 100644 --- a/src/components/mute-mic.js +++ b/src/components/mute-mic.js @@ -1,6 +1,6 @@ const bindAllEvents = function(elements, events, f) { if (!elements || !elements.length) return; - for (var el of elements) { + for (const el of elements) { events.length && events.forEach(e => { el.addEventListener(e, f); @@ -9,7 +9,7 @@ const bindAllEvents = function(elements, events, f) { }; const unbindAllEvents = function(elements, events, f) { if (!elements || !elements.length) return; - for (var el of elements) { + for (const el of elements) { events.length && events.forEach(e => { el.removeEventListener(e, f); diff --git a/src/components/networked-counter.js b/src/components/networked-counter.js index c35dcbaa461fa5ca8627d7cf693455d4244ec9fe..9c4fb7105578b49b48d5abe61e4c92193a0aaede 100644 --- a/src/components/networked-counter.js +++ b/src/components/networked-counter.js @@ -20,7 +20,7 @@ AFRAME.registerComponent("networked-counter", { item.el.removeEventListener(this.data.release_event, item.onReleaseHandler); } } - + for (const id in this.timeouts) { this._removeTimeout(id); } @@ -75,11 +75,11 @@ AFRAME.registerComponent("networked-counter", { } }, - _onGrabbed: function(id, e) { + _onGrabbed: function(id) { this._removeTimeout(id); }, - _onReleased: function(id, e) { + _onReleased: function(id) { this._removeTimeout(id); this._addTimeout(id); this.queue[id].ts = Date.now(); @@ -91,7 +91,6 @@ AFRAME.registerComponent("networked-counter", { ts = Number.MAX_VALUE; for (const id in this.queue) { if (this.queue.hasOwnProperty(id)) { - const expiration = this.queue[id].ts + this.data.ttl * 1000; if (this.queue[id].ts < ts && !this._isCurrentlyGrabbed(id)) { oldest = this.queue[id]; ts = this.queue[id].ts; diff --git a/src/components/networked-video-player.js b/src/components/networked-video-player.js index 51adcf2f7f2ddd6d0023c54442e03627ea952c70..189a43e393f359a5c2d217b5978132476f6407c6 100644 --- a/src/components/networked-video-player.js +++ b/src/components/networked-video-player.js @@ -25,7 +25,7 @@ AFRAME.registerComponent("networked-video-player", { if (ownerId !== NAF.clientId && rejectScreenShares) { // Toggle material visibility since object visibility is network-synced // TODO: There ought to be a better way to disable network syncs on a remote entity - this.el.setAttribute("material", {visible: false}); + this.el.setAttribute("material", { visible: false }); return; } diff --git a/src/components/offset-relative-to.js b/src/components/offset-relative-to.js index 923eae3e5673488e8eceb726b9e91f592285f546..f940b687284b34677c579e931557fc9671631f41 100644 --- a/src/components/offset-relative-to.js +++ b/src/components/offset-relative-to.js @@ -12,10 +12,7 @@ AFRAME.registerComponent("offset-relative-to", { }, init() { this.updateOffset(); - this.el.sceneEl.addEventListener( - this.data.on, - this.updateOffset.bind(this) - ); + this.el.sceneEl.addEventListener(this.data.on, this.updateOffset.bind(this)); }, updateOffset() { const offsetVector = new THREE.Vector3().copy(this.data.offset); diff --git a/src/components/player-info.js b/src/components/player-info.js index ac2fc6b55fcdaf0215e26327368c07dd67e88fcc..b9455352a104bd71aceb07ed4718a2999a4d08b6 100644 --- a/src/components/player-info.js +++ b/src/components/player-info.js @@ -12,7 +12,7 @@ AFRAME.registerComponent("player-info", { pause() { this.el.removeEventListener("model-loaded", this.applyProperties); }, - update(oldProps) { + update() { this.applyProperties(); }, applyProperties() { @@ -25,7 +25,7 @@ AFRAME.registerComponent("player-info", { const modelEl = this.el.querySelector(".model"); if (this.data.avatarSrc && modelEl) { - modelEl.setAttribute("src", this.data.avatarSrc); + modelEl.setAttribute("gltf-model-plus", "src", this.data.avatarSrc); } } }); diff --git a/src/components/super-cursor.js b/src/components/super-cursor.js index e498a1acc2ad9b99c9131134bd17560bee1af531..28589171658f9d06ed57b4e604d91a73032ea583 100644 --- a/src/components/super-cursor.js +++ b/src/components/super-cursor.js @@ -103,7 +103,7 @@ AFRAME.registerComponent("super-cursor", { } }, - _handleMouseDown: function(e) { + _handleMouseDown: function() { if (this.isInteractable) { const lookControls = this.data.camera.components["look-controls"]; lookControls.pause(); @@ -115,7 +115,7 @@ AFRAME.registerComponent("super-cursor", { this.mousePos.set(e.clientX / window.innerWidth * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1); }, - _handleMouseUp: function(e) { + _handleMouseUp: function() { const lookControls = this.data.camera.components["look-controls"]; lookControls.play(); this.data.cursor.emit("action_release", {}); @@ -125,13 +125,13 @@ AFRAME.registerComponent("super-cursor", { if (this.isGrabbing) this.currentDistanceMod += e.deltaY / 10; }, - _handleEnterVR: function(e) { + _handleEnterVR: function() { if (AFRAME.utils.device.checkHeadsetConnected() || AFRAME.utils.device.isMobile()) { this._disable(); } }, - _handleExitVR: function(e) { + _handleExitVR: function() { this._enable(); }, diff --git a/src/components/super-networked-interactable.js b/src/components/super-networked-interactable.js index 4aeafecaa460379852a0ff4e9eb0f0f0d25bf9f3..a5baed53dd2f3f5404a81a26150904353434e9e9 100644 --- a/src/components/super-networked-interactable.js +++ b/src/components/super-networked-interactable.js @@ -11,7 +11,7 @@ AFRAME.registerComponent("super-networked-interactable", { NAF.utils.getNetworkedEntity(this.el).then(networkedEl => { this.networkedEl = networkedEl; if (!NAF.utils.isMine(networkedEl)) { - this.el.setAttribute("body", {type: "dynamic", mass: 0}); + this.el.setAttribute("body", { type: "dynamic", mass: 0 }); } else { this.counter.register(networkedEl); } @@ -33,7 +33,7 @@ AFRAME.registerComponent("super-networked-interactable", { this.hand = e.detail.hand; if (this.networkedEl && !NAF.utils.isMine(this.networkedEl)) { if (NAF.utils.takeOwnership(this.networkedEl)) { - this.el.setAttribute("body", {mass: this.data.mass}); + this.el.setAttribute("body", { mass: this.data.mass }); this.counter.register(this.networkedEl); } else { this.el.emit("grab-end", { hand: this.hand }); @@ -42,8 +42,8 @@ AFRAME.registerComponent("super-networked-interactable", { } }, - _onOwnershipLost: function(e) { - this.el.setAttribute("body", {mass: 0}); + _onOwnershipLost: function() { + this.el.setAttribute("body", { mass: 0 }); this.el.emit("grab-end", { hand: this.hand }); this.hand = null; this.counter.deregister(this.el); diff --git a/src/components/super-spawner.js b/src/components/super-spawner.js index 72d24eff785b9eebed42c60e2e4feb21e659468a..bb6762a200eeacf53c29179f95554c7dc8fdab57 100644 --- a/src/components/super-spawner.js +++ b/src/components/super-spawner.js @@ -19,7 +19,7 @@ AFRAME.registerComponent("super-spawner", { }, remove: function() { - for (let entity of this.entities.keys()) { + for (const entity of this.entities.keys()) { const data = this.entities.get(entity); entity.removeEventListener("componentinitialized", data.componentinInitializedListener); entity.removeEventListener("bodyloaded", data.bodyLoadedListener); @@ -39,12 +39,12 @@ AFRAME.registerComponent("super-spawner", { this.entities.set(entity, { hand: hand, - componentInitialized: false, - bodyLoaded: false, - componentinInitializedListener: componentinInitializedListener, + componentInitialized: false, + bodyLoaded: false, + componentinInitializedListener: componentinInitializedListener, bodyLoadedListener: bodyLoadedListener }); - + entity.addEventListener("componentinitialized", componentinInitializedListener); entity.addEventListener("body-loaded", bodyLoadedListener); @@ -60,7 +60,7 @@ AFRAME.registerComponent("super-spawner", { } }, - _handleBodyLoaded: function(entity, e) { + _handleBodyLoaded: function(entity) { this.entities.get(entity).bodyLoaded = true; this._emitEvents.call(this, entity); }, diff --git a/src/components/virtual-gamepad-controls.js b/src/components/virtual-gamepad-controls.js index d399e38b29765280a677bb23289cdc3e4c2aff6b..d70219bf1e6374fefc6d6daaeb9e8e10bfc27fb8 100644 --- a/src/components/virtual-gamepad-controls.js +++ b/src/components/virtual-gamepad-controls.js @@ -1,10 +1,6 @@ import nipplejs from "nipplejs"; import styles from "./virtual-gamepad-controls.css"; -const THREE = AFRAME.THREE; -const DEGREES = Math.PI / 180; -const HALF_PI = Math.PI / 2; - AFRAME.registerComponent("virtual-gamepad-controls", { schema: {}, diff --git a/src/components/wasd-to-analog2d.js b/src/components/wasd-to-analog2d.js index a424cb893ab10c29575667f7c68539dd65e90689..35b68026bd121a35bb7f7c139d15423262475b86 100644 --- a/src/components/wasd-to-analog2d.js +++ b/src/components/wasd-to-analog2d.js @@ -44,7 +44,7 @@ AFRAME.registerComponent("wasd-to-analog2d", { this.keys[key] = down; }, - tick: function(t, dt) { + tick: function() { this.target = [0, 0]; for (const key in this.keys) { diff --git a/src/components/water.js b/src/components/water.js index 9152e548435ea8b184e9d1eb0f8cd4c2371217cc..b7f176131ea97f6f47fbbb31b27c59e80350b78b 100644 --- a/src/components/water.js +++ b/src/components/water.js @@ -17,24 +17,14 @@ function MobileWater(geometry, options) { options = options || {}; - const clipBias = options.clipBias !== undefined ? options.clipBias : 0.0; const time = options.time !== undefined ? options.time : 0.0; - const normalSampler = - options.waterNormals !== undefined ? options.waterNormals : null; + const normalSampler = options.waterNormals !== undefined ? options.waterNormals : null; const sunDirection = - options.sunDirection !== undefined - ? options.sunDirection - : new THREE.Vector3(0.70707, 0.70707, 0.0); - const sunColor = new THREE.Color( - options.sunColor !== undefined ? options.sunColor : 0xffffff - ); - const waterColor = new THREE.Color( - options.waterColor !== undefined ? options.waterColor : 0x7f7f7f - ); - const eye = - options.eye !== undefined ? options.eye : new THREE.Vector3(0, 0, 0); - const distortionScale = - options.distortionScale !== undefined ? options.distortionScale : 20.0; + options.sunDirection !== undefined ? options.sunDirection : new THREE.Vector3(0.70707, 0.70707, 0.0); + const sunColor = new THREE.Color(options.sunColor !== undefined ? options.sunColor : 0xffffff); + const waterColor = new THREE.Color(options.waterColor !== undefined ? options.waterColor : 0x7f7f7f); + const eye = options.eye !== undefined ? options.eye : new THREE.Vector3(0, 0, 0); + const distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0; const side = options.side !== undefined ? options.side : THREE.FrontSide; const fog = options.fog !== undefined ? options.fog : false; diff --git a/src/elements/a-progressive-asset.js b/src/elements/a-progressive-asset.js index 8bf922aa17b10ce6eec09baf413e93dcf6d871f1..db51b9b74484043a1f83847cc64d4ccc4bc6554c 100644 --- a/src/elements/a-progressive-asset.js +++ b/src/elements/a-progressive-asset.js @@ -33,7 +33,7 @@ AFRAME.registerElement("a-progressive-asset", { src = lowSrc; } - this.fileLoader.setResponseType(this.getAttribute("response-type") || inferResponseType(src)); + this.fileLoader.setResponseType(this.getAttribute("response-type")); this.fileLoader.load( src, function handleOnLoad(response) { diff --git a/src/gltf-component-mappings.js b/src/gltf-component-mappings.js index e4cf086733ad1e0dc547abafe3fb37ed025a5608..7c6b6a8d8774eeeda0de6f0fb074848a5e78be51 100644 --- a/src/gltf-component-mappings.js +++ b/src/gltf-component-mappings.js @@ -1,4 +1,4 @@ -import "./elements/a-gltf-entity"; +import "./components/gltf-model-plus"; -AFRAME.AGLTFEntity.registerComponent("scale-audio-feedback", "scale-audio-feedback"); -AFRAME.AGLTFEntity.registerComponent("loop-animation", "loop-animation"); +AFRAME.GLTFModelPlus.registerComponent("scale-audio-feedback", "scale-audio-feedback"); +AFRAME.GLTFModelPlus.registerComponent("loop-animation", "loop-animation"); diff --git a/src/hub.html b/src/hub.html index 1105c66efc22c08d0e791ef92ee75d4953fd0392..987d889be0bfb0224737569c3b29e31880c1a340 100644 --- a/src/hub.html +++ b/src/hub.html @@ -112,7 +112,7 @@ <a-entity class="right-controller"></a-entity> - <a-gltf-entity class="model" inflate="true"> + <a-entity class="model" gltf-model-plus="inflate: true"> <template data-selector=".RootScene"> <a-entity ik-controller animation-mixer></a-entity> </template> @@ -145,13 +145,13 @@ <template data-selector=".RightHand"> <a-entity personal-space-invader ></a-entity> </template> - </a-gltf-entity> + </a-entity> </a-entity> </template> <template id="interactable-template"> <a-entity - gltf-model="#interactable-duck" + gltf-model-plus="src: #interactable-duck" scale="2 2 2" class="interactable" super-networked-interactable="counter: #counter; mass: 5;" @@ -176,8 +176,8 @@ <!-- Interactables --> <a-entity id="counter" networked-counter="max: 3; ttl: 120"></a-entity> - <a-entity - gltf-model="#interactable-duck" + <a-entity + gltf-model-plus="src: #interactable-duck" scale="2 2 2" class="interactable" super-spawner="template: #interactable-template;" @@ -264,7 +264,7 @@ app-mode-toggle-attribute__line="mode: hud; property: visible;" ></a-entity> - <a-gltf-entity class="model" inflate="true"> + <a-entity gltf-model-plus="inflate: true;" class="model"> <template data-selector=".RootScene"> <a-entity ik-controller @@ -285,14 +285,14 @@ <template data-selector=".LeftHand"> <a-entity> - <a-gltf-entity + <a-entity id="watch" - src="#watch-model" + gltf-model-plus="src: #watch-model" bone-mute-state-indicator scale="1.5 1.5 1.5" rotation="0 -90 90" position="0 -0.04 0" - ></a-gltf-entity> + ></a--entity> <a-entity event-repeater="events: action_grab, action_release; eventSource: #player-left-controller" static-body="shape: sphere; sphereRadius: 0.02" @@ -313,7 +313,7 @@ </a-entity> </template> - </a-gltf-entity> + </a-entity> </a-entity> <!-- Lights --> diff --git a/src/hub.js b/src/hub.js index e29dc6cdfbe801a5938407d2ebe66d5df8fd6b2b..b6cf032d1677bbafecd557cfb5fa530ff5b63451 100644 --- a/src/hub.js +++ b/src/hub.js @@ -41,6 +41,7 @@ import "./components/player-info"; import "./components/debug"; import "./components/animation-mixer"; import "./components/loop-animation"; +import "./components/gltf-model-plus"; import "./components/gltf-bundle"; import ReactDOM from "react-dom"; @@ -50,8 +51,6 @@ import UIRoot from "./react-components/ui-root"; import "./systems/personal-space-bubble"; import "./systems/app-mode"; -import "./elements/a-gltf-entity"; - import "./gltf-component-mappings"; import { App } from "./App"; @@ -222,6 +221,8 @@ function mountUI(scene) { const enableScreenSharing = qsTruthy("enable_screen_sharing"); const htmlPrefix = document.body.dataset.htmlPrefix || ""; + // TODO: Refactor to avoid using return value + /* eslint-disable react/no-render-return-value */ const uiRoot = ReactDOM.render( <UIRoot {...{ @@ -238,6 +239,7 @@ function mountUI(scene) { />, document.getElementById("ui-root") ); + /* eslint-enable react/no-render-return-value */ return uiRoot; } diff --git a/src/index.js b/src/index.js index 2518b42dd56390078e56b04220ed780ab6abe707..198ee8d47378bd740fc9a2891cc6945447442b2a 100644 --- a/src/index.js +++ b/src/index.js @@ -4,4 +4,5 @@ import ReactDOM from "react-dom"; import HomeRoot from "./react-components/home-root"; import registerTelemetry from "./telemetry"; +registerTelemetry(); ReactDOM.render(<HomeRoot />, document.getElementById("home-root")); diff --git a/src/react-components/2d-hud.js b/src/react-components/2d-hud.js index fda3ae06ad0ce63b7b773d5a69f4141b52389c9f..8d9223a072c97c670210e67e48434cfc58543d49 100644 --- a/src/react-components/2d-hud.js +++ b/src/react-components/2d-hud.js @@ -1,8 +1,8 @@ -import React, { Component } from "react"; +import React from "react"; import PropTypes from "prop-types"; import cx from "classnames"; -import styles from "./2d-hud.css"; +import styles from "../assets/stylesheets/2d-hud.css"; const TwoDHUD = ({ name, muted, onToggleMute }) => ( <div className={styles.container}> diff --git a/src/react-components/auto-exit-warning.js b/src/react-components/auto-exit-warning.js index 8663dcca2277d42456511adb212238d3bcdb936f..d8691a85182c7f8bbd3ee9a7472e428df9aa2d3b 100644 --- a/src/react-components/auto-exit-warning.js +++ b/src/react-components/auto-exit-warning.js @@ -1,4 +1,4 @@ -import React, { Component } from "react"; +import React from "react"; import { FormattedMessage } from "react-intl"; import PropTypes from "prop-types"; diff --git a/src/react-components/avatar-selector.js b/src/react-components/avatar-selector.js index 6ce9a8a172726bb1b6d6194e13d5f88aa29185b5..5cb456ee578bd0c966c02653296457d425c31326 100644 --- a/src/react-components/avatar-selector.js +++ b/src/react-components/avatar-selector.js @@ -56,7 +56,7 @@ class AvatarSelector extends Component { const avatarEntities = this.props.avatars.map((avatar, i) => ( <a-entity key={avatar.id} position="0 0 0" rotation={`0 ${360 * -i / this.props.avatars.length} 0`}> - <a-gltf-entity position="0 0 5" rotation="0 0 0" src={"#" + avatar.id} inflate="true"> + <a-entity position="0 0 5" rotation="0 0 0" gltf-model-plus={`src: #${avatar.id}`} inflate="true"> <template data-selector=".RootScene"> <a-entity animation-mixer /> </template> @@ -66,7 +66,7 @@ class AvatarSelector extends Component { to={`0 ${this.getAvatarIndex() === i ? 360 : 0} 0`} repeat="indefinite" /> - </a-gltf-entity> + </a-entity> </a-entity> )); @@ -100,7 +100,7 @@ class AvatarSelector extends Component { position="0 5 -15" /> <a-entity hide-when-quality="low" light="type: ambient; color: #FFF" /> - <a-gltf-entity id="meeting-space" src="#meeting-space1-mesh" position="0 0 0" /> + <a-entity id="meeting-space" gltf-model-plus="src: #meeting-space1-mesh" position="0 0 0" /> </a-scene> <button className="avatar-selector__previous-button" onClick={this.emitChangeToPrevious}> <FontAwesomeIcon icon={faAngleLeft} /> diff --git a/src/react-components/entry-buttons.js b/src/react-components/entry-buttons.js index e567c6e1111f362ab4175087794af1f38a6a1c24..8ed8b171d6e7ec08d82d59af2c5314d9353a7910 100644 --- a/src/react-components/entry-buttons.js +++ b/src/react-components/entry-buttons.js @@ -1,28 +1,28 @@ -import React, { Component } from "react"; +import React from "react"; import { FormattedMessage } from "react-intl"; import PropTypes from "prop-types"; -import MobileDetect from 'mobile-detect'; +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'; +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"/> +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 }/> + <FormattedMessage id={props.prefixMessageId} /> </span> <span className="entry-button--bolded"> - <FormattedMessage id={ props.mediumMessageId }/> + <FormattedMessage id={props.mediumMessageId} /> </span> - { props.subtitle && (<div className="entry-button__subtitle">{props.subtitle}</div>) } + {props.subtitle && <div className="entry-button__subtitle">{props.subtitle}</div>} </div> </div> </div> @@ -34,20 +34,20 @@ EntryButton.propTypes = { prefixMessageId: PropTypes.string, mediumMessageId: PropTypes.string, subtitle: PropTypes.string -} +}; -export const TwoDEntryButton = (props) => { +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" + mediumMessageId: mobiledetect.mobile() ? "entry.mobile-screen" : "entry.desktop-screen" }; - return (<EntryButton {...entryButtonProps}/>); -} + return <EntryButton {...entryButtonProps} />; +}; -export const GenericEntryButton = (props) => { +export const GenericEntryButton = props => { const entryButtonProps = { ...props, iconSrc: GenericVREntryImg, @@ -55,10 +55,10 @@ export const GenericEntryButton = (props) => { mediumMessageId: "entry.generic-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; -export const GearVREntryButton = (props) => { +export const GearVREntryButton = props => { const entryButtonProps = { ...props, iconSrc: GearVREntryImg, @@ -66,10 +66,10 @@ export const GearVREntryButton = (props) => { mediumMessageId: "entry.gearvr-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; -export const DaydreamEntryButton = (props) => { +export const DaydreamEntryButton = props => { const entryButtonProps = { ...props, iconSrc: DaydreamEntyImg, @@ -77,6 +77,5 @@ export const DaydreamEntryButton = (props) => { mediumMessageId: "entry.daydream-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; - diff --git a/src/react-components/home-root.js b/src/react-components/home-root.js index 332904b9db9fd29ecb8b57f6e929cf07127d78f3..984a3d44c2fbb29973c3f1ed9dee4395d015bfa2 100644 --- a/src/react-components/home-root.js +++ b/src/react-components/home-root.js @@ -1,8 +1,6 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; -import classNames from "classnames"; -import queryString from "query-string"; -import { IntlProvider, injectIntl, FormattedMessage, addLocaleData } from "react-intl"; +import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl"; import en from "react-intl/locale-data/en"; import homeVideo from "../assets/video/home.webm"; diff --git a/src/react-components/profile-entry-panel.js b/src/react-components/profile-entry-panel.js index 7dd04d84ecbaab4ae3248fbab400579d3d621135..f5225de8d982b1c7249a0b3a16c541300bcc784c 100644 --- a/src/react-components/profile-entry-panel.js +++ b/src/react-components/profile-entry-panel.js @@ -1,6 +1,6 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { injectIntl, FormattedMessage } from 'react-intl'; +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 { @@ -8,14 +8,15 @@ class ProfileEntryPanel extends Component { store: PropTypes.object, messages: PropTypes.object, finished: PropTypes.func, - htmlPrefix: PropTypes.string - } + htmlPrefix: PropTypes.string, + intl: PropTypes.object + }; constructor(props) { super(props); this.state = { display_name: this.props.store.state.profile.display_name, - avatar_id: this.props.store.state.profile.avatar_id, + avatar_id: this.props.store.state.profile.avatar_id }; this.props.store.addEventListener("statechanged", this.storeUpdated); } @@ -23,64 +24,72 @@ class ProfileEntryPanel extends Component { storeUpdated = () => { const { avatar_id, display_name } = this.props.store.state.profile; this.setState({ avatar_id, display_name }); - } + }; - saveStateAndFinish = (e) => { + saveStateAndFinish = e => { e.preventDefault(); - this.props.store.update({profile: { - display_name: this.state.display_name, - avatar_id: this.state.avatar_id - }}); + this.props.store.update({ + profile: { + display_name: this.state.display_name, + avatar_id: this.state.avatar_id + } + }); this.props.finished(); - } + }; - stopPropagation = (e) => { + stopPropagation = e => { e.stopPropagation(); - } + }; - setAvatarStateFromIframeMessage = (e) => { - if (e.source !== this.avatarSelector.contentWindow) { return; } - this.setState({avatar_id: e.data.avatarId}); - } + setAvatarStateFromIframeMessage = e => { + if (e.source !== this.avatarSelector.contentWindow) { + return; + } + this.setState({ avatar_id: e.data.avatarId }); + }; componentDidMount() { // stop propagation so that avatar doesn't move when wasd'ing during text input. - this.nameInput.addEventListener('keydown', this.stopPropagation); - this.nameInput.addEventListener('keypress', this.stopPropagation); - this.nameInput.addEventListener('keyup', this.stopPropagation); - window.addEventListener('message', this.setAvatarStateFromIframeMessage); + this.nameInput.addEventListener("keydown", this.stopPropagation); + this.nameInput.addEventListener("keypress", this.stopPropagation); + this.nameInput.addEventListener("keyup", this.stopPropagation); + window.addEventListener("message", this.setAvatarStateFromIframeMessage); } componentWillUnmount() { - this.props.store.removeEventListener('statechanged', this.storeUpdated); - this.nameInput.removeEventListener('keydown', this.stopPropagation); - this.nameInput.removeEventListener('keypress', this.stopPropagation); - this.nameInput.removeEventListener('keyup', this.stopPropagation); - window.removeEventListener('message', this.setAvatarStateFromIframeMessage); + this.props.store.removeEventListener("statechanged", this.storeUpdated); + this.nameInput.removeEventListener("keydown", this.stopPropagation); + this.nameInput.removeEventListener("keypress", this.stopPropagation); + this.nameInput.removeEventListener("keyup", this.stopPropagation); + window.removeEventListener("message", this.setAvatarStateFromIframeMessage); } - render () { + render() { const { formatMessage } = this.props.intl; return ( <div className="profile-entry"> <form onSubmit={this.saveStateAndFinish}> - <div className="profile-entry__box profile-entry__box--darkened"> - <div className="profile-entry__subtitle"> - <FormattedMessage id="profile.header"/> + <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.display_name} + onChange={e => this.setState({ display_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)} + /> + <iframe + className="profile-entry__avatar-selector" + src={`${this.props.htmlPrefix}avatar-selector.html#avatar_id=${this.state.avatar_id}`} + ref={ifr => (this.avatarSelector = ifr)} + /> + <input className="profile-entry__form-submit" type="submit" value={formatMessage({ id: "profile.save" })} /> </div> - <input - className="profile-entry__form-field-text" - value={this.state.display_name} onChange={(e) => this.setState({display_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}/> - <iframe - className="profile-entry__avatar-selector" - src={`${this.props.htmlPrefix}avatar-selector.html#avatar_id=${this.state.avatar_id}`} - ref={ifr => this.avatarSelector = ifr}></iframe> - <input className="profile-entry__form-submit" type="submit" value={formatMessage({ id: "profile.save" }) }/> - </div> </form> </div> ); diff --git a/src/react-components/profile-info-header.js b/src/react-components/profile-info-header.js index 6b9e82bb124e931dbf59b0867eaa3e36aae431f2..43ec49291007089d54e1d1a52c7a586e766e5fb4 100644 --- a/src/react-components/profile-info-header.js +++ b/src/react-components/profile-info-header.js @@ -1,4 +1,4 @@ -import React, { Component } from "react"; +import React from "react"; import PropTypes from "prop-types"; export const ProfileInfoHeader = props => ( diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index b2b0633927428f93787298216536a247fed4d815..397621adc442b4ef7089588ff1eb3484c30891a9 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -3,7 +3,6 @@ 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"; @@ -34,13 +33,6 @@ const ENTRY_STEPS = { 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); -} - // 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, @@ -49,13 +41,19 @@ async function grantedMicLabels() { // // 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 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); +} const AUTO_EXIT_TIMER_SECONDS = 10; class UIRoot extends Component { static propTypes = { enterScene: PropTypes.func, + exitScene: PropTypes.func, concurrentLoadDetector: PropTypes.object, disableAutoExitOnConcurrentLoad: PropTypes.bool, forcedVREntryType: PropTypes.string, @@ -254,32 +252,32 @@ class UIRoot extends Component { this.exit(); // Launch via Oculus Browser - const qs = queryString.parse(document.location.search); + const location = window.location; + const qs = queryString.parse(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 || ""}`; + const ovrwebUrl = + `ovrweb://${location.protocol || "http:"}//${location.host}` + + `${location.pathname || ""}?${queryString.stringify(qs)}#${location.hash || ""}`; - document.location = ovrwebUrl; + window.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); + const location = window.location; + const qs = queryString.parse(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; + const intentUrl = + `intent://${location.host}${location.pathname || ""}?` + + `${queryString.stringify(qs)}#Intent;scheme=${(location.protocol || "http:").replace(":", "")};` + + `action=android.intent.action.VIEW;package=com.android.chrome;end;`; + + window.location = intentUrl; } else { await this.performDirectEntryFlow(true); } @@ -328,7 +326,7 @@ class UIRoot extends Component { this.setState({ audioTrack: mediaStream.getAudioTracks()[0] }); }; - setupNewMediaStream = async constraints => { + setupNewMediaStream = async () => { const mediaStream = new MediaStream(); // we should definitely have an audioTrack at this point. diff --git a/src/storage/store.js b/src/storage/store.js index 037e27bf1d9603a210423baa5ee6543c2ee77fdb..a517bfc5749c53eb9a193298194f7ed7fa0ee56f 100644 --- a/src/storage/store.js +++ b/src/storage/store.js @@ -4,7 +4,7 @@ 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" +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 :)) @@ -17,7 +17,7 @@ export const SCHEMA = { additionalProperties: false, properties: { display_name: { type: "string", pattern: "^[A-Za-z0-9-]{3,32}$" }, - avatar_id: { type: "string" }, + avatar_id: { type: "string" } } } }, @@ -26,11 +26,11 @@ export const SCHEMA = { 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" }, + profile: { $ref: "#/definitions/profile" } }, additionalProperties: false -} +}; export default class Store extends EventTarget { constructor() { @@ -51,15 +51,14 @@ export default class Store extends EventTarget { update(newState) { if (newState.id) { - throw "Store id is immutable."; + throw new Error("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; + throw new Error(`Write of ${JSON.stringify(finalState)} to store failed schema validation.`); } localStorage.setItem(LOCAL_STORE_KEY, JSON.stringify(finalState)); diff --git a/src/systems/app-mode.js b/src/systems/app-mode.js index 421bf48abbab344b877f9805803a7dfe30472336..1ef1c2cd0d7b79c75d0738717558901370320e5b 100644 --- a/src/systems/app-mode.js +++ b/src/systems/app-mode.js @@ -188,7 +188,7 @@ AFRAME.registerComponent("vr-mode-toggle-visibility", { this.el.sceneEl.removeEventListener("exit-vr", this.updateComponentState); }, - updateComponentState(i) { + updateComponentState() { const inVRMode = this.el.sceneEl.is("vr-mode"); this.el.setAttribute("visible", inVRMode !== this.data.invert); } @@ -218,7 +218,7 @@ AFRAME.registerComponent("vr-mode-toggle-playing", { this.el.sceneEl.removeEventListener("exit-vr", this.updateComponentState); }, - updateComponentState(i) { + updateComponentState() { const componentName = this.id; const inVRMode = this.el.sceneEl.is("vr-mode"); this.el.components[componentName][inVRMode !== this.data.invert ? "play" : "pause"](); diff --git a/src/systems/personal-space-bubble.js b/src/systems/personal-space-bubble.js index 563818408f3dc4ea594f5ba2bc2eb10749dca5d2..e696705e138159a67c79e9707fe97bada64a9a58 100644 --- a/src/systems/personal-space-bubble.js +++ b/src/systems/personal-space-bubble.js @@ -39,16 +39,16 @@ AFRAME.registerSystem("personal-space-bubble", { tick() { // Update matrix positions once for each space bubble and space invader - for (var i = 0; i < this.bubbles.length; i++) { + for (let i = 0; i < this.bubbles.length; i++) { this.bubbles[i].object3D.updateMatrixWorld(true); } - for (var i = 0; i < this.invaders.length; i++) { + for (let i = 0; i < this.invaders.length; i++) { this.invaders[i].object3D.updateMatrixWorld(true); } // Loop through all of the space bubbles (usually one) - for (var i = 0; i < this.bubbles.length; i++) { + for (let i = 0; i < this.bubbles.length; i++) { const bubble = this.bubbles[i]; bubblePos.setFromMatrixPosition(bubble.object3D.matrixWorld); diff --git a/src/utils/concurrent-load-detector.js b/src/utils/concurrent-load-detector.js index f05f619a3f51fab56a33acaec2eeb3b75e48ccea..53fc2d1fa37c1f236966211ba2abaaf4631960de 100644 --- a/src/utils/concurrent-load-detector.js +++ b/src/utils/concurrent-load-detector.js @@ -3,7 +3,7 @@ // events. const LOCAL_STORE_KEY = "___concurrent_load_detector"; -import { EventTarget } from "event-target-shim" +import { EventTarget } from "event-target-shim"; export default class ConcurrentLoadDetector extends EventTarget { constructor(instanceKey) { @@ -20,17 +20,17 @@ export default class ConcurrentLoadDetector extends EventTarget { // 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())); @@ -40,5 +40,5 @@ export default class ConcurrentLoadDetector extends EventTarget { this.dispatchEvent(new CustomEvent("concurrentload")); this.stop(); } - } + }; } diff --git a/src/utils/dpad.js b/src/utils/dpad.js index 6f135122e6408bfdbc0a5a87d373104cbdc2e9bd..c603e75ad35438deb4311be5bdc68b64022da776 100644 --- a/src/utils/dpad.js +++ b/src/utils/dpad.js @@ -13,7 +13,7 @@ export function angleTo4Direction(angle) { export function angleTo8Direction(angle) { angle = (angle * THREE.Math.RAD2DEG + 180 + 45) % 360; - var direction = ""; + let direction = ""; if ((angle >= 0 && angle < 120) || angle >= 330) { direction += "north"; } diff --git a/src/utils/identity.js b/src/utils/identity.js index 118fe30812c1013cb64c47a335c24465a12f6636..def830cbc4df4d9d1a71b0c10d7b54c5a5610b4a 100644 --- a/src/utils/identity.js +++ b/src/utils/identity.js @@ -164,7 +164,7 @@ const names = [ ]; function selectRandom(arr) { - return arr[Math.floor(Math.random() * arr.length)] + return arr[Math.floor(Math.random() * arr.length)]; } export const avatarIds = avatars.map(av => av.id); @@ -172,7 +172,7 @@ export const avatarIds = avatars.map(av => av.id); export function generateDefaultProfile() { const name = selectRandom(names); return { - display_name: name.replace(/^./, name[0].toUpperCase()) , + display_name: name.replace(/^./, name[0].toUpperCase()), avatar_id: selectRandom(avatarIds) }; } diff --git a/webpack.config.js b/webpack.config.js index cd37e36c9064668355d7232ef37703ae6047bc0b..44b833cedd8bd6882cb629bec366d44014003de3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,8 +17,6 @@ function createHTTPSConfig() { return false; } - let https; - // Generate certs for the local webpack-dev-server. if (fs.existsSync(path.join(__dirname, "certs"))) { const key = fs.readFileSync(path.join(__dirname, "certs", "key.pem")); @@ -215,7 +213,8 @@ const config = { inject: "head" }), // Extract required css and add a content hash. - new ExtractTextPlugin("assets/stylesheets/[name]-[contenthash].css", { + new ExtractTextPlugin({ + filename: "assets/stylesheets/[name]-[contenthash].css", disable: process.env.NODE_ENV !== "production" }), // Transform the output of the html-loader using _.template diff --git a/yarn.lock b/yarn.lock index 18f7727e7dd7217a0e581df22c2ddfc12a8ff16a..41b0864f101f538ab31881607f082cfbc8b50cd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -440,16 +440,6 @@ assert@^1.1.1, assert@^1.4.0: dependencies: util "0.10.3" -assets-webpack-plugin@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/assets-webpack-plugin/-/assets-webpack-plugin-3.5.1.tgz#931ce0d66d42e88ed5e7f18d65522943c57a387d" - dependencies: - camelcase "^1.2.1" - escape-string-regexp "^1.0.3" - lodash.assign "^3.2.0" - lodash.merge "^3.3.2" - mkdirp "^0.5.1" - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -1309,7 +1299,7 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^3.4.7, bluebird@^3.5.1: +bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -1719,10 +1709,6 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -2829,6 +2815,16 @@ error@^7.0.2: string-template "~0.2.1" xtend "~4.0.0" +es-abstract@^1.5.1: + version "1.11.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + es-abstract@^1.7.0: version "1.10.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" @@ -2858,7 +2854,7 @@ escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" -escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -3884,17 +3880,17 @@ html-minifier@^3.2.3, html-minifier@^3.5.8: relateurl "0.2.x" uglify-js "3.3.x" -html-webpack-plugin@webpack-contrib/html-webpack-plugin: - version "2.30.1" - resolved "https://codeload.github.com/webpack-contrib/html-webpack-plugin/tar.gz/1dee37e2696fd3990fe9be0a2945e465c0bb9a64" +html-webpack-plugin@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.1.0.tgz#6e02baaedb1e906310917f03239c793a75af2885" dependencies: - bluebird "^3.4.7" html-minifier "^3.2.3" loader-utils "^0.2.16" lodash "^4.17.3" pretty-error "^2.0.2" tapable "^1.0.0" toposort "^1.0.0" + util.promisify "1.0.0" htmlescape@^1.1.0: version "1.1.1" @@ -4745,57 +4741,6 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" -lodash._arraycopy@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" - -lodash._arrayeach@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz#bab156b2a90d3f1bbd5c653403349e5e5933ef9e" - -lodash._baseassign@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" - dependencies: - lodash._basecopy "^3.0.0" - lodash.keys "^3.0.0" - -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - -lodash._basefor@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" - -lodash._bindcallback@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - -lodash._createassigner@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" - dependencies: - lodash._bindcallback "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash.restparam "^3.0.0" - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - -lodash.assign@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" - dependencies: - lodash._baseassign "^3.0.0" - lodash._createassigner "^3.0.0" - lodash.keys "^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" @@ -4812,49 +4757,14 @@ lodash.endswith@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09" -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - lodash.isfunction@^3.0.8: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" -lodash.isplainobject@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz#9a8238ae16b200432960cd7346512d0123fbf4c5" - dependencies: - lodash._basefor "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.keysin "^3.0.0" - lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" -lodash.istypedarray@^3.0.0: - version "3.0.6" - resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62" - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.keysin@^3.0.0: - version "3.0.8" - resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz#22c4493ebbedb1427962a54b445b2c8a767fb47f" - dependencies: - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4863,30 +4773,10 @@ lodash.memoize@~3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" -lodash.merge@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-3.3.2.tgz#0d90d93ed637b1878437bb3e21601260d7afe994" - dependencies: - lodash._arraycopy "^3.0.0" - lodash._arrayeach "^3.0.0" - lodash._createassigner "^3.0.0" - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - lodash.isplainobject "^3.0.0" - lodash.istypedarray "^3.0.0" - lodash.keys "^3.0.0" - lodash.keysin "^3.0.0" - lodash.toplainobject "^3.0.0" - lodash.mergewith@^4.6.0: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - lodash.startswith@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c" @@ -4895,13 +4785,6 @@ lodash.tail@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" -lodash.toplainobject@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash.toplainobject/-/lodash.toplainobject-3.0.0.tgz#28790ad942d293d78aa663a07ecf7f52ca04198d" - dependencies: - lodash._basecopy "^3.0.0" - lodash.keysin "^3.0.0" - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -4995,10 +4878,6 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -material-design-lite@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/material-design-lite/-/material-design-lite-1.3.0.tgz#d004ce3fee99a1eeb74a78b8a325134a5f1171d3" - math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" @@ -5603,6 +5482,13 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -8073,6 +7959,13 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +util.promisify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + util@0.10.3, util@^0.10.3, util@~0.10.1: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"