diff --git a/.env.template b/.env.template index a9ae63d7..4ba9bcec 100644 --- a/.env.template +++ b/.env.template @@ -10,6 +10,8 @@ START_ROOM_URL=/_/global/maps.workadventure.localhost/Floor0/floor0.json # If you are using Coturn, this is the value of the "static-auth-secret" parameter in your coturn config file. # Keep empty if you are sharing hard coded / clear text credentials. TURN_STATIC_AUTH_SECRET= +DISABLE_NOTIFICATIONS=true +SKIP_RENDER_OPTIMIZATIONS=false # The email address used by Let's encrypt to send renewal warnings (compulsory) ACME_EMAIL= diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 9e22dedc..3bf00b99 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -1,6 +1,8 @@ name: Build, push and deploy Docker image on: + push: + branches: [master, develop] release: types: [created] pull_request: @@ -14,7 +16,7 @@ env: jobs: build-front: - if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }} + if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }} runs-on: ubuntu-latest steps: @@ -34,11 +36,11 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: thecodingmachine/workadventure-front - tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} + tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} add_git_labels: true build-back: - if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }} + if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }} runs-on: ubuntu-latest steps: @@ -57,11 +59,11 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: thecodingmachine/workadventure-back - tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} + tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} add_git_labels: true build-pusher: - if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }} + if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }} runs-on: ubuntu-latest steps: @@ -80,11 +82,11 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: thecodingmachine/workadventure-pusher - tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} + tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} add_git_labels: true build-uploader: - if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }} + if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }} runs-on: ubuntu-latest steps: @@ -103,11 +105,11 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: thecodingmachine/workadventure-uploader - tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} + tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} add_git_labels: true build-maps: - if: ${{ github.event.release || contains(github.event.pull_request.labels.*.name, 'deploy') }} + if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }} runs-on: ubuntu-latest steps: @@ -127,7 +129,7 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: thecodingmachine/workadventure-maps - tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} + tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} add_git_labels: true deeploy: @@ -138,7 +140,7 @@ jobs: - build-maps - build-uploader runs-on: ubuntu-latest - if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }} steps: - name: Checkout @@ -147,6 +149,37 @@ jobs: # Create a slugified value of the branch - uses: rlespinasse/github-slug-action@3.1.0 + - name: Write certificate + run: echo "${CERTS_PRIVATE_KEY}" > secret.key && chmod 0600 secret.key + env: + CERTS_PRIVATE_KEY: ${{ secrets.CERTS_PRIVATE_KEY }} + + - name: Download certificate + run: mkdir secrets && scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i secret.key ubuntu@cert.workadventu.re:./config/live/workadventu.re/* secrets/ + + - name: Create namespace + uses: steebchen/kubectl@v1.0.0 + env: + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_FILE_BASE64 }} + with: + args: create namespace workadventure-${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} + continue-on-error: true + + - name: Delete old certificates in namespace + uses: steebchen/kubectl@v1.0.0 + env: + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_FILE_BASE64 }} + with: + args: -n workadventure-${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} delete secret certificate-tls + continue-on-error: true + + - name: Install certificates in namespace + uses: steebchen/kubectl@v1.0.0 + env: + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_FILE_BASE64 }} + with: + args: -n workadventure-${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} create secret tls certificate-tls --key="secrets/privkey.pem" --cert="secrets/fullchain.pem" + - name: Deploy uses: thecodingmachine/deeployer-action@master env: @@ -156,14 +189,14 @@ jobs: JITSI_URL: ${{ secrets.JITSI_URL }} SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }} TURN_STATIC_AUTH_SECRET: ${{ secrets.TURN_STATIC_AUTH_SECRET }} - DEPLOY_REF: ${{ env.GITHUB_HEAD_REF_SLUG }} + DEPLOY_REF: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} with: - namespace: workadventure-${{ env.GITHUB_HEAD_REF_SLUG }} + namespace: workadventure-${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }} - name: Add a comment in PR uses: unsplash/comment-on-pr@v1.2.0 + if: ${{ github.event_name == 'pull_request' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - msg: Environment deployed at https://play.${{ env.GITHUB_HEAD_REF_SLUG }}.test.workadventu.re - check_for_duplicate_msg: true + msg: Environment deployed at https://play-${{ env.GITHUB_HEAD_REF_SLUG }}.test.workadventu.re diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 38d8c8f0..06c1c58e 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -49,7 +49,7 @@ jobs: - name: "Build" run: yarn run build env: - API_URL: "localhost:8080" + PUSHER_URL: "//localhost:8080" working-directory: "front" - name: "Lint" diff --git a/CHANGELOG.md b/CHANGELOG.md index 3117ea31..1dd2c973 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,38 @@ ## Version 1.3.9 - in dev +### BREAKING CHANGES + +- Scripting API: + - Changed function names: `restorePlayerControl` => `restorePlayerControls`, `disablePlayerControl` => `disablePlayerControls`. + Please keep in mind that the scripting API is still experimental. Some breaking changes can occur in it until we mark it as stable. + ### Updates +- Added the emote feature to WorkAdventure. (@Kharhamel, @Tabascoeye) + - The emote menu can be opened by clicking on your character. + - Clicking on one of its element will close the menu and play an emote above your character. + - This emote can be seen by other players. +- Player names were improved. (@Kharhamel) + - We now create a GameObject.Text instead of GameObject.BitmapText + - now use the 'Press Start 2P' font family and added an outline + - As a result, we can now allow non-standard letters like french accents or chinese characters! + +- Added the contact card feature. (@Kharhamel) + - Click on another player to see its contact info. + - Premium-only feature unfortunately. I need to find a way to make it available for all. + - If no contact data is found (either because the user is anonymous or because no admin backend), display an error card. + - Mobile support has been improved - - WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used - - Mouse wheel support to zoom in / out - - Pinch support on mobile to zoom in / out - - Improved virtual joystick size (adapts to the zoom level) + - WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used + - Mouse wheel support to zoom in / out + - Pinch support on mobile to zoom in / out + - Improved virtual joystick size (adapts to the zoom level) +- Redesigned intermediate scenes + - Redesigned Select Companion scene + - Redesigned Enter Your Name scene + - Added a new `DISPLAY_TERMS_OF_USE` environment variable to trigger the display of terms of use +- New scripting API features: + - Use `WA.loadSound(): Sound` to load / play / stop a sound ### Bug Fixes diff --git a/back/src/Model/Admin.ts b/back/src/Model/Admin.ts index 0be74b85..29b53385 100644 --- a/back/src/Model/Admin.ts +++ b/back/src/Model/Admin.ts @@ -1,9 +1,6 @@ import { - BatchMessage, - PusherToBackMessage, ServerToAdminClientMessage, - ServerToClientMessage, - SubMessage, UserJoinedRoomMessage, UserLeftRoomMessage + UserJoinedRoomMessage, UserLeftRoomMessage } from "../Messages/generated/messages_pb"; import {AdminSocket} from "../RoomManager"; diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 4436fb60..53d0a855 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -2,12 +2,12 @@ import {PointInterface} from "./Websocket/PointInterface"; import {Group} from "./Group"; import {User, UserSocket} from "./User"; import {PositionInterface} from "_Model/PositionInterface"; -import {EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone"; +import {EmoteCallback, EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone"; import {PositionNotifier} from "./PositionNotifier"; import {Movable} from "_Model/Movable"; import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "./RoomIdentifier"; import {arrayIntersect} from "../Services/ArrayHelper"; -import {JoinRoomMessage} from "../Messages/generated/messages_pb"; +import {EmoteEventMessage, JoinRoomMessage} from "../Messages/generated/messages_pb"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; import {ZoneSocket} from "src/RoomManager"; import {Admin} from "../Model/Admin"; @@ -51,8 +51,9 @@ export class GameRoom { groupRadius: number, onEnters: EntersCallback, onMoves: MovesCallback, - onLeaves: LeavesCallback) - { + onLeaves: LeavesCallback, + onEmote: EmoteCallback, + ) { this.roomId = roomId; if (isRoomAnonymous(roomId)) { @@ -74,7 +75,7 @@ export class GameRoom { this.minDistance = minDistance; this.groupRadius = groupRadius; // A zone is 10 sprites wide. - this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves); + this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves, onEmote); } public getGroups(): Group[] { @@ -88,7 +89,10 @@ export class GameRoom { public getUserByUuid(uuid: string): User|undefined { return this.usersByUuid.get(uuid); } - + public getUserById(id: number): User|undefined { + return this.users.get(id); + } + public join(socket : UserSocket, joinRoomMessage: JoinRoomMessage): User { const positionMessage = joinRoomMessage.getPositionmessage(); if (positionMessage === undefined) { @@ -104,6 +108,7 @@ export class GameRoom { this.positionNotifier, socket, joinRoomMessage.getTagList(), + joinRoomMessage.getVisitcardurl(), joinRoomMessage.getName(), ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()), joinRoomMessage.getCompanion() @@ -325,4 +330,8 @@ export class GameRoom { this.versionNumber++ return this.versionNumber; } + + public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) { + this.positionNotifier.emitEmoteEvent(user, emoteEventMessage); + } } diff --git a/back/src/Model/PositionNotifier.ts b/back/src/Model/PositionNotifier.ts index 6eff17a3..275bf9d0 100644 --- a/back/src/Model/PositionNotifier.ts +++ b/back/src/Model/PositionNotifier.ts @@ -8,10 +8,12 @@ * The PositionNotifier is important for performance. It allows us to send the position of players only to a restricted * number of players around the current player. */ -import {EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone"; +import {EmoteCallback, EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone"; import {Movable} from "_Model/Movable"; import {PositionInterface} from "_Model/PositionInterface"; import {ZoneSocket} from "../RoomManager"; +import {User} from "_Model/User"; +import {EmoteEventMessage} from "../Messages/generated/messages_pb"; interface ZoneDescriptor { i: number; @@ -24,7 +26,7 @@ export class PositionNotifier { private zones: Zone[][] = []; - constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: EntersCallback, private onUserMoves: MovesCallback, private onUserLeaves: LeavesCallback) { + constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: EntersCallback, private onUserMoves: MovesCallback, private onUserLeaves: LeavesCallback, private onEmote: EmoteCallback) { } private getZoneDescriptorFromCoordinates(x: number, y: number): ZoneDescriptor { @@ -77,7 +79,7 @@ export class PositionNotifier { let zone = this.zones[j][i]; if (zone === undefined) { - zone = new Zone(this.onUserEnters, this.onUserMoves, this.onUserLeaves, i, j); + zone = new Zone(this.onUserEnters, this.onUserMoves, this.onUserLeaves, this.onEmote, i, j); this.zones[j][i] = zone; } return zone; @@ -93,4 +95,11 @@ export class PositionNotifier { const zone = this.getZone(x, y); zone.removeListener(call); } + + public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) { + const zoneDesc = this.getZoneDescriptorFromCoordinates(user.getPosition().x, user.getPosition().y); + const zone = this.getZone(zoneDesc.i, zoneDesc.j); + zone.emitEmoteEvent(emoteEventMessage); + + } } diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 52a96b61..4a3e75ec 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -22,6 +22,7 @@ export class User implements Movable { private positionNotifier: PositionNotifier, public readonly socket: UserSocket, public readonly tags: string[], + public readonly visitCardUrl: string|null, public readonly name: string, public readonly characterLayers: CharacterLayer[], public readonly companion?: CompanionMessage diff --git a/back/src/Model/Zone.ts b/back/src/Model/Zone.ts index ca695317..ffb172bb 100644 --- a/back/src/Model/Zone.ts +++ b/back/src/Model/Zone.ts @@ -3,21 +3,19 @@ import {PositionInterface} from "_Model/PositionInterface"; import {Movable} from "./Movable"; import {Group} from "./Group"; import {ZoneSocket} from "../RoomManager"; +import {EmoteEventMessage} from "../Messages/generated/messages_pb"; export type EntersCallback = (thing: Movable, fromZone: Zone|null, listener: ZoneSocket) => void; export type MovesCallback = (thing: Movable, position: PositionInterface, listener: ZoneSocket) => void; export type LeavesCallback = (thing: Movable, newZone: Zone|null, listener: ZoneSocket) => void; +export type EmoteCallback = (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => void; export class Zone { private things: Set = new Set(); private listeners: Set = new Set(); - - /** - * @param x For debugging purpose only - * @param y For debugging purpose only - */ - constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, public readonly x: number, public readonly y: number) { - } + + + constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, private onEmote: EmoteCallback, public readonly x: number, public readonly y: number) { } /** * A user/thing leaves the zone @@ -41,9 +39,7 @@ export class Zone { */ private notifyLeft(thing: Movable, newZone: Zone|null) { for (const listener of this.listeners) { - //if (listener !== thing && (newZone === null || !listener.listenedZones.has(newZone))) { - this.onLeaves(thing, newZone, listener); - //} + this.onLeaves(thing, newZone, listener); } } @@ -57,15 +53,6 @@ export class Zone { */ private notifyEnter(thing: Movable, oldZone: Zone|null, position: PositionInterface) { for (const listener of this.listeners) { - - /*if (listener === thing) { - continue; - } - if (oldZone === null || !listener.listenedZones.has(oldZone)) { - this.onEnters(thing, listener); - } else { - this.onMoves(thing, position, listener); - }*/ this.onEnters(thing, oldZone, listener); } } @@ -85,28 +72,6 @@ export class Zone { } } - /*public startListening(listener: User): void { - for (const thing of this.things) { - if (thing !== listener) { - this.onEnters(thing, listener); - } - } - - this.listeners.add(listener); - listener.listenedZones.add(this); - } - - public stopListening(listener: User): void { - for (const thing of this.things) { - if (thing !== listener) { - this.onLeaves(thing, listener); - } - } - - this.listeners.delete(listener); - listener.listenedZones.delete(this); - }*/ - public getThings(): Set { return this.things; } @@ -119,4 +84,11 @@ export class Zone { public removeListener(socket: ZoneSocket): void { this.listeners.delete(socket); } + + public emitEmoteEvent(emoteEventMessage: EmoteEventMessage) { + for (const listener of this.listeners) { + this.onEmote(emoteEventMessage, listener); + } + + } } diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 54215698..19266687 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -5,6 +5,7 @@ import { AdminPusherToBackMessage, AdminRoomMessage, BanMessage, + EmotePromptMessage, EmptyMessage, ItemEventMessage, JoinRoomMessage, @@ -71,6 +72,8 @@ const roomManager: IRoomManagerServer = { socketManager.emitPlayGlobalMessage(room, message.getPlayglobalmessage() as PlayGlobalMessage); } else if (message.hasQueryjitsijwtmessage()){ socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage); + } else if (message.hasEmotepromptmessage()){ + socketManager.handleEmoteEventMessage(room, user, message.getEmotepromptmessage() as EmotePromptMessage); }else if (message.hasSendusermessage()) { const sendUserMessage = message.getSendusermessage(); if(sendUserMessage !== undefined) { diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 647afc95..a56a1ac4 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -26,7 +26,8 @@ import { GroupLeftZoneMessage, WorldFullWarningMessage, UserLeftZoneMessage, - BanUserMessage, RefreshRoomMessage, + EmoteEventMessage, + BanUserMessage, RefreshRoomMessage, EmotePromptMessage, } from "../Messages/generated/messages_pb"; import {User, UserSocket} from "../Model/User"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; @@ -67,6 +68,7 @@ export class SocketManager { private rooms: Map = new Map(); constructor() { + clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => { gaugeManager.incNbClientPerRoomGauge(roomId); }); @@ -263,7 +265,8 @@ export class SocketManager { GROUP_RADIUS, (thing: Movable, fromZone: Zone|null, listener: ZoneSocket) => this.onZoneEnter(thing, fromZone, listener), (thing: Movable, position:PositionInterface, listener: ZoneSocket) => this.onClientMove(thing, position, listener), - (thing: Movable, newZone: Zone|null, listener: ZoneSocket) => this.onClientLeave(thing, newZone, listener) + (thing: Movable, newZone: Zone|null, listener: ZoneSocket) => this.onClientLeave(thing, newZone, listener), + (emoteEventMessage:EmoteEventMessage, listener: ZoneSocket) => this.onEmote(emoteEventMessage, listener), ); gaugeManager.incNbRoomGauge(); this.rooms.set(roomId, world); @@ -296,6 +299,9 @@ export class SocketManager { userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone)); + if (thing.visitCardUrl) { + userJoinedZoneMessage.setVisitcardurl(thing.visitCardUrl); + } userJoinedZoneMessage.setCompanion(thing.companion); const subMessage = new SubToPusherMessage(); @@ -339,6 +345,14 @@ export class SocketManager { } } + + private onEmote(emoteEventMessage: EmoteEventMessage, client: ZoneSocket) { + const subMessage = new SubToPusherMessage(); + subMessage.setEmoteeventmessage(emoteEventMessage); + + emitZoneMessage(subMessage, client); + } + private emitCreateUpdateGroupEvent(client: ZoneSocket, fromZone: Zone|null, group: Group): void { const position = group.getPosition(); const pointMessage = new PointMessage(); @@ -593,6 +607,9 @@ export class SocketManager { userJoinedMessage.setName(thing.name); userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); + if (thing.visitCardUrl) { + userJoinedMessage.setVisitcardurl(thing.visitCardUrl); + } userJoinedMessage.setCompanion(thing.companion); const subMessage = new SubToPusherMessage(); @@ -751,6 +768,13 @@ export class SocketManager { recipient.socket.write(clientMessage); }); } + + handleEmoteEventMessage(room: GameRoom, user: User, emotePromptMessage: EmotePromptMessage) { + const emoteEventMessage = new EmoteEventMessage(); + emoteEventMessage.setEmote(emotePromptMessage.getEmote()); + emoteEventMessage.setActoruserid(user.id); + room.emitEmoteEvent(user, emoteEventMessage); + } } export const socketManager = new SocketManager(); diff --git a/back/tests/GameRoomTest.ts b/back/tests/GameRoomTest.ts index 45721334..6bdc6912 100644 --- a/back/tests/GameRoomTest.ts +++ b/back/tests/GameRoomTest.ts @@ -5,6 +5,7 @@ import {Group} from "../src/Model/Group"; import {User, UserSocket} from "_Model/User"; import {JoinRoomMessage, PositionMessage} from "../src/Messages/generated/messages_pb"; import Direction = PositionMessage.Direction; +import {EmoteCallback} from "_Model/Zone"; function createMockUser(userId: number): User { return { @@ -33,6 +34,8 @@ function createJoinRoomMessage(uuid: string, x: number, y: number): JoinRoomMess return joinRoomMessage; } +const emote: EmoteCallback = (emoteEventMessage, listener): void => {} + describe("GameRoom", () => { it("should connect user1 and user2", () => { let connectCalledNumber: number = 0; @@ -43,7 +46,8 @@ describe("GameRoom", () => { } - const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote); @@ -72,7 +76,7 @@ describe("GameRoom", () => { } - const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote); const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100)); @@ -101,7 +105,7 @@ describe("GameRoom", () => { disconnectCallNumber++; } - const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote); const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100)); diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 5901202f..1aaf2e13 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -1,10 +1,6 @@ import "jasmine"; -import {GameRoom, ConnectCallback, DisconnectCallback } from "_Model/GameRoom"; -import {Point} from "../src/Model/Websocket/MessageUserPosition"; -import { Group } from "../src/Model/Group"; import {PositionNotifier} from "../src/Model/PositionNotifier"; import {User, UserSocket} from "../src/Model/User"; -import {PointInterface} from "../src/Model/Websocket/PointInterface"; import {Zone} from "_Model/Zone"; import {Movable} from "_Model/Movable"; import {PositionInterface} from "_Model/PositionInterface"; @@ -23,21 +19,21 @@ describe("PositionNotifier", () => { moveTriggered = true; }, (thing: Movable) => { leaveTriggered = true; - }); + }, () => {}); const user1 = new User(1, 'test', '10.0.0.2', { x: 500, y: 500, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: -9999, y: -9999, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); positionNotifier.addZoneListener({} as ZoneSocket, 0, 0); positionNotifier.addZoneListener({} as ZoneSocket, 0, 1); @@ -98,21 +94,21 @@ describe("PositionNotifier", () => { moveTriggered = true; }, (thing: Movable) => { leaveTriggered = true; - }); + }, () => {}); const user1 = new User(1, 'test', '10.0.0.2', { x: 500, y: 500, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: 0, y: 0, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const listener = {} as ZoneSocket; positionNotifier.addZoneListener(listener, 0, 0); diff --git a/back/yarn.lock b/back/yarn.lock index 43f58988..8af760c8 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -1251,9 +1251,9 @@ has-values@^1.0.0: kind-of "^4.0.0" hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== http-errors@1.7.2: version "1.7.2" @@ -1704,9 +1704,9 @@ lodash.once@^4.0.0: integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== long@~3: version "3.2.0" diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 8d4db6cf..ba52caf2 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -209,9 +209,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "requires": { "is-glob": "^4.0.1" } @@ -230,9 +230,9 @@ } }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "indent-string": { "version": "2.1.0", diff --git a/benchmark/yarn.lock b/benchmark/yarn.lock index d93e3667..a7f9ec0d 100644 --- a/benchmark/yarn.lock +++ b/benchmark/yarn.lock @@ -148,8 +148,8 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" dependencies: is-glob "^4.0.1" @@ -169,8 +169,8 @@ graceful-fs@^4.1.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" indent-string@^2.1.0: version "2.1.0" diff --git a/deeployer.libsonnet b/deeployer.libsonnet index 7ed9db8c..8d9c2bfd 100644 --- a/deeployer.libsonnet +++ b/deeployer.libsonnet @@ -2,7 +2,7 @@ local env = std.extVar("env"), local namespace = env.DEPLOY_REF, local tag = namespace, - local url = if namespace == "master" then "workadventu.re" else namespace+".test.workadventu.re", + local url = namespace+".test.workadventu.re", // develop branch does not use admin because of issue with SSL certificate of admin as of now. local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://"+url else null, "$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json", @@ -11,8 +11,7 @@ "back1": { "image": "thecodingmachine/workadventure-back:"+tag, "host": { - "url": "api1."+url, - "https": "enable", + "url": "api1-"+url, "containerPort": 8080 }, "ports": [8080, 50051], @@ -25,16 +24,12 @@ "TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET, } + (if adminUrl != null then { "ADMIN_API_URL": adminUrl, - } else {}) + if namespace != "master" then { - // Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids! - "NODE_TLS_REJECT_UNAUTHORIZED": "0" - } + } else {}) }, "back2": { "image": "thecodingmachine/workadventure-back:"+tag, "host": { - "url": "api2."+url, - "https": "enable", + "url": "api2-"+url, "containerPort": 8080 }, "ports": [8080, 50051], @@ -47,17 +42,13 @@ "TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET, } + (if adminUrl != null then { "ADMIN_API_URL": adminUrl, - } else {}) + if namespace != "master" then { - // Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids! - "NODE_TLS_REJECT_UNAUTHORIZED": "0" - } + } else {}) }, "pusher": { "replicas": 2, "image": "thecodingmachine/workadventure-pusher:"+tag, "host": { - "url": "pusher."+url, - "https": "enable" + "url": "pusher-"+url, }, "ports": [8080], "env": { @@ -69,35 +60,30 @@ "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, } + (if adminUrl != null then { "ADMIN_API_URL": adminUrl, - } else {}) + if namespace != "master" then { - // Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids! - "NODE_TLS_REJECT_UNAUTHORIZED": "0" - } + } else {}) }, "front": { "image": "thecodingmachine/workadventure-front:"+tag, "host": { - "url": "play."+url, - "https": "enable" + "url": "play-"+url, }, "ports": [80], "env": { - "PUSHER_URL": "//pusher."+url, - "UPLOADER_URL": "//uploader."+url, + "PUSHER_URL": "//pusher-"+url, + "UPLOADER_URL": "//uploader-"+url, "ADMIN_URL": "//"+url, "JITSI_URL": env.JITSI_URL, "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, "TURN_SERVER": "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443", "JITSI_PRIVATE_MODE": if env.SECRET_JITSI_KEY != '' then "true" else "false", - "START_ROOM_URL": "/_/global/maps."+url+"/Floor0/floor0.json" + "START_ROOM_URL": "/_/global/maps-"+url+"/Floor0/floor0.json" //"GA_TRACKING_ID": "UA-10196481-11" } }, "uploader": { "image": "thecodingmachine/workadventure-uploader:"+tag, "host": { - "url": "uploader."+url, - "https": "enable", + "url": "uploader-"+url, "containerPort": 8080 }, "ports": [8080], @@ -107,16 +93,12 @@ "maps": { "image": "thecodingmachine/workadventure-maps:"+tag, "host": { - "url": "maps."+url, - "https": "enable" + "url": "maps-"+url }, "ports": [80] }, }, "config": { - "https": { - "mail": "d.negrier@thecodingmachine.com" - }, k8sextension(k8sConf):: k8sConf + { back1+: { @@ -131,6 +113,14 @@ } } } + }, + ingress+: { + spec+: { + tls+: [{ + hosts: ["api1-"+url], + secretName: "certificate-tls" + }] + } } }, back2+: { @@ -145,6 +135,14 @@ } } } + }, + ingress+: { + spec+: { + tls+: [{ + hosts: ["api2-"+url], + secretName: "certificate-tls" + }] + } } }, pusher+: { @@ -159,8 +157,46 @@ } } } - } - } + }, + ingress+: { + spec+: { + tls+: [{ + hosts: ["pusher-"+url], + secretName: "certificate-tls" + }] + } + } + }, + front+: { + ingress+: { + spec+: { + tls+: [{ + hosts: ["play-"+url], + secretName: "certificate-tls" + }] + } + } + }, + uploader+: { + ingress+: { + spec+: { + tls+: [{ + hosts: ["uploader-"+url], + secretName: "certificate-tls" + }] + } + } + }, + maps+: { + ingress+: { + spec+: { + tls+: [{ + hosts: ["maps-"+url], + secretName: "certificate-tls" + }] + } + } + }, } } } diff --git a/docker-compose.single-domain.yaml b/docker-compose.single-domain.yaml index 81305671..345ccf8d 100644 --- a/docker-compose.single-domain.yaml +++ b/docker-compose.single-domain.yaml @@ -33,6 +33,8 @@ services: STARTUP_COMMAND_1: ./templater.sh STARTUP_COMMAND_2: yarn install TURN_SERVER: "turn:localhost:3478,turns:localhost:5349" + DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS" + SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS" # Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials. # Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container TURN_USER: "" diff --git a/docker-compose.yaml b/docker-compose.yaml index cea1bc03..1c1bcb8f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -33,6 +33,8 @@ services: STARTUP_COMMAND_2: yarn install STUN_SERVER: "stun:stun.l.google.com:19302" TURN_SERVER: "turn:coturn.workadventure.localhost:3478,turns:coturn.workadventure.localhost:5349" + DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS" + SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS" # Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials. # Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container TURN_USER: "" diff --git a/docs/maps/api-reference.md b/docs/maps/api-reference.md new file mode 100644 index 00000000..9891a88a --- /dev/null +++ b/docs/maps/api-reference.md @@ -0,0 +1,237 @@ +{.section-title.accent.text-primary} +# API Reference + +### Sending a message in the chat + +``` +sendChatMessage(message: string, author: string): void +``` + +Sends a message in the chat. The message is only visible in the browser of the current user. + +* **message**: the message to be displayed in the chat +* **author**: the name displayed for the author of the message. It does not have to be a real user. + +Example: + +```javascript +WA.sendChatMessage('Hello world', 'Mr Robot'); +``` + +### Listening to messages from the chat + +```javascript +onChatMessage(callback: (message: string) => void): void +``` + +Listens to messages typed by the current user and calls the callback. Messages from other users in the chat cannot be listened to. + +* **callback**: the function that will be called when a message is received. It contains the message typed by the user. + +Example: + +```javascript +WA.onChatMessage((message => { + console.log('The user typed a message', message); +})); +``` + +### Detecting when the user enters/leaves a zone + +``` +onEnterZone(name: string, callback: () => void): void +onLeaveZone(name: string, callback: () => void): void +``` + +Listens to the position of the current user. The event is triggered when the user enters or leaves a given zone. The name of the zone is stored in the map, on a dedicated layer with the `zone` property. + +
+
+ +
The `zone` property, applied on a layer
+
+
+ +* **name**: the name of the zone, as defined in the `zone` property. +* **callback**: the function that will be called when a user enters or leaves the zone. + +Example: + +```javascript +WA.onEnterZone('myZone', () => { + WA.sendChatMessage("Hello!", 'Mr Robot'); +}) + +WA.onLeaveZone('myZone', () => { + WA.sendChatMessage("Goodbye!", 'Mr Robot'); +}) +``` + +### Opening a popup + +In order to open a popup window, you must first define the position of the popup on your map. + +You can position this popup by using a "rectangle" object in Tiled that you will place on an "object" layer. + +
+
+ +
+
+ +
+
+ +``` +openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup +``` + +* **targetObject**: the name of the rectangle object defined in Tiled. +* **message**: the message to display in the popup. +* **buttons**: an array of action buttons defined underneath the popup. + +Action buttons are `ButtonDescriptor` objects containing these properties. + +* **label (_string_)**: The label of the button. +* **className (_string_)**: The visual type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled". +* **callback (_(popup: Popup)=>void_)**: Callback called when the button is pressed. + +Please note that `openPopup` returns an object of the `Popup` class. Also, the callback called when a button is clicked is passed a `Popup` object. + +The `Popup` class that represents an open popup contains a single method: `close()`. This will obviously close the popup when called. + +```javascript +class Popup { + /** + * Closes the popup + */ + close() {}; +} +``` + +Example: + +```javascript +let helloWorldPopup; + +// Open the popup when we enter a given zone +helloWorldPopup = WA.onEnterZone('myZone', () => { + WA.openPopup("popupRectangle", 'Hello world!', [{ + label: "Close", + className: "primary", + callback: (popup) => { + // Close the popup when the "Close" button is pressed. + popup.close(); + } + }); +}]); + +// Close the popup when we leave the zone. +WA.onLeaveZone('myZone', () => { + helloWorldPopup.close(); +}); +``` + +### Disabling / restoring controls + +``` +disablePlayerControls(): void +restorePlayerControls(): void +``` + +These 2 methods can be used to completely disable player controls and to enable them again. + +When controls are disabled, the user cannot move anymore using keyboard input. This can be useful in a "First Time User Experience" part, to display an important message to a user before letting him/her move again. + +Example: + +```javascript +WA.onEnterZone('myZone', () => { + WA.disablePlayerControls(); + WA.openPopup("popupRectangle", 'This is an imporant message!', [{ + label: "Got it!", + className: "primary", + callback: (popup) => { + WA.restorePlayerControls(); + popup.close(); + } + }]); +}); +``` + +### Opening a web page in a new tab + +``` +openTab(url: string): void +``` + +Opens the webpage at "url" in your browser, in a new tab. + +Example: + +```javascript +WA.openTab('https://www.wikipedia.org/'); +``` + +### Opening a web page in the current tab + +``` +goToPage(url: string): void +``` + +Opens the webpage at "url" in your browser in place of WorkAdventure. WorkAdventure will be completely unloaded. + +Example: + +```javascript +WA.goToPage('https://www.wikipedia.org/'); +``` + +### Opening/closing a web page in an iFrame + +``` +openCoWebSite(url: string): void +closeCoWebSite(): void +``` + +Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. + +Example: + +```javascript +WA.openCoWebSite('https://www.wikipedia.org/'); +// ... +WA.closeCoWebSite(); +``` + +### Load a sound from an url + +``` +loadSound(url: string): Sound +``` + +Load a sound from an url + +Please note that `loadSound` returns an object of the `Sound` class + +The `Sound` class that represents a loaded sound contains two methods: `play(soundConfig : SoundConfig|undefined)` and `stop()` + +The parameter soundConfig is optional, if you call play without a Sound config the sound will be played with the basic configuration. + +Example: + +```javascript +var mySound = WA.loadSound("Sound.ogg"); +var config = { + volume : 0.5, + loop : false, + rate : 1, + detune : 1, + delay : 0, + seek : 0, + mute : false +} +mySound.play(config); +// ... +mySound.stop(); +``` diff --git a/docs/maps/scripting.md b/docs/maps/scripting.md new file mode 100644 index 00000000..b9dee484 --- /dev/null +++ b/docs/maps/scripting.md @@ -0,0 +1,117 @@ +{.alert.alert-danger style="width:80%"} +This feature is "_experimental_". We may apply changes in the near future to the way it works when we gather some feedback. + +{.section-title.accent.text-primary} +# Scripting WorkAdventure maps + +Do you want to add a bit of intelligence to your map? Scripts allow you to create maps with special features. + +You can for instance: + +* Create FTUE (First Time User Experience) scenarios where a first-time user will be displayed a notification popup. +* Create NPC (non playing characters) and interact with those characters using the chat. +* Organize interactions between an iframe and your map (for instance, walking on a special zone might add a product in the cart of an eCommerce website...) +* etc... + +Please note that scripting in WorkAdventure is at an early stage of development and that more features might be added in the future. You can actually voice your opinion about useful features by adding [an issue on Github](https://github.com/thecodingmachine/workadventure/issues). + +{.alert.alert-warning} +**Beware:** Scripts are executed in the browser of the current user only. Generally speaking, scripts cannot be used to trigger a change that will be displayed on other users screen. + +## Scripting language + +Client-side scripting is done in **Javascript** (or any language that transpiles to Javascript like _Typescript_). + +There are 2 ways you can use the scripting language: + +* **In the map**: By directly referring a Javascript file inside your map, in the `script` property of your map. +* **In an iFrame**: By placing your Javascript script into an iFrame, your script can communicate with the WorkAdventure game + +## Adding a script in the map + +Create a `script` property in your map. + +In Tiled, in order to access your map properties, you can click on _"Map > Map properties"_. + +
+
+ +
The Map properties menu
+
+
+ +Create a `script` property (a "string"), and put the URL of your script. + +You can put relative URLs. If your script file is next to your map, you can simply write the name of the script file here. + +
+
+ +
The script property
+
+
+ +Start by testing this with a simple message sent to the chat. + +**script.js** +```javascript +WA.sendChatMessage('Hello world', 'Mr Robot'); +``` + +The `WA` objects contains a number of useful methods enabling you to interact with the WorkAdventure game. For instance, `WA.sendChatMessage` opens the chat and adds a message in it. + +In your browser console, when you open the map, the chat message should be displayed right away. + +## Adding a script in an iFrame + +In WorkAdventure, you can easily [open an iFrame using the `openWebsite` property on a layer](special-zones). However, by default, the iFrame is not allowed to communicate with WorkAdventure. + +This is done to improve security. In order to be able to execute a script that communicates with WorkAdventure inside an iFrame, you have to **explicitly allow the iFrame to use the "iFrame API"**. + +In order to allow communication with WorkAdventure, you need to add an additional property: `openWebsiteAllowApi`. This property must be _boolean_ and you must set it to "true". + +
+
+ +
The `openWebsiteAllowApi` property
+
+
+ +In your iFrame HTML page, you now need to import the _WorkAdventure client API Javascript library_. This library contains the `WA` object that you can use to communicate with WorkAdventure. + +The library is available at `https://play.workadventu.re/iframe_api.js`. + +_Note:_ if you are using a self-hosted version of WorkAdventure, use `https://[front_domain]/iframe_api.js` + +**iframe.html** +```html + + + + + + + + +``` + +You can now start by testing this with a simple message sent to the chat. + +**iframe.html** +```html +... + +... +``` + +Let's now review the complete list of methods available in this `WA` object. + +## Using Typescript + +View the dedicated page about [using Typescript with the scripting API](using-typescript). + +## Available features in the client API + +The list of available functions and features is [available in the API Reference page, with examples](api-reference). diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 3aab37d9..037fddae 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -25,6 +25,15 @@ ], "rules": { "no-unused-vars": "off", - "@typescript-eslint/no-explicit-any": "error" + "@typescript-eslint/no-explicit-any": "error", + + // TODO: remove those ignored rules and write a stronger code! + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/restrict-plus-operands": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/restrict-template-expressions": "off" } } diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index c4763b6e..aa63229f 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -29,7 +29,6 @@ - WorkAdventure @@ -38,6 +37,8 @@
+
+
@@ -45,33 +46,6 @@ -
-
- -
- - - - - -
-
-
-
-
- - -
-
- - -
-
- - -
-
-
-
-
- -
-
- - -
- - diff --git a/front/dist/resources/emotes/clap-emote.png b/front/dist/resources/emotes/clap-emote.png new file mode 100644 index 00000000..a64f2e5f Binary files /dev/null and b/front/dist/resources/emotes/clap-emote.png differ diff --git a/front/dist/resources/emotes/hand-emote.png b/front/dist/resources/emotes/hand-emote.png new file mode 100644 index 00000000..3bc01acf Binary files /dev/null and b/front/dist/resources/emotes/hand-emote.png differ diff --git a/front/dist/resources/emotes/heart-emote.png b/front/dist/resources/emotes/heart-emote.png new file mode 100644 index 00000000..867d6be1 Binary files /dev/null and b/front/dist/resources/emotes/heart-emote.png differ diff --git a/front/dist/resources/emotes/thanks-emote.png b/front/dist/resources/emotes/thanks-emote.png new file mode 100644 index 00000000..8e326ed5 Binary files /dev/null and b/front/dist/resources/emotes/thanks-emote.png differ diff --git a/front/dist/resources/emotes/thumb-down-emote.png b/front/dist/resources/emotes/thumb-down-emote.png new file mode 100644 index 00000000..8ec7c961 Binary files /dev/null and b/front/dist/resources/emotes/thumb-down-emote.png differ diff --git a/front/dist/resources/emotes/thumb-up-emote.png b/front/dist/resources/emotes/thumb-up-emote.png new file mode 100644 index 00000000..eecb0e57 Binary files /dev/null and b/front/dist/resources/emotes/thumb-up-emote.png differ diff --git a/front/dist/resources/fonts/fonts.css b/front/dist/resources/fonts/fonts.css new file mode 100644 index 00000000..a3d3cf71 --- /dev/null +++ b/front/dist/resources/fonts/fonts.css @@ -0,0 +1,5 @@ +/*This file is a workaround to allow phaser to load directly this font */ +@font-face { + font-family: "Press Start 2P"; + src: url("/fonts/press-start-2p-latin-400-normal.woff2") format('woff2'); +} \ No newline at end of file diff --git a/front/dist/resources/html/CustomCharacterScene.html b/front/dist/resources/html/CustomCharacterScene.html deleted file mode 100644 index 0bc050ea..00000000 --- a/front/dist/resources/html/CustomCharacterScene.html +++ /dev/null @@ -1,160 +0,0 @@ - - - diff --git a/front/dist/resources/html/EnableCameraScene.html b/front/dist/resources/html/EnableCameraScene.html deleted file mode 100644 index 2dda6cc1..00000000 --- a/front/dist/resources/html/EnableCameraScene.html +++ /dev/null @@ -1,129 +0,0 @@ - - - diff --git a/front/dist/resources/html/SelectCompanionScene.html b/front/dist/resources/html/SelectCompanionScene.html deleted file mode 100644 index cffa7880..00000000 --- a/front/dist/resources/html/SelectCompanionScene.html +++ /dev/null @@ -1,134 +0,0 @@ - - - diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html index 6abf2753..26be2a1c 100644 --- a/front/dist/resources/html/gameMenu.html +++ b/front/dist/resources/html/gameMenu.html @@ -1,7 +1,4 @@ - - diff --git a/front/dist/resources/html/loginScene.html b/front/dist/resources/html/loginScene.html deleted file mode 100644 index 38e798e5..00000000 --- a/front/dist/resources/html/loginScene.html +++ /dev/null @@ -1,120 +0,0 @@ - - - diff --git a/front/dist/resources/html/selectCharacterScene.html b/front/dist/resources/html/selectCharacterScene.html deleted file mode 100644 index c51731df..00000000 --- a/front/dist/resources/html/selectCharacterScene.html +++ /dev/null @@ -1,142 +0,0 @@ - - - diff --git a/front/dist/resources/html/warningContainer.html b/front/dist/resources/html/warningContainer.html index 4989c49d..832ac4da 100644 --- a/front/dist/resources/html/warningContainer.html +++ b/front/dist/resources/html/warningContainer.html @@ -1,7 +1,4 @@ \ No newline at end of file diff --git a/front/src/Components/EnableCamera/EnableCameraScene.svelte b/front/src/Components/EnableCamera/EnableCameraScene.svelte new file mode 100644 index 00000000..605a1458 --- /dev/null +++ b/front/src/Components/EnableCamera/EnableCameraScene.svelte @@ -0,0 +1,229 @@ + + +
+
+

Turn on your camera and microphone

+
+ {#if $localStreamStore.stream} + + {:else } +
+ +
+ {/if} + + +
+ + {#if $cameraListStore.length > 1 } +
+ Camera +
+ +
+
+ {/if} + + {#if $microphoneListStore.length > 1 } +
+ Microphone +
+ +
+
+ {/if} + +
+
+ +
+
+ + + diff --git a/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte b/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte new file mode 100644 index 00000000..84352ebb --- /dev/null +++ b/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte @@ -0,0 +1,82 @@ + + + +
+ {#each [...Array(NB_BARS).keys()] as i} +
+ {/each} +
+ + diff --git a/front/src/Components/HelpCameraSettings/HelpCameraSettingsPopup.svelte b/front/src/Components/HelpCameraSettings/HelpCameraSettingsPopup.svelte new file mode 100644 index 00000000..8f4de785 --- /dev/null +++ b/front/src/Components/HelpCameraSettings/HelpCameraSettingsPopup.svelte @@ -0,0 +1,73 @@ + + +
+
+

Camera / Microphone access needed

+

Permission denied

+

You must allow camera and microphone access in your browser.

+

+ {#if isFirefox } +

Please click the "Remember this decision" checkbox, if you don't want Firefox to keep asking you the authorization.

+ + {:else if isChrome && !isAndroid } + + {/if} +

+
+
+ + +
+
+ + + diff --git a/front/dist/resources/objects/help-setting-camera-permission-chrome.png b/front/src/Components/HelpCameraSettings/images/help-setting-camera-permission-chrome.png similarity index 100% rename from front/dist/resources/objects/help-setting-camera-permission-chrome.png rename to front/src/Components/HelpCameraSettings/images/help-setting-camera-permission-chrome.png diff --git a/front/dist/resources/objects/help-setting-camera-permission-firefox.png b/front/src/Components/HelpCameraSettings/images/help-setting-camera-permission-firefox.png similarity index 100% rename from front/dist/resources/objects/help-setting-camera-permission-firefox.png rename to front/src/Components/HelpCameraSettings/images/help-setting-camera-permission-firefox.png diff --git a/front/src/Components/Login/LoginScene.svelte b/front/src/Components/Login/LoginScene.svelte new file mode 100644 index 00000000..dbe3daaf --- /dev/null +++ b/front/src/Components/Login/LoginScene.svelte @@ -0,0 +1,123 @@ + + +
+
+ WorkAdventure logo +
+
+

Enter your name

+
+ {startValidating = true}} class:is-error={name.trim() === '' && startValidating} /> +
+ {#if name.trim() === '' && startValidating } +

The name is empty

+ {/if} +
+ + {#if DISPLAY_TERMS_OF_USE} +
+

By continuing, you are agreeing our terms of use, privacy policy and cookie policy.

+
+ {/if} +
+ +
+
+ + diff --git a/front/src/Components/Menu/MenuIcon.svelte b/front/src/Components/Menu/MenuIcon.svelte new file mode 100644 index 00000000..241bf45f --- /dev/null +++ b/front/src/Components/Menu/MenuIcon.svelte @@ -0,0 +1,33 @@ + + +
+
+ +
+
+ + diff --git a/front/src/Components/MyCamera.svelte b/front/src/Components/MyCamera.svelte new file mode 100644 index 00000000..3ff88d89 --- /dev/null +++ b/front/src/Components/MyCamera.svelte @@ -0,0 +1,46 @@ + + + +
+
+ + +
+
diff --git a/front/src/Components/SelectCompanion/SelectCompanionScene.svelte b/front/src/Components/SelectCompanion/SelectCompanionScene.svelte new file mode 100644 index 00000000..205a18ee --- /dev/null +++ b/front/src/Components/SelectCompanion/SelectCompanionScene.svelte @@ -0,0 +1,87 @@ + + +
+
+

Select your companion

+ + +
+
+ + +
+
+ + diff --git a/front/src/Components/SoundMeterWidget.svelte b/front/src/Components/SoundMeterWidget.svelte new file mode 100644 index 00000000..40c467b1 --- /dev/null +++ b/front/src/Components/SoundMeterWidget.svelte @@ -0,0 +1,53 @@ + + + +
+ 1}> + 2}> + 3}> + 4}> + 5}> +
diff --git a/front/src/Components/UI/AudioPlaying.svelte b/front/src/Components/UI/AudioPlaying.svelte new file mode 100644 index 00000000..8889ac52 --- /dev/null +++ b/front/src/Components/UI/AudioPlaying.svelte @@ -0,0 +1,52 @@ + + +
+ Audio playing +

Audio message

+ +
+ + diff --git a/front/src/Components/UI/ErrorDialog.svelte b/front/src/Components/UI/ErrorDialog.svelte new file mode 100644 index 00000000..3244de24 --- /dev/null +++ b/front/src/Components/UI/ErrorDialog.svelte @@ -0,0 +1,48 @@ + + +
+

Error

+
+ {#each $errorStore as error} +

{error}

+ {/each} +
+
+ +
+
+ + diff --git a/front/src/Components/UI/images/megaphone.svg b/front/src/Components/UI/images/megaphone.svg new file mode 100644 index 00000000..708f860c --- /dev/null +++ b/front/src/Components/UI/images/megaphone.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/front/src/Components/VisitCard/VisitCard.svelte b/front/src/Components/VisitCard/VisitCard.svelte new file mode 100644 index 00000000..ebf22934 --- /dev/null +++ b/front/src/Components/VisitCard/VisitCard.svelte @@ -0,0 +1,84 @@ + + + + + +
+ {#if hidden} +
+ {/if} + + {#if !hidden} +
+ +
+ {/if} + +
+ + diff --git a/front/src/Components/images/cinema-close.svg b/front/src/Components/images/cinema-close.svg new file mode 100644 index 00000000..aa1d9b17 --- /dev/null +++ b/front/src/Components/images/cinema-close.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/cinema.svg b/front/src/Components/images/cinema.svg similarity index 100% rename from front/dist/resources/logos/cinema.svg rename to front/src/Components/images/cinema.svg diff --git a/front/src/Components/images/logo.png b/front/src/Components/images/logo.png new file mode 100644 index 00000000..f4440ad5 Binary files /dev/null and b/front/src/Components/images/logo.png differ diff --git a/front/src/Components/images/microphone-close.svg b/front/src/Components/images/microphone-close.svg new file mode 100644 index 00000000..16731829 --- /dev/null +++ b/front/src/Components/images/microphone-close.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/microphone.svg b/front/src/Components/images/microphone.svg similarity index 100% rename from front/dist/resources/logos/microphone.svg rename to front/src/Components/images/microphone.svg diff --git a/front/dist/resources/logos/monitor-close.svg b/front/src/Components/images/monitor-close.svg similarity index 100% rename from front/dist/resources/logos/monitor-close.svg rename to front/src/Components/images/monitor-close.svg diff --git a/front/dist/resources/logos/monitor.svg b/front/src/Components/images/monitor.svg similarity index 100% rename from front/dist/resources/logos/monitor.svg rename to front/src/Components/images/monitor.svg diff --git a/front/src/Components/selectCharacter/SelectCharacterScene.svelte b/front/src/Components/selectCharacter/SelectCharacterScene.svelte new file mode 100644 index 00000000..e227771c --- /dev/null +++ b/front/src/Components/selectCharacter/SelectCharacterScene.svelte @@ -0,0 +1,92 @@ + + +
+
+

Select your WOKA

+ + +
+
+ + +
+
+ + \ No newline at end of file diff --git a/front/src/Connexion/AdminMessagesService.ts b/front/src/Connexion/AdminMessagesService.ts index 1750dd28..0cf7f95f 100644 --- a/front/src/Connexion/AdminMessagesService.ts +++ b/front/src/Connexion/AdminMessagesService.ts @@ -1,5 +1,5 @@ import {Subject} from "rxjs"; -import {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb"; +import type {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb"; export enum AdminMessageEventTypes { admin = 'message', @@ -19,11 +19,11 @@ interface AdminMessageEvent { class AdminMessagesService { private _messageStream: Subject = new Subject(); public messageStream = this._messageStream.asObservable(); - + constructor() { this.messageStream.subscribe((event) => console.log('message', event)) } - + onSendusermessage(message: SendUserMessage|BanUserMessage) { this._messageStream.next({ type: message.getType() as unknown as AdminMessageEventTypes, @@ -32,4 +32,4 @@ class AdminMessagesService { } } -export const adminMessagesService = new AdminMessagesService(); \ No newline at end of file +export const adminMessagesService = new AdminMessagesService(); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index a0edacbc..8112ba17 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -1,10 +1,10 @@ import Axios from "axios"; import {PUSHER_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable"; import {RoomConnection} from "./RoomConnection"; -import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels"; +import type {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels"; import {GameConnexionTypes, urlManager} from "../Url/UrlManager"; import {localUserStore} from "./LocalUserStore"; -import {LocalUser} from "./LocalUser"; +import {CharacterTexture, LocalUser} from "./LocalUser"; import {Room} from "./Room"; @@ -46,8 +46,8 @@ class ConnectionManager { urlManager.pushRoomIdToUrl(room); return Promise.resolve(room); } else if (connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) { - const localUser = localUserStore.getLocalUser(); + let localUser = localUserStore.getLocalUser(); if (localUser && localUser.jwtToken && localUser.uuid && localUser.textures) { this.localUser = localUser; try { @@ -57,16 +57,42 @@ class ConnectionManager { console.error('JWT token invalid. Did it expire? Login anonymously instead.'); await this.anonymousLogin(); } - } else { + }else{ await this.anonymousLogin(); } - let roomId: string + + localUser = localUserStore.getLocalUser(); + if(!localUser){ + throw "Error to store local user data"; + } + + let roomId: string; if (connexionType === GameConnexionTypes.empty) { roomId = START_ROOM_URL; } else { roomId = window.location.pathname + window.location.search + window.location.hash; } - return Promise.resolve(new Room(roomId)); + + //get detail map for anonymous login and set texture in local storage + const room = new Room(roomId); + const mapDetail = await room.getMapDetail(); + if(mapDetail.textures != undefined && mapDetail.textures.length > 0) { + //check if texture was changed + if(localUser.textures.length === 0){ + localUser.textures = mapDetail.textures; + }else{ + mapDetail.textures.forEach((newTexture) => { + const alreadyExistTexture = localUser?.textures.find((c) => newTexture.id === c.id); + if(localUser?.textures.findIndex((c) => newTexture.id === c.id) !== -1){ + return; + } + localUser?.textures.push(newTexture) + }); + } + this.localUser = localUser; + localUserStore.saveUser(localUser); + } + return Promise.resolve(room); } return Promise.reject(new Error('Invalid URL')); diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 477e86e3..b5a66296 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -1,8 +1,6 @@ -import {PlayerAnimationDirections} from "../Phaser/Player/Animation"; -import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; -import {SignalData} from "simple-peer"; -import {RoomConnection} from "./RoomConnection"; -import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures"; +import type {SignalData} from "simple-peer"; +import type {RoomConnection} from "./RoomConnection"; +import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures"; export enum EventMessage{ CONNECT = "connect", @@ -47,6 +45,7 @@ export interface MessageUserPositionInterface { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + visitCardUrl: string|null; companion: string|null; } @@ -60,6 +59,7 @@ export interface MessageUserJoined { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + visitCardUrl: string | null; companion: string|null; } @@ -85,11 +85,6 @@ export interface WebRtcSignalReceivedMessageInterface { webRtcPassword: string | undefined } -export interface StartMapInterface { - mapUrlStart: string, - startInstance: string -} - export interface ViewportInterface { left: number, top: number, diff --git a/front/src/Connexion/EmoteEventStream.ts b/front/src/Connexion/EmoteEventStream.ts new file mode 100644 index 00000000..9a639697 --- /dev/null +++ b/front/src/Connexion/EmoteEventStream.ts @@ -0,0 +1,19 @@ +import {Subject} from "rxjs"; + +interface EmoteEvent { + userId: number, + emoteName: string, +} + +class EmoteEventStream { + + private _stream:Subject = new Subject(); + public stream = this._stream.asObservable(); + + + fire(userId: number, emoteName:string) { + this._stream.next({userId, emoteName}); + } +} + +export const emoteEventStream = new EmoteEventStream(); \ No newline at end of file diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index 0793a938..c877d119 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -9,9 +9,8 @@ export interface CharacterTexture { export const maxUserNameLength: number = MAX_USERNAME_LENGTH; -export function isUserNameValid(value: string): boolean { - const regexp = new RegExp('^[A-Za-z0-9]{1,'+maxUserNameLength+'}$'); - return regexp.test(value); +export function isUserNameValid(value: unknown): boolean { + return typeof value === "string" && value.length > 0 && value.length <= maxUserNameLength && value.indexOf(' ') === -1; } export function areCharacterLayersValid(value: string[] | null): boolean { @@ -25,6 +24,6 @@ export function areCharacterLayersValid(value: string[] | null): boolean { } export class LocalUser { - constructor(public readonly uuid:string, public readonly jwtToken: string, public readonly textures: CharacterTexture[]) { + constructor(public readonly uuid:string, public readonly jwtToken: string, public textures: CharacterTexture[]) { } } diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 05d94440..3ae8d2ed 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -1,10 +1,17 @@ import Axios from "axios"; import {PUSHER_URL} from "../Enum/EnvironmentVariable"; +import type {CharacterTexture} from "./LocalUser"; + +export class MapDetail{ + constructor(public readonly mapUrl: string, public readonly textures : CharacterTexture[]|undefined) { + } +} export class Room { public readonly id: string; public readonly isPublic: boolean; private mapUrl: string|undefined; + private textures: CharacterTexture[]|undefined; private instance: string|undefined; private _search: URLSearchParams; @@ -50,10 +57,10 @@ export class Room { return {roomId, hash} } - public async getMapUrl(): Promise { - return new Promise((resolve, reject) => { - if (this.mapUrl !== undefined) { - resolve(this.mapUrl); + public async getMapDetail(): Promise { + return new Promise((resolve, reject) => { + if (this.mapUrl !== undefined && this.textures != undefined) { + resolve(new MapDetail(this.mapUrl, this.textures)); return; } @@ -61,7 +68,7 @@ export class Room { const match = /_\/[^/]+\/(.+)/.exec(this.id); if (!match) throw new Error('Could not extract url from "'+this.id+'"'); this.mapUrl = window.location.protocol+'//'+match[1]; - resolve(this.mapUrl); + resolve(new MapDetail(this.mapUrl, this.textures)); return; } else { // We have a private ID, we need to query the map URL from the server. @@ -71,7 +78,7 @@ export class Room { params: urlParts }).then(({data}) => { console.log('Map ', this.id, ' resolves to URL ', data.mapUrl); - resolve(data.mapUrl); + resolve(data); return; }).catch((reason) => { reject(reason); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 101bdd61..fe1a78b0 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -27,11 +27,13 @@ import { SendJitsiJwtMessage, CharacterLayerMessage, PingMessage, - SendUserMessage, - BanUserMessage + EmoteEventMessage, + EmotePromptMessage, + SendUserMessage, + BanUserMessage, } from "../Messages/generated/messages_pb" -import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; +import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; import Direction = PositionMessage.Direction; import {ProtobufClientUtils} from "../Network/ProtobufClientUtils"; import { @@ -42,11 +44,12 @@ import { ViewportInterface, WebRtcDisconnectMessageInterface, WebRtcSignalReceivedMessageInterface, } from "./ConnexionModels"; -import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures"; +import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures"; import {adminMessagesService} from "./AdminMessagesService"; import {worldFullMessageStream} from "./WorldFullMessageStream"; import {worldFullWarningStream} from "./WorldFullWarningStream"; import {connectionManager} from "./ConnectionManager"; +import {emoteEventStream} from "./EmoteEventStream"; const manualPingDelay = 20000; @@ -86,7 +89,7 @@ export class RoomConnection implements RoomConnection { url += '&bottom='+Math.floor(viewport.bottom); url += '&left='+Math.floor(viewport.left); url += '&right='+Math.floor(viewport.right); - + if (typeof companion === 'string') { url += '&companion='+encodeURIComponent(companion); } @@ -124,7 +127,7 @@ export class RoomConnection implements RoomConnection { if (message.hasBatchmessage()) { for (const subMessage of (message.getBatchmessage() as BatchMessage).getPayloadList()) { - let event: string; + let event: string|null = null; let payload; if (subMessage.hasUsermovedmessage()) { event = EventMessage.USER_MOVED; @@ -144,11 +147,16 @@ export class RoomConnection implements RoomConnection { } else if (subMessage.hasItemeventmessage()) { event = EventMessage.ITEM_EVENT; payload = subMessage.getItemeventmessage(); + } else if (subMessage.hasEmoteeventmessage()) { + const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage; + emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote()); } else { throw new Error('Unexpected batch message type'); } - this.dispatch(event, payload); + if (event) { + this.dispatch(event, payload); + } } } else if (message.hasRoomjoinedmessage()) { const roomJoinedMessage = message.getRoomjoinedmessage() as RoomJoinedMessage; @@ -336,6 +344,7 @@ export class RoomConnection implements RoomConnection { userId: message.getUserid(), name: message.getName(), characterLayers, + visitCardUrl: message.getVisitcardurl(), position: ProtobufClientUtils.toPointInterface(position), companion: companion ? companion.getName() : null } @@ -599,4 +608,14 @@ export class RoomConnection implements RoomConnection { public isAdmin(): boolean { return this.hasTag('admin'); } + + public emitEmoteEvent(emoteName: string): void { + const emoteMessage = new EmotePromptMessage(); + emoteMessage.setEmote(emoteName) + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setEmotepromptmessage(emoteMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } } diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index 7dd3a175..73f6427c 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -1,11 +1,11 @@ const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true"; const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json'; -// For compatibility reasons with older versions, API_URL is the old host name of PUSHER_URL -const PUSHER_URL = process.env.PUSHER_URL || (process.env.API_URL ? '//'+process.env.API_URL : "//pusher.workadventure.localhost"); +const PUSHER_URL = process.env.PUSHER_URL || '//pusher.workadventure.localhost'; const UPLOADER_URL = process.env.UPLOADER_URL || '//uploader.workadventure.localhost'; -const ADMIN_URL = process.env.ADMIN_URL || "//workadventure.localhost"; const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302"; const TURN_SERVER: string = process.env.TURN_SERVER || ""; +const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true"; +const DISABLE_NOTIFICATIONS: boolean = process.env.DISABLE_NOTIFICATIONS == "true"; const TURN_USER: string = process.env.TURN_USER || ''; const TURN_PASSWORD: string = process.env.TURN_PASSWORD || ''; const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL; @@ -14,15 +14,17 @@ const POSITION_DELAY = 200; // Wait 200ms between sending position events const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8; export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4'); +export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == 'true'; export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window.innerHeight <= 600 ) ); export { DEBUG_MODE, START_ROOM_URL, + SKIP_RENDER_OPTIMIZATIONS, + DISABLE_NOTIFICATIONS, PUSHER_URL, UPLOADER_URL, - ADMIN_URL, POSITION_DELAY, MAX_EXTRAPOLATION_TIME, STUN_SERVER, diff --git a/front/src/Network/ProtobufClientUtils.ts b/front/src/Network/ProtobufClientUtils.ts index 6a402e97..3d2d5c32 100644 --- a/front/src/Network/ProtobufClientUtils.ts +++ b/front/src/Network/ProtobufClientUtils.ts @@ -1,6 +1,6 @@ import {PositionMessage} from "../Messages/generated/messages_pb"; import Direction = PositionMessage.Direction; -import {PointInterface} from "../Connexion/ConnexionModels"; +import type {PointInterface} from "../Connexion/ConnexionModels"; export class ProtobufClientUtils { diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 72491ae1..1c43f452 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -1,6 +1,5 @@ import Sprite = Phaser.GameObjects.Sprite; import Container = Phaser.GameObjects.Container; -import { lazyLoadCompanionResource } from "./CompanionTexturesLoadingManager"; import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; export interface CompanionStatus { @@ -25,7 +24,7 @@ export class Companion extends Container { constructor(scene: Phaser.Scene, x: number, y: number, name: string, texturePromise: Promise) { super(scene, x + 14, y + 4); - + this.sprites = new Map(); this.delta = 0; @@ -104,7 +103,7 @@ export class Companion extends Container { } } } - + this.setDepth(this.y); this.playAnimation(this.direction, this.animationType); } @@ -137,7 +136,7 @@ export class Companion extends Container { this.getAnimations(resource).forEach(animation => { this.scene.anims.create(animation); }); - + this.scene.sys.updateList.add(sprite); this.sprites.set(resource, sprite); } diff --git a/front/src/Phaser/Components/ChatModeIcon.ts b/front/src/Phaser/Components/ChatModeIcon.ts index 932a4d88..69449a1d 100644 --- a/front/src/Phaser/Components/ChatModeIcon.ts +++ b/front/src/Phaser/Components/ChatModeIcon.ts @@ -1,3 +1,5 @@ +import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes"; + export class ChatModeIcon extends Phaser.GameObjects.Sprite { constructor(scene: Phaser.Scene, x: number, y: number) { super(scene, x, y, 'layout_modes', 3); @@ -6,6 +8,6 @@ export class ChatModeIcon extends Phaser.GameObjects.Sprite { this.setOrigin(0, 1); this.setInteractive(); this.setVisible(false); - this.setDepth(99999); + this.setDepth(DEPTH_INGAME_TEXT_INDEX); } } \ No newline at end of file diff --git a/front/src/Phaser/Components/MobileJoystick.ts b/front/src/Phaser/Components/MobileJoystick.ts index 0af3d6c8..b3fc021b 100644 --- a/front/src/Phaser/Components/MobileJoystick.ts +++ b/front/src/Phaser/Components/MobileJoystick.ts @@ -1,6 +1,6 @@ import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; -import ScaleManager = Phaser.Scale.ScaleManager; import {waScaleManager} from "../Services/WaScaleManager"; +import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes"; //the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free export const joystickBaseKey = 'joystickBase'; @@ -20,21 +20,21 @@ export class MobileJoystick extends VirtualJoystick { x: -1000, y: -1000, radius: radius * window.devicePixelRatio, - base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(baseSize * window.devicePixelRatio, baseSize * window.devicePixelRatio).setDepth(99999), - thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(thumbSize * window.devicePixelRatio, thumbSize * window.devicePixelRatio).setDepth(99999), + base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(baseSize * window.devicePixelRatio, baseSize * window.devicePixelRatio).setDepth(DEPTH_INGAME_TEXT_INDEX), + thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(thumbSize * window.devicePixelRatio, thumbSize * window.devicePixelRatio).setDepth(DEPTH_INGAME_TEXT_INDEX), enable: true, dir: "8dir", }); this.visible = false; this.enable = false; - this.scene.input.on('pointerdown', (pointer: { x: number; y: number; wasTouch: boolean; event: TouchEvent }) => { + this.scene.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => { if (!pointer.wasTouch) { return; } // Let's only display the joystick if there is one finger on the screen - if (pointer.event.touches.length === 1) { + if ((pointer.event as TouchEvent).touches.length === 1) { this.x = pointer.x; this.y = pointer.y; this.visible = true; diff --git a/front/src/Phaser/Components/OpenChatIcon.ts b/front/src/Phaser/Components/OpenChatIcon.ts index 1e9429e8..ab07a80c 100644 --- a/front/src/Phaser/Components/OpenChatIcon.ts +++ b/front/src/Phaser/Components/OpenChatIcon.ts @@ -1,4 +1,5 @@ import {discussionManager} from "../../WebRtc/DiscussionManager"; +import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes"; export const openChatIconName = 'openChatIcon'; export class OpenChatIcon extends Phaser.GameObjects.Image { @@ -9,7 +10,7 @@ export class OpenChatIcon extends Phaser.GameObjects.Image { this.setOrigin(0, 1); this.setInteractive(); this.setVisible(false); - this.setDepth(99999); + this.setDepth(DEPTH_INGAME_TEXT_INDEX); this.on("pointerup", () => discussionManager.showDiscussionPart()); } diff --git a/front/src/Phaser/Components/PresentationModeIcon.ts b/front/src/Phaser/Components/PresentationModeIcon.ts index 49ff2ea1..09c8beb5 100644 --- a/front/src/Phaser/Components/PresentationModeIcon.ts +++ b/front/src/Phaser/Components/PresentationModeIcon.ts @@ -1,3 +1,5 @@ +import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes"; + export class PresentationModeIcon extends Phaser.GameObjects.Sprite { constructor(scene: Phaser.Scene, x: number, y: number) { super(scene, x, y, 'layout_modes', 0); @@ -6,6 +8,6 @@ export class PresentationModeIcon extends Phaser.GameObjects.Sprite { this.setOrigin(0, 1); this.setInteractive(); this.setVisible(false); - this.setDepth(99999); + this.setDepth(DEPTH_INGAME_TEXT_INDEX); } } \ No newline at end of file diff --git a/front/src/Phaser/Components/RadialMenu.ts b/front/src/Phaser/Components/RadialMenu.ts new file mode 100644 index 00000000..1094f73a --- /dev/null +++ b/front/src/Phaser/Components/RadialMenu.ts @@ -0,0 +1,74 @@ +import Sprite = Phaser.GameObjects.Sprite; +import {DEPTH_UI_INDEX} from "../Game/DepthIndexes"; +import {waScaleManager} from "../Services/WaScaleManager"; + +export interface RadialMenuItem { + image: string, + name: string, +} + +export const RadialMenuClickEvent = 'radialClick'; + +export class RadialMenu extends Phaser.GameObjects.Container { + private resizeCallback: OmitThisParameter<() => void>; + + constructor(scene: Phaser.Scene, x: number, y: number, private items: RadialMenuItem[]) { + super(scene, x, y); + this.setDepth(DEPTH_UI_INDEX) + this.scene.add.existing(this); + this.initItems(); + + this.resize(); + this.resizeCallback = this.resize.bind(this); + this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback); + } + + private initItems() { + const itemsNumber = this.items.length; + const menuRadius = 70 + (waScaleManager.uiScalingFactor - 1) * 20; + this.items.forEach((item, index) => this.createRadialElement(item, index, itemsNumber, menuRadius)) + } + + private createRadialElement(item: RadialMenuItem, index: number, itemsNumber: number, menuRadius: number) { + const image = new Sprite(this.scene, 0, menuRadius, item.image); + this.add(image); + this.scene.sys.updateList.add(image); + const scalingFactor = waScaleManager.uiScalingFactor * 0.075; + image.setScale(scalingFactor) + image.setInteractive({ + useHandCursor: true, + }); + image.on('pointerdown', () => this.emit(RadialMenuClickEvent, item)); + image.on('pointerover', () => { + this.scene.tweens.add({ + targets: image, + props: { + scale: 2 * scalingFactor, + }, + duration: 500, + ease: 'Power3', + }) + }); + image.on('pointerout', () => { + this.scene.tweens.add({ + targets: image, + props: { + scale: scalingFactor, + }, + duration: 500, + ease: 'Power3', + }) + }); + const angle = 2 * Math.PI * index / itemsNumber; + Phaser.Actions.RotateAroundDistance([image], {x: 0, y: 0}, angle, menuRadius); + } + + private resize() { + this.setScale(waScaleManager.uiScalingFactor); + } + + public destroy() { + this.scene.scale.removeListener(Phaser.Scale.Events.RESIZE, this.resizeCallback); + super.destroy(); + } +} \ No newline at end of file diff --git a/front/src/Phaser/Components/SoundMeter.ts b/front/src/Phaser/Components/SoundMeter.ts index af75940e..53802d31 100644 --- a/front/src/Phaser/Components/SoundMeter.ts +++ b/front/src/Phaser/Components/SoundMeter.ts @@ -1,3 +1,5 @@ +import type {IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode} from 'standardized-audio-context'; + /** * Class to measure the sound volume of a media stream */ @@ -5,10 +7,10 @@ export class SoundMeter { private instant: number; private clip: number; //private script: ScriptProcessorNode; - private analyser: AnalyserNode|undefined; + private analyser: IAnalyserNode|undefined; private dataArray: Uint8Array|undefined; - private context: AudioContext|undefined; - private source: MediaStreamAudioSourceNode|undefined; + private context: IAudioContext|undefined; + private source: IMediaStreamAudioSourceNode|undefined; constructor() { this.instant = 0.0; @@ -16,19 +18,21 @@ export class SoundMeter { //this.script = context.createScriptProcessor(2048, 1, 1); } - private init(context: AudioContext) { - if (this.context === undefined) { - this.context = context; - this.analyser = this.context.createAnalyser(); + private init(context: IAudioContext) { + this.context = context; + this.analyser = this.context.createAnalyser(); - this.analyser.fftSize = 2048; - const bufferLength = this.analyser.fftSize; - this.dataArray = new Uint8Array(bufferLength); - } + this.analyser.fftSize = 2048; + const bufferLength = this.analyser.fftSize; + this.dataArray = new Uint8Array(bufferLength); } - public connectToSource(stream: MediaStream, context: AudioContext): void + public connectToSource(stream: MediaStream, context: IAudioContext): void { + if (this.source !== undefined) { + this.stop(); + } + this.init(context); this.source = this.context?.createMediaStreamSource(stream); @@ -83,56 +87,3 @@ export class SoundMeter { } - -// Meter class that generates a number correlated to audio volume. -// The meter class itself displays nothing, but it makes the -// instantaneous and time-decaying volumes available for inspection. -// It also reports on the fraction of samples that were at or near -// the top of the measurement range. -/*function SoundMeter(context) { - this.context = context; - this.instant = 0.0; - this.slow = 0.0; - this.clip = 0.0; - this.script = context.createScriptProcessor(2048, 1, 1); - const that = this; - this.script.onaudioprocess = function(event) { - const input = event.inputBuffer.getChannelData(0); - let i; - let sum = 0.0; - let clipcount = 0; - for (i = 0; i < input.length; ++i) { - sum += input[i] * input[i]; - if (Math.abs(input[i]) > 0.99) { - clipcount += 1; - } - } - that.instant = Math.sqrt(sum / input.length); - that.slow = 0.95 * that.slow + 0.05 * that.instant; - that.clip = clipcount / input.length; - }; -} - -SoundMeter.prototype.connectToSource = function(stream, callback) { - console.log('SoundMeter connecting'); - try { - this.mic = this.context.createMediaStreamSource(stream); - this.mic.connect(this.script); - // necessary to make sample run, but should not be. - this.script.connect(this.context.destination); - if (typeof callback !== 'undefined') { - callback(null); - } - } catch (e) { - console.error(e); - if (typeof callback !== 'undefined') { - callback(e); - } - } -}; - -SoundMeter.prototype.stop = function() { - this.mic.disconnect(); - this.script.disconnect(); -}; -*/ diff --git a/front/src/Phaser/Components/SoundMeterSprite.ts b/front/src/Phaser/Components/SoundMeterSprite.ts deleted file mode 100644 index 2787059d..00000000 --- a/front/src/Phaser/Components/SoundMeterSprite.ts +++ /dev/null @@ -1,44 +0,0 @@ -import Container = Phaser.GameObjects.Container; -import {Scene} from "phaser"; -import GameObject = Phaser.GameObjects.GameObject; -import Rectangle = Phaser.GameObjects.Rectangle; - - -export class SoundMeterSprite extends Container { - private rectangles: Rectangle[] = new Array(); - private static readonly NB_BARS = 20; - - constructor(scene: Scene, x?: number, y?: number, children?: GameObject[]) { - super(scene, x, y, children); - - for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) { - const rectangle = new Rectangle(scene, i * 13, 0, 10, 20, (Math.round(255 - i * 255 / SoundMeterSprite.NB_BARS) << 8) + (Math.round(i * 255 / SoundMeterSprite.NB_BARS) << 16)); - this.add(rectangle); - this.rectangles.push(rectangle); - } - } - - /** - * A number between 0 and 100 - * - * @param volume - */ - public setVolume(volume: number): void { - - const normalizedVolume = volume / 100 * SoundMeterSprite.NB_BARS; - for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) { - if (normalizedVolume < i) { - this.rectangles[i].alpha = 0.5; - } else { - this.rectangles[i].alpha = 1; - } - } - } - - public getWidth(): number { - return SoundMeterSprite.NB_BARS * 13; - } - - - -} diff --git a/front/src/Phaser/Components/TextUtils.ts b/front/src/Phaser/Components/TextUtils.ts index beee840a..db9a97fb 100644 --- a/front/src/Phaser/Components/TextUtils.ts +++ b/front/src/Phaser/Components/TextUtils.ts @@ -1,7 +1,5 @@ -import {ITiledMapObject} from "../Map/ITiledMap"; -import Text = Phaser.GameObjects.Text; -import {GameScene} from "../Game/GameScene"; -import TextStyle = Phaser.GameObjects.TextStyle; +import type {ITiledMapObject} from "../Map/ITiledMap"; +import type {GameScene} from "../Game/GameScene"; export class TextUtils { public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void { diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 9f2bd1fd..2ff66178 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -1,10 +1,15 @@ import {PlayerAnimationDirections, PlayerAnimationTypes} from "../Player/Animation"; import {SpeechBubble} from "./SpeechBubble"; -import BitmapText = Phaser.GameObjects.BitmapText; +import Text = Phaser.GameObjects.Text; import Container = Phaser.GameObjects.Container; import Sprite = Phaser.GameObjects.Sprite; import {TextureError} from "../../Exception/TextureError"; import {Companion} from "../Companion/Companion"; +import type {GameScene} from "../Game/GameScene"; +import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes"; +import {waScaleManager} from "../Services/WaScaleManager"; + +const playerNameY = - 25; interface AnimationData { key: string; @@ -14,24 +19,31 @@ interface AnimationData { frames : number[] } +const interactiveRadius = 35; + export abstract class Character extends Container { private bubble: SpeechBubble|null = null; - private readonly playerName: BitmapText; + private readonly playerName: Text; public PlayerValue: string; public sprites: Map; private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; //private teleportation: Sprite; private invisible: boolean; public companion?: Companion; + private emote: Phaser.GameObjects.Sprite | null = null; + private emoteTween: Phaser.Tweens.Tween|null = null; - constructor(scene: Phaser.Scene, + constructor(scene: GameScene, x: number, y: number, texturesPromise: Promise, name: string, direction: PlayerAnimationDirections, moving: boolean, - frame?: string | number + frame: string | number, + isClickable: boolean, + companion: string|null, + companionTexturePromise?: Promise ) { super(scene, x, y/*, texture, frame*/); this.PlayerValue = name; @@ -45,19 +57,18 @@ export abstract class Character extends Container { this.invisible = false }) - /*this.teleportation = new Sprite(scene, -20, -10, 'teleportation', 3); - this.teleportation.setInteractive(); - this.teleportation.visible = false; - this.teleportation.on('pointerup', () => { - this.report.visible = false; - this.teleportation.visible = false; - }); - this.add(this.teleportation);*/ - - this.playerName = new BitmapText(scene, 0, - 25, 'main_font', name, 7); - this.playerName.setOrigin(0.5).setCenterAlign().setDepth(99999); + this.playerName = new Text(scene, 0, playerNameY, name, {fontFamily: '"Press Start 2P"', fontSize: '8px', strokeThickness: 2, stroke: "gray"}); + this.playerName.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX); this.add(this.playerName); + if (isClickable) { + this.setInteractive({ + hitArea: new Phaser.Geom.Circle(0, 0, interactiveRadius), + hitAreaCallback: Phaser.Geom.Circle.Contains, //eslint-disable-line @typescript-eslint/unbound-method + useHandCursor: true, + }); + } + scene.add.existing(this); this.scene.physics.world.enableBody(this); @@ -69,6 +80,10 @@ export abstract class Character extends Container { this.setDepth(-1); this.playAnimation(direction, moving); + + if (typeof companion === 'string') { + this.addCompanion(companion, companionTexturePromise); + } } public addCompanion(name: string, texturePromise?: Promise): void { @@ -79,11 +94,10 @@ export abstract class Character extends Container { public addTextures(textures: string[], frame?: string | number): void { for (const texture of textures) { - if(!this.scene.textures.exists(texture)){ + if(this.scene && !this.scene.textures.exists(texture)){ throw new TextureError('texture not found'); } const sprite = new Sprite(this.scene, 0, 0, texture, frame); - sprite.setInteractive({useHandCursor: true}); this.add(sprite); this.getPlayerAnimations(texture).forEach(d => { this.scene.anims.create({ @@ -225,7 +239,84 @@ export abstract class Character extends Container { this.scene.sys.updateList.remove(sprite); } } + this.list.forEach(objectContaining => objectContaining.destroy()) super.destroy(); - this.playerName.destroy(); + } + + playEmote(emoteKey: string) { + this.cancelPreviousEmote(); + + const scalingFactor = waScaleManager.uiScalingFactor * 0.05; + const emoteY = -30 - scalingFactor * 10; + + this.playerName.setVisible(false); + this.emote = new Sprite(this.scene, 0, 0, emoteKey); + this.emote.setAlpha(0); + this.emote.setScale(0.1 * scalingFactor); + this.add(this.emote); + this.scene.sys.updateList.add(this.emote); + + this.createStartTransition(scalingFactor, emoteY); + } + + private createStartTransition(scalingFactor: number, emoteY: number) { + this.emoteTween = this.scene?.tweens.add({ + targets: this.emote, + props: { + scale: scalingFactor, + alpha: 1, + y: emoteY, + }, + ease: 'Power2', + duration: 500, + onComplete: () => { + this.startPulseTransition(emoteY, scalingFactor); + } + }); + } + + private startPulseTransition(emoteY: number, scalingFactor: number) { + this.emoteTween = this.scene?.tweens.add({ + targets: this.emote, + props: { + y: emoteY * 1.3, + scale: scalingFactor * 1.1 + }, + duration: 250, + yoyo: true, + repeat: 1, + completeDelay: 200, + onComplete: () => { + this.startExitTransition(emoteY); + } + }); + } + + private startExitTransition(emoteY: number) { + this.emoteTween = this.scene?.tweens.add({ + targets: this.emote, + props: { + alpha: 0, + y: 2 * emoteY, + }, + ease: 'Power2', + duration: 500, + onComplete: () => { + this.destroyEmote(); + } + }); + } + + cancelPreviousEmote() { + if (!this.emote) return; + + this.emoteTween?.remove(); + this.destroyEmote() + } + + private destroyEmote() { + this.emote?.destroy(); + this.emote = null; + this.playerName.setVisible(true); } } diff --git a/front/src/Phaser/Entity/CustomizedCharacter.ts b/front/src/Phaser/Entity/CustomizedCharacter.ts new file mode 100644 index 00000000..3a7f1597 --- /dev/null +++ b/front/src/Phaser/Entity/CustomizedCharacter.ts @@ -0,0 +1,20 @@ +import Container = Phaser.GameObjects.Container; +import type {Scene} from "phaser"; +import Sprite = Phaser.GameObjects.Sprite; + +/** + * A sprite of a customized character (used in the Customize Scene only) + */ +export class CustomizedCharacter extends Container { + public constructor(scene: Scene, x: number, y: number, layers: string[]) { + super(scene, x, y); + this.updateSprites(layers); + } + + public updateSprites(layers: string[]): void { + this.removeAll(true); + for (const layer of layers) { + this.add(new Sprite(this.scene, 0, 0, layer)); + } + } +} diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index 3dfe8368..95f00a9e 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -1,8 +1,11 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; -import TextureManager = Phaser.Textures.TextureManager; -import {CharacterTexture} from "../../Connexion/LocalUser"; +import type {CharacterTexture} from "../../Connexion/LocalUser"; import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures"; +export interface FrameConfig { + frameWidth: number, + frameHeight: number, +} export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => { const returnArray:BodyResourceDescriptionInterface[][] = []; @@ -27,7 +30,10 @@ export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptio export const loadCustomTexture = (loaderPlugin: LoaderPlugin, texture: CharacterTexture) : Promise => { const name = 'customCharacterTexture'+texture.id; const playerResourceDescriptor: BodyResourceDescriptionInterface = {name, img: texture.url, level: texture.level} - return createLoadingPromise(loaderPlugin, playerResourceDescriptor); + return createLoadingPromise(loaderPlugin, playerResourceDescriptor, { + frameWidth: 32, + frameHeight: 32 + }); } export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, texturekeys:Array): Promise => { @@ -37,7 +43,10 @@ export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, textur //TODO refactor const playerResourceDescriptor = getRessourceDescriptor(textureKey); if (playerResourceDescriptor && !loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { - promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor)); + promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor, { + frameWidth: 32, + frameHeight: 32 + })); } }catch (err){ console.error(err); @@ -70,15 +79,12 @@ export const getRessourceDescriptor = (textureKey: string|BodyResourceDescriptio throw 'Could not find a data for texture '+textureName; } -const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) => { +export const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface, frameConfig: FrameConfig) => { return new Promise((res) => { if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { return res(playerResourceDescriptor); } - loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, { - frameWidth: 32, - frameHeight: 32 - }); + loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, frameConfig); loadPlugin.once('filecomplete-spritesheet-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor)); }); } diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index 41e2e2df..32881e97 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -1,13 +1,16 @@ -import {GameScene} from "../Game/GameScene"; -import {PointInterface} from "../../Connexion/ConnexionModels"; +import type {GameScene} from "../Game/GameScene"; +import type {PointInterface} from "../../Connexion/ConnexionModels"; import {Character} from "../Entity/Character"; -import {PlayerAnimationDirections} from "../Player/Animation"; +import type {PlayerAnimationDirections} from "../Player/Animation"; +import {requestVisitCardsStore} from "../../Stores/GameStore"; + /** * Class representing the sprite of a remote player (a player that plays on another computer) */ export class RemotePlayer extends Character { userId: number; + private visitCardUrl: string|null; constructor( userId: number, @@ -18,26 +21,28 @@ export class RemotePlayer extends Character { texturesPromise: Promise, direction: PlayerAnimationDirections, moving: boolean, + visitCardUrl: string|null, companion: string|null, companionTexturePromise?: Promise ) { - super(Scene, x, y, texturesPromise, name, direction, moving, 1); + super(Scene, x, y, texturesPromise, name, direction, moving, 1, !!visitCardUrl, companion, companionTexturePromise); //set data this.userId = userId; - - if (typeof companion === 'string') { - this.addCompanion(companion, companionTexturePromise); - } + this.visitCardUrl = visitCardUrl; + + this.on('pointerdown', () => { + requestVisitCardsStore.set(this.visitCardUrl); + }) } updatePosition(position: PointInterface): void { this.playAnimation(position.direction as PlayerAnimationDirections, position.moving); this.setX(position.x); this.setY(position.y); - + this.setDepth(position.y); //this is to make sure the perspective (player models closer the bottom of the screen will appear in front of models nearer the top of the screen). - + if (this.companion) { this.companion.setTarget(position.x, position.y, position.direction as PlayerAnimationDirections); } diff --git a/front/src/Phaser/Entity/SpeechBubble.ts b/front/src/Phaser/Entity/SpeechBubble.ts index 231de875..17b7ed0e 100644 --- a/front/src/Phaser/Entity/SpeechBubble.ts +++ b/front/src/Phaser/Entity/SpeechBubble.ts @@ -1,12 +1,12 @@ import Scene = Phaser.Scene; -import {Character} from "./Character"; +import type {Character} from "./Character"; //todo: improve this WIP export class SpeechBubble { private bubble: Phaser.GameObjects.Graphics; private content: Phaser.GameObjects.Text; - + constructor(scene: Scene, player: Character, text: string = "") { const bubbleHeight = 50; diff --git a/front/src/Phaser/Game/AddPlayerInterface.ts b/front/src/Phaser/Game/AddPlayerInterface.ts index 96762a66..1a5176f0 100644 --- a/front/src/Phaser/Game/AddPlayerInterface.ts +++ b/front/src/Phaser/Game/AddPlayerInterface.ts @@ -1,10 +1,11 @@ -import {PointInterface} from "../../Connexion/ConnexionModels"; -import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; +import type {PointInterface} from "../../Connexion/ConnexionModels"; +import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; export interface AddPlayerInterface { userId: number; name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + visitCardUrl: string|null; companion: string|null; } diff --git a/front/src/Phaser/Game/DepthIndexes.ts b/front/src/Phaser/Game/DepthIndexes.ts new file mode 100644 index 00000000..d2d38328 --- /dev/null +++ b/front/src/Phaser/Game/DepthIndexes.ts @@ -0,0 +1,8 @@ +//this file contains all the depth indexes which will be used in our game + +export const DEPTH_TILE_INDEX = 0; +//Note: Player characters use their y coordinate as their depth to simulate a perspective. +//See the Character class. +export const DEPTH_OVERLAY_INDEX = 10000; +export const DEPTH_INGAME_TEXT_INDEX = 100000; +export const DEPTH_UI_INDEX = 1000000; diff --git a/front/src/Phaser/Game/DirtyScene.ts b/front/src/Phaser/Game/DirtyScene.ts index 03ec9a95..3e1f3cdf 100644 --- a/front/src/Phaser/Game/DirtyScene.ts +++ b/front/src/Phaser/Game/DirtyScene.ts @@ -2,6 +2,8 @@ import {ResizableScene} from "../Login/ResizableScene"; import GameObject = Phaser.GameObjects.GameObject; import Events = Phaser.Scenes.Events; import AnimationEvents = Phaser.Animations.Events; +import StructEvents = Phaser.Structs.Events; +import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable"; /** * A scene that can track its dirty/pristine state. @@ -10,6 +12,7 @@ export abstract class DirtyScene extends ResizableScene { private isAlreadyTracking: boolean = false; protected dirty:boolean = true; private objectListChanged:boolean = true; + private physicsEnabled: boolean = false; /** * Track all objects added to the scene and adds a callback each time an animation is added. @@ -18,17 +21,16 @@ export abstract class DirtyScene extends ResizableScene { * Note: this does not work with animations from sprites inside containers. */ protected trackDirtyAnims(): void { - if (this.isAlreadyTracking) { + if (this.isAlreadyTracking || SKIP_RENDER_OPTIMIZATIONS) { return; } this.isAlreadyTracking = true; const trackAnimationFunction = this.trackAnimation.bind(this); - this.events.on(Events.ADDED_TO_SCENE, (gameObject: GameObject) => { + this.sys.updateList.on(StructEvents.PROCESS_QUEUE_ADD, (gameObject: GameObject) => { this.objectListChanged = true; gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction); }); - - this.events.on(Events.REMOVED_FROM_SCENE, (gameObject: GameObject) => { + this.sys.updateList.on(StructEvents.PROCESS_QUEUE_REMOVE, (gameObject: GameObject) => { this.objectListChanged = true; gameObject.removeListener(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction); }); @@ -36,6 +38,27 @@ export abstract class DirtyScene extends ResizableScene { this.events.on(Events.RENDER, () => { this.objectListChanged = false; }); + + this.physics.disableUpdate(); + this.events.on(Events.POST_UPDATE, () => { + let objectMoving = false; + for (const body of this.physics.world.bodies.entries) { + if (body.velocity.x !== 0 || body.velocity.y !== 0) { + this.objectListChanged = true; + objectMoving = true; + if (!this.physicsEnabled) { + this.physics.enableUpdate(); + this.physicsEnabled = true; + } + break; + } + } + if (!objectMoving && this.physicsEnabled) { + this.physics.disableUpdate(); + this.physicsEnabled = false; + } + }); + } private trackAnimation(): void { @@ -46,7 +69,7 @@ export abstract class DirtyScene extends ResizableScene { return this.dirty || this.objectListChanged; } - public onResize(ev: UIEvent): void { + public onResize(): void { this.objectListChanged = true; } } diff --git a/front/src/Phaser/Game/EmoteManager.ts b/front/src/Phaser/Game/EmoteManager.ts new file mode 100644 index 00000000..2e0bbd67 --- /dev/null +++ b/front/src/Phaser/Game/EmoteManager.ts @@ -0,0 +1,73 @@ +import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; +import {emoteEventStream} from "../../Connexion/EmoteEventStream"; +import type {GameScene} from "./GameScene"; +import type {RadialMenuItem} from "../Components/RadialMenu"; +import LoaderPlugin = Phaser.Loader.LoaderPlugin; +import type {Subscription} from "rxjs"; + + +interface RegisteredEmote extends BodyResourceDescriptionInterface { + name: string; + img: string; +} + +export const emotes: {[key: string]: RegisteredEmote} = { + 'emote-heart': {name: 'emote-heart', img: 'resources/emotes/heart-emote.png'}, + 'emote-clap': {name: 'emote-clap', img: 'resources/emotes/clap-emote.png'}, + 'emote-hand': {name: 'emote-hand', img: 'resources/emotes/hand-emote.png'}, + 'emote-thanks': {name: 'emote-thanks', img: 'resources/emotes/thanks-emote.png'}, + 'emote-thumb-up': {name: 'emote-thumb-up', img: 'resources/emotes/thumb-up-emote.png'}, + 'emote-thumb-down': {name: 'emote-thumb-down', img: 'resources/emotes/thumb-down-emote.png'}, +}; + +export class EmoteManager { + private subscription: Subscription; + + constructor(private scene: GameScene) { + this.subscription = emoteEventStream.stream.subscribe((event) => { + const actor = this.scene.MapPlayersByKey.get(event.userId); + if (actor) { + this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => { + actor.playEmote(emoteKey); + }) + } + }) + } + createLoadingPromise(loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) { + return new Promise((res) => { + if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { + return res(playerResourceDescriptor.name); + } + loadPlugin.image(playerResourceDescriptor.name, playerResourceDescriptor.img); + loadPlugin.once('filecomplete-image-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor.name)); + }); + } + + lazyLoadEmoteTexture(textureKey: string): Promise { + const emoteDescriptor = emotes[textureKey]; + if (emoteDescriptor === undefined) { + throw 'Emote not found!'; + } + const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor); + this.scene.load.start(); + return loadPromise + } + + getMenuImages(): Promise { + const promises = []; + for (const key in emotes) { + const promise = this.lazyLoadEmoteTexture(key).then((textureKey) => { + return { + image: textureKey, + name: textureKey, + } + }); + promises.push(promise); + } + return Promise.all(promises); + } + + destroy() { + this.subscription.unsubscribe(); + } +} \ No newline at end of file diff --git a/front/src/Phaser/Game/Game.ts b/front/src/Phaser/Game/Game.ts index 50bfb17a..e267e80a 100644 --- a/front/src/Phaser/Game/Game.ts +++ b/front/src/Phaser/Game/Game.ts @@ -1,3 +1,8 @@ +import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable"; +import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; +import {waScaleManager} from "../Services/WaScaleManager"; +import {ResizableScene} from "../Login/ResizableScene"; + const Events = Phaser.Core.Events; /** @@ -5,8 +10,35 @@ const Events = Phaser.Core.Events; * It comes with an optimization to skip rendering. * * Beware, the "step" function might vary in future versions of Phaser. + * + * It also automatically calls "onResize" on any scenes extending ResizableScene. */ export class Game extends Phaser.Game { + + private _isDirty = false; + + + constructor(GameConfig: Phaser.Types.Core.GameConfig) { + super(GameConfig); + + this.scale.on(Phaser.Scale.Events.RESIZE, () => { + for (const scene of this.scene.getScenes(true)) { + if (scene instanceof ResizableScene) { + scene.onResize(); + } + } + }) + + /*window.addEventListener('resize', (event) => { + // Let's trigger the onResize method of any active scene that is a ResizableScene + for (const scene of this.scene.getScenes(true)) { + if (scene instanceof ResizableScene) { + scene.onResize(event); + } + } + });*/ + } + public step(time: number, delta: number) { // @ts-ignore @@ -35,7 +67,7 @@ export class Game extends Phaser.Game { eventEmitter.emit(Events.POST_STEP, time, delta); // This "if" is the changed introduced by the new "Game" class to avoid rendering unnecessarily. - if (this.isDirty()) { + if (SKIP_RENDER_OPTIMIZATIONS || this.isDirty()) { const renderer = this.renderer; // Run the Pre-render (clearing the canvas, setting background colors, etc) @@ -62,6 +94,11 @@ export class Game extends Phaser.Game { } private isDirty(): boolean { + if (this._isDirty) { + this._isDirty = false; + return true; + } + // Loop through the scenes in forward order for (let i = 0; i < this.scene.scenes.length; i++) { @@ -85,4 +122,11 @@ export class Game extends Phaser.Game { return false; } + + /** + * Marks the game as needing to be redrawn. + */ + public markDirty(): void { + this._isDirty = true; + } } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 6047d430..cd2575c0 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,12 +1,14 @@ import {GameScene} from "./GameScene"; import {connectionManager} from "../../Connexion/ConnectionManager"; -import {Room} from "../../Connexion/Room"; +import type {Room} from "../../Connexion/Room"; import {MenuScene, MenuSceneName} from "../Menu/MenuScene"; -import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene"; import {LoginSceneName} from "../Login/LoginScene"; import {SelectCharacterSceneName} from "../Login/SelectCharacterScene"; import {EnableCameraSceneName} from "../Login/EnableCameraScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; +import {get} from "svelte/store"; +import {requestedCameraState, requestedMicrophoneState} from "../../Stores/MediaStore"; +import {helpCameraSettingsVisibleStore} from "../../Stores/HelpCameraSettingsStore"; export interface HasMovedEvent { direction: string; @@ -24,7 +26,7 @@ export class GameManager { private companion: string|null; private startRoom!:Room; currentGameSceneName: string|null = null; - + constructor() { this.playerName = localUserStore.getName(); this.characterLayers = localUserStore.getCharacterLayers(); @@ -65,7 +67,7 @@ export class GameManager { return this.characterLayers; } - + setCompanion(companion: string|null): void { this.companion = companion; } @@ -76,11 +78,11 @@ export class GameManager { public async loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin): Promise { const roomID = room.id; - const mapUrl = await room.getMapUrl(); + const mapDetail = await room.getMapDetail(); const gameIndex = scenePlugin.getIndex(roomID); if(gameIndex === -1){ - const game : Phaser.Scene = new GameScene(room, mapUrl); + const game : Phaser.Scene = new GameScene(room, mapDetail.mapUrl); scenePlugin.add(roomID, game, false); } } @@ -89,9 +91,13 @@ export class GameManager { console.log('starting '+ (this.currentGameSceneName || this.startRoom.id)) scenePlugin.start(this.currentGameSceneName || this.startRoom.id); scenePlugin.launch(MenuSceneName); - scenePlugin.launch(HelpCameraSettingsSceneName);//700 + + if(!localUserStore.getHelpCameraSettingsShown() && (!get(requestedMicrophoneState) || !get(requestedCameraState))){ + helpCameraSettingsVisibleStore.set(true); + localUserStore.setHelpCameraSettingsShown(); + } } - + public gameSceneIsCreated(scene: GameScene) { this.currentGameSceneName = scene.scene.key; const menuScene: MenuScene = scene.scene.get(MenuSceneName) as MenuScene; @@ -125,7 +131,7 @@ export class GameManager { scene.scene.run(fallbackSceneName) } } - + public getCurrentGameScene(scene: Phaser.Scene): GameScene { if (this.currentGameSceneName === null) throw 'No current scene id set!'; return scene.scene.get(this.currentGameSceneName) as GameScene diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index 5fe91b62..7c93a702 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -1,4 +1,4 @@ -import {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap"; +import type {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap"; import {LayersIterator} from "../Map/LayersIterator"; export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map) => void; diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 2c505e31..b09f5a1e 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,5 +1,5 @@ import {gameManager, HasMovedEvent} from "./GameManager"; -import { +import type { GroupCreatedUpdatedMessageInterface, MessageUserJoined, MessageUserMovedInterface, @@ -9,23 +9,22 @@ import { PositionInterface, RoomJoinedMessageInterface } from "../../Connexion/ConnexionModels"; -import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player"; +import {hasMovedEventName, Player, requestEmoteEventName} from "../Player/Player"; import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY, } from "../../Enum/EnvironmentVariable"; -import { +import type { ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapObject, - ITiledText, ITiledMapTileLayer, ITiledTileSet } from "../Map/ITiledMap"; -import {AddPlayerInterface} from "./AddPlayerInterface"; +import type {AddPlayerInterface} from "./AddPlayerInterface"; import {PlayerAnimationDirections} from "../Player/Animation"; import {PlayerMovement} from "./PlayerMovement"; import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator"; @@ -49,13 +48,14 @@ import { import {GameMap} from "./GameMap"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {mediaManager} from "../../WebRtc/MediaManager"; -import {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; -import {ActionableItem} from "../Items/ActionableItem"; +import type {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; +import type {ActionableItem} from "../Items/ActionableItem"; import {UserInputManager} from "../UserInput/UserInputManager"; -import {UserMovedMessage} from "../../Messages/generated/messages_pb"; +import {soundManager} from "./SoundManager"; +import type {UserMovedMessage} from "../../Messages/generated/messages_pb"; import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils"; import {connectionManager} from "../../Connexion/ConnectionManager"; -import {RoomConnection} from "../../Connexion/RoomConnection"; +import type {RoomConnection} from "../../Connexion/RoomConnection"; import {GlobalMessageManager} from "../../Administration/GlobalMessageManager"; import {userMessageManager} from "../../Administration/UserMessageManager"; import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager"; @@ -81,7 +81,7 @@ import GameObject = Phaser.GameObjects.GameObject; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import DOMElement = Phaser.GameObjects.DOMElement; import EVENT_TYPE =Phaser.Scenes.Events -import {Subscription} from "rxjs"; +import type {Subscription} from "rxjs"; import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; import RenderTexture = Phaser.GameObjects.RenderTexture; @@ -91,7 +91,10 @@ import {TextUtils} from "../Components/TextUtils"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick"; +import {DEPTH_OVERLAY_INDEX} from "./DepthIndexes"; import {waScaleManager} from "../Services/WaScaleManager"; +import {peerStore} from "../../Stores/PeerStore"; +import {EmoteManager} from "./EmoteManager"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -132,7 +135,7 @@ const defaultStartLayerName = 'start'; export class GameScene extends DirtyScene implements CenterListener { Terrains : Array; - CurrentPlayer!: CurrentGamerInterface; + CurrentPlayer!: Player; MapPlayers!: Phaser.Physics.Arcade.Group; MapPlayersByKey : Map = new Map(); Map!: Phaser.Tilemaps.Tilemap; @@ -152,11 +155,12 @@ export class GameScene extends DirtyScene implements CenterListener { private GlobalMessageManager!: GlobalMessageManager; public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager; private connectionAnswerPromise: Promise; - private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike) => void; + private connectionAnswerPromiseResolve!: (value: RoomJoinedMessageInterface | PromiseLike) => void; // A promise that will resolve when the "create" method is called (signaling loading is ended) private createPromise: Promise; private createPromiseResolve!: (value?: void | PromiseLike) => void; private iframeSubscriptionList! : Array; + private peerStoreUnsubscribe!: () => void; MapUrlFile: string; RoomId: string; instance: string; @@ -187,6 +191,8 @@ export class GameScene extends DirtyScene implements CenterListener { private popUpElements : Map = new Map(); private originalMapUrl: string|undefined; private pinchManager: PinchManager|undefined; + private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time. + private emoteManager!: EmoteManager; constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { super({ @@ -202,7 +208,7 @@ export class GameScene extends DirtyScene implements CenterListener { this.createPromise = new Promise((resolve, reject): void => { this.createPromiseResolve = resolve; - }) + }); this.connectionAnswerPromise = new Promise((resolve, reject): void => { this.connectionAnswerPromiseResolve = resolve; }); @@ -223,6 +229,11 @@ export class GameScene extends DirtyScene implements CenterListener { this.load.image(joystickBaseKey, joystickBaseImg); this.load.image(joystickThumbKey, joystickThumbImg); } + this.load.audio('audio-webrtc-in', '/resources/objects/webrtc-in.mp3'); + this.load.audio('audio-webrtc-out', '/resources/objects/webrtc-out.mp3'); + //this.load.audio('audio-report-message', '/resources/objects/report-message.mp3'); + this.sound.pauseOnBlur = false; + this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { // If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments) if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) { @@ -269,6 +280,14 @@ export class GameScene extends DirtyScene implements CenterListener { this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32}); this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + //eslint-disable-next-line @typescript-eslint/no-explicit-any + (this.load as any).rexWebFont({ + custom: { + families: ['Press Start 2P'], + urls: ['/resources/fonts/fonts.css'], + testString: 'abcdefg' + }, + }); //this function must stay at the end of preload function addLoader(this); @@ -418,7 +437,7 @@ export class GameScene extends DirtyScene implements CenterListener { } } if (layer.type === 'objectgroup' && layer.name === 'floorLayer') { - depth = 10000; + depth = DEPTH_OVERLAY_INDEX; } if (layer.type === 'objectgroup') { for (const object of layer.objects) { @@ -504,6 +523,23 @@ export class GameScene extends DirtyScene implements CenterListener { if (!this.room.isDisconnected()) { this.connect(); } + + this.emoteManager = new EmoteManager(this); + + let oldPeerNumber = 0; + this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { + const newPeerNumber = peers.size; + if (newPeerNumber > oldPeerNumber) { + this.sound.play('audio-webrtc-in', { + volume: 0.2 + }); + } else if (newPeerNumber < oldPeerNumber) { + this.sound.play('audio-webrtc-out', { + volume: 0.2 + }); + } + oldPeerNumber = newPeerNumber; + }); } /** @@ -536,6 +572,7 @@ export class GameScene extends DirtyScene implements CenterListener { characterLayers: message.characterLayers, name: message.name, position: message.position, + visitCardUrl: message.visitCardUrl, companion: message.companion } this.addPlayer(userMessage); @@ -608,6 +645,7 @@ export class GameScene extends DirtyScene implements CenterListener { // When connection is performed, let's connect SimplePeer this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName); + peerStore.connectToSimplePeer(this.simplePeer); this.GlobalMessageManager = new GlobalMessageManager(this.connection); userMessageManager.setReceiveBanListener(this.bannedUser.bind(this)); @@ -862,6 +900,24 @@ ${escapedMessage} this.userInputManager.disableControls(); })); + this.iframeSubscriptionList.push(iframeListener.playSoundStream.subscribe((playSoundEvent)=> + { + const url = new URL(playSoundEvent.url, this.MapUrlFile); + soundManager.playSound(this.load,this.sound,url.toString(),playSoundEvent.config); + })) + + this.iframeSubscriptionList.push(iframeListener.stopSoundStream.subscribe((stopSoundEvent)=> + { + const url = new URL(stopSoundEvent.url, this.MapUrlFile); + soundManager.stopSound(this.sound,url.toString()); + })) + + this.iframeSubscriptionList.push(iframeListener.loadSoundStream.subscribe((loadSoundEvent)=> + { + const url = new URL(loadSoundEvent.url, this.MapUrlFile); + soundManager.loadSound(this.load,this.sound,url.toString()); + })) + this.iframeSubscriptionList.push(iframeListener.enablePlayerControlStream.subscribe(()=>{ this.userInputManager.restoreControls(); })); @@ -890,6 +946,8 @@ ${escapedMessage} } private onMapExit(exitKey: string) { + if (this.mapTransitioning) return; + this.mapTransitioning = true; const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey); urlManager.pushStartLayerNameToUrl(hash); @@ -907,6 +965,7 @@ ${escapedMessage} this.initPositionFromLayerName(hash || defaultStartLayerName); this.CurrentPlayer.x = this.startX; this.CurrentPlayer.y = this.startY; + setTimeout(() => this.mapTransitioning = false, 500); } } @@ -928,6 +987,10 @@ ${escapedMessage} this.messageSubscription?.unsubscribe(); this.userInputManager.destroy(); this.pinchManager?.destroy(); + this.emoteManager.destroy(); + this.peerStoreUnsubscribe(); + + mediaManager.hideGameOverlay(); for(const iframeEvents of this.iframeSubscriptionList){ iframeEvents.unsubscribe(); @@ -1039,10 +1102,10 @@ ${escapedMessage} } //todo: push that into the gameManager - private async loadNextGame(exitSceneIdentifier: string){ + private loadNextGame(exitSceneIdentifier: string): void { const {roomId, hash} = Room.getIdFromIdentifier(exitSceneIdentifier, this.MapUrlFile, this.instance); const room = new Room(roomId); - await gameManager.loadMap(room, this.scene); + gameManager.loadMap(room, this.scene).catch(() => {}); } private startUser(layer: ITiledMapTileLayer): PositionInterface { @@ -1084,7 +1147,6 @@ ${escapedMessage} } createCollisionWithPlayer() { - this.physics.disableUpdate(); //add collision layer this.Layers.forEach((Layer: Phaser.Tilemaps.TilemapLayer) => { this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => { @@ -1118,6 +1180,15 @@ ${escapedMessage} this.companion, this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined ); + this.CurrentPlayer.on('pointerdown', (pointer: Phaser.Input.Pointer) => { + if (pointer.wasTouch && (pointer.event as TouchEvent).touches.length > 1) { + return; //we don't want the menu to open when pinching on a touch screen. + } + this.emoteManager.getMenuImages().then((emoteMenuElements) => this.CurrentPlayer.openOrCloseEmoteMenu(emoteMenuElements)) + }) + this.CurrentPlayer.on(requestEmoteEventName, (emoteKey: string) => { + this.connection?.emitEmoteEvent(emoteKey); + }) }catch (err){ if(err instanceof TextureError) { gameManager.leaveGame(this, SelectCharacterSceneName, new SelectCharacterScene()); @@ -1218,17 +1289,7 @@ ${escapedMessage} this.dirty = false; mediaManager.updateScene(); this.currentTick = time; - if (this.CurrentPlayer.isMoving()) { - this.dirty = true; - } this.CurrentPlayer.moveUser(delta); - if (this.CurrentPlayer.isMoving()) { - this.dirty = true; - this.physics.enableUpdate(); - } else { - this.physics.disableUpdate(); - } - // Let's handle all events while (this.pendingEvents.length !== 0) { @@ -1322,6 +1383,7 @@ ${escapedMessage} texturesPromise, addPlayerData.position.direction as PlayerAnimationDirections, addPlayerData.position.moving, + addPlayerData.visitCardUrl, addPlayerData.companion, addPlayerData.companion !== null ? lazyLoadCompanionResource(this.load, addPlayerData.companion) : undefined ); @@ -1428,8 +1490,8 @@ ${escapedMessage} this.connection?.emitActionableEvent(itemId, eventName, state, parameters); } - public onResize(ev: UIEvent): void { - super.onResize(ev); + public onResize(): void { + super.onResize(); this.reposition(); // Send new viewport to server diff --git a/front/src/Phaser/Game/PlayerMovement.ts b/front/src/Phaser/Game/PlayerMovement.ts index eb1a5d1b..89c4c1a4 100644 --- a/front/src/Phaser/Game/PlayerMovement.ts +++ b/front/src/Phaser/Game/PlayerMovement.ts @@ -1,6 +1,6 @@ -import {HasMovedEvent} from "./GameManager"; +import type {HasMovedEvent} from "./GameManager"; import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable"; -import {PositionInterface} from "../../Connexion/ConnexionModels"; +import type {PositionInterface} from "../../Connexion/ConnexionModels"; export class PlayerMovement { public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) { diff --git a/front/src/Phaser/Game/PlayersPositionInterpolator.ts b/front/src/Phaser/Game/PlayersPositionInterpolator.ts index 3ac87397..100aa1a0 100644 --- a/front/src/Phaser/Game/PlayersPositionInterpolator.ts +++ b/front/src/Phaser/Game/PlayersPositionInterpolator.ts @@ -2,8 +2,8 @@ * This class is in charge of computing the position of all players. * Player movement is delayed by 200ms so position depends on ticks. */ -import {PlayerMovement} from "./PlayerMovement"; -import {HasMovedEvent} from "./GameManager"; +import type {PlayerMovement} from "./PlayerMovement"; +import type {HasMovedEvent} from "./GameManager"; export class PlayersPositionInterpolator { playerMovements: Map = new Map(); diff --git a/front/src/Phaser/Game/SoundManager.ts b/front/src/Phaser/Game/SoundManager.ts new file mode 100644 index 00000000..47614fca --- /dev/null +++ b/front/src/Phaser/Game/SoundManager.ts @@ -0,0 +1,39 @@ +import LoaderPlugin = Phaser.Loader.LoaderPlugin; +import BaseSoundManager = Phaser.Sound.BaseSoundManager; +import BaseSound = Phaser.Sound.BaseSound; +import SoundConfig = Phaser.Types.Sound.SoundConfig; + +class SoundManager { + private soundPromises : Map> = new Map>(); + public loadSound (loadPlugin: LoaderPlugin, soundManager : BaseSoundManager, soundUrl: string) : Promise { + let soundPromise = this.soundPromises.get(soundUrl); + if (soundPromise !== undefined) { + return soundPromise; + } + soundPromise = new Promise((res) => { + + const sound = soundManager.get(soundUrl); + if (sound !== null) { + return res(sound); + } + loadPlugin.audio(soundUrl, soundUrl); + loadPlugin.once('filecomplete-audio-' + soundUrl, () => { + res(soundManager.add(soundUrl)); + }); + loadPlugin.start(); + }); + this.soundPromises.set(soundUrl,soundPromise); + return soundPromise; + } + + public async playSound(loadPlugin: LoaderPlugin, soundManager : BaseSoundManager, soundUrl: string, config: SoundConfig|undefined) : Promise { + const sound = await this.loadSound(loadPlugin,soundManager,soundUrl); + if (config === undefined) sound.play(); + else sound.play(config); + } + + public stopSound(soundManager : BaseSoundManager,soundUrl : string){ + soundManager.get(soundUrl).stop(); + } +} +export const soundManager = new SoundManager(); diff --git a/front/src/Phaser/Items/ActionableItem.ts b/front/src/Phaser/Items/ActionableItem.ts index fe4de385..f012b525 100644 --- a/front/src/Phaser/Items/ActionableItem.ts +++ b/front/src/Phaser/Items/ActionableItem.ts @@ -4,7 +4,7 @@ */ import Sprite = Phaser.GameObjects.Sprite; import {OutlinePipeline} from "../Shaders/OutlinePipeline"; -import {GameScene} from "../Game/GameScene"; +import type {GameScene} from "../Game/GameScene"; type EventCallback = (state: unknown, parameters: unknown) => void; diff --git a/front/src/Phaser/Items/Computer/computer.ts b/front/src/Phaser/Items/Computer/computer.ts index fdc7a358..8240d904 100644 --- a/front/src/Phaser/Items/Computer/computer.ts +++ b/front/src/Phaser/Items/Computer/computer.ts @@ -1,9 +1,9 @@ import * as Phaser from 'phaser'; import {Scene} from "phaser"; import Sprite = Phaser.GameObjects.Sprite; -import {ITiledMapObject} from "../../Map/ITiledMap"; -import {ItemFactoryInterface} from "../ItemFactoryInterface"; -import {GameScene} from "../../Game/GameScene"; +import type {ITiledMapObject} from "../../Map/ITiledMap"; +import type {ItemFactoryInterface} from "../ItemFactoryInterface"; +import type {GameScene} from "../../Game/GameScene"; import {ActionableItem} from "../ActionableItem"; import * as tg from "generic-type-guard"; diff --git a/front/src/Phaser/Items/ItemFactoryInterface.ts b/front/src/Phaser/Items/ItemFactoryInterface.ts index e3e52517..deff8fbb 100644 --- a/front/src/Phaser/Items/ItemFactoryInterface.ts +++ b/front/src/Phaser/Items/ItemFactoryInterface.ts @@ -1,7 +1,7 @@ +import type {GameScene} from "../Game/GameScene"; +import type {ITiledMapObject} from "../Map/ITiledMap"; +import type {ActionableItem} from "./ActionableItem"; import LoaderPlugin = Phaser.Loader.LoaderPlugin; -import {GameScene} from "../Game/GameScene"; -import {ITiledMapObject} from "../Map/ITiledMap"; -import {ActionableItem} from "./ActionableItem"; export interface ItemFactoryInterface { preload: (loader: LoaderPlugin) => void; diff --git a/front/src/Phaser/Login/AbstractCharacterScene.ts b/front/src/Phaser/Login/AbstractCharacterScene.ts index dfc98539..0d3a7c3b 100644 --- a/front/src/Phaser/Login/AbstractCharacterScene.ts +++ b/front/src/Phaser/Login/AbstractCharacterScene.ts @@ -1,8 +1,8 @@ import {ResizableScene} from "./ResizableScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; -import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; +import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager"; -import {CharacterTexture} from "../../Connexion/LocalUser"; +import type {CharacterTexture} from "../../Connexion/LocalUser"; export abstract class AbstractCharacterScene extends ResizableScene { @@ -38,4 +38,4 @@ export abstract class AbstractCharacterScene extends ResizableScene { const localUser = localUserStore.getLocalUser(); return localUser?.textures; } -} \ No newline at end of file +} diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index c20bc61e..4c19d944 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -6,11 +6,16 @@ import Container = Phaser.GameObjects.Container; import {gameManager} from "../Game/GameManager"; import {localUserStore} from "../../Connexion/LocalUserStore"; import {addLoader} from "../Components/Loader"; -import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; +import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {areCharacterLayersValid} from "../../Connexion/LocalUser"; import { MenuScene } from "../Menu/MenuScene"; import { SelectCharacterSceneName } from "./SelectCharacterScene"; +import {customCharacterSceneVisibleStore} from "../../Stores/CustomCharacterStore"; +import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore"; +import {waScaleManager} from "../Services/WaScaleManager"; +import {isMobile} from "../../Enum/EnvironmentVariable"; +import {CustomizedCharacter} from "../Entity/CustomizedCharacter"; export const CustomizeSceneName = "CustomizeScene"; @@ -21,11 +26,14 @@ export class CustomizeScene extends AbstractCharacterScene { private Rectangle!: Rectangle; private selectedLayers: number[] = [0]; - private containersRow: Container[][] = []; - private activeRow:number = 0; + private containersRow: CustomizedCharacter[][] = []; + public activeRow:number = 0; private layers: BodyResourceDescriptionInterface[][] = []; - private customizeSceneElement!: Phaser.GameObjects.DOMElement; + protected lazyloadingAttempt = true; //permit to update texture loaded after renderer + + private moveHorizontally: number = 0; + private moveVertically: number = 0; constructor() { super({ @@ -36,7 +44,6 @@ export class CustomizeScene extends AbstractCharacterScene { preload() { this.load.html(customizeSceneKey, 'resources/html/CustomCharacterScene.html'); - this.layers = loadAllLayers(this.load); this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => { bodyResourceDescriptions.forEach((bodyResourceDescription) => { if(bodyResourceDescription.level == undefined || bodyResourceDescription.level < 0 || bodyResourceDescription.level > 5 ){ @@ -44,43 +51,28 @@ export class CustomizeScene extends AbstractCharacterScene { } this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription); }); + this.lazyloadingAttempt = true; }); + this.layers = loadAllLayers(this.load); + this.lazyloadingAttempt = false; + + //this function must stay at the end of preload function addLoader(this); } create() { - this.customizeSceneElement = this.add.dom(-1000, 0).createFromCache(customizeSceneKey); - this.centerXDomElement(this.customizeSceneElement, 150); - MenuScene.revealMenusAfterInit(this.customizeSceneElement, customizeSceneKey); - - this.customizeSceneElement.addListener('click'); - this.customizeSceneElement.on('click', (event:MouseEvent) => { - event.preventDefault(); - if((event?.target as HTMLInputElement).id === 'customizeSceneButtonLeft') { - this.moveCursorHorizontally(-1); - }else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonRight') { - this.moveCursorHorizontally(1); - }else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonDown') { - this.moveCursorVertically(1); - }else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonUp') { - this.moveCursorVertically(-1); - }else if((event?.target as HTMLInputElement).id === 'customizeSceneFormBack') { - if(this.activeRow > 0){ - this.moveCursorVertically(-1); - }else{ - this.backToPreviousScene(); - } - }else if((event?.target as HTMLButtonElement).id === 'customizeSceneFormSubmit') { - if(this.activeRow < 5){ - this.moveCursorVertically(1); - }else{ - this.nextSceneToCamera(); - } - } + customCharacterSceneVisibleStore.set(true); + this.events.addListener('wake', () => { + waScaleManager.saveZoom(); + waScaleManager.zoomModifier = isMobile() ? 3 : 1; + customCharacterSceneVisibleStore.set(true); }); + waScaleManager.saveZoom(); + waScaleManager.zoomModifier = isMobile() ? 3 : 1; + this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 3, 32, 33) this.Rectangle.setStrokeStyle(2, 0xFFFFFF); this.add.existing(this.Rectangle); @@ -100,10 +92,13 @@ export class CustomizeScene extends AbstractCharacterScene { this.backToPreviousScene(); }); - this.input.keyboard.on('keyup-RIGHT', () => this.moveCursorHorizontally(1)); - this.input.keyboard.on('keyup-LEFT', () => this.moveCursorHorizontally(-1)); - this.input.keyboard.on('keyup-DOWN', () => this.moveCursorVertically(1)); - this.input.keyboard.on('keyup-UP', () => this.moveCursorVertically(-1)); + // Note: the key bindings are not directly put on the moveCursorVertically or moveCursorHorizontally methods + // because if 2 such events are fired close to one another, it makes the whole application crawl to a halt (for a reason I cannot + // explain, the list of sprites managed by the update list become immense + this.input.keyboard.on('keyup-RIGHT', () => this.moveHorizontally = 1); + this.input.keyboard.on('keyup-LEFT', () => this.moveHorizontally = -1); + this.input.keyboard.on('keyup-DOWN', () => this.moveVertically = 1); + this.input.keyboard.on('keyup-UP', () => this.moveVertically = -1); const customCursorPosition = localUserStore.getCustomCursorPosition(); if (customCursorPosition) { @@ -116,7 +111,15 @@ export class CustomizeScene extends AbstractCharacterScene { this.onResize(); } - private moveCursorHorizontally(index: number): void { + public moveCursorHorizontally(index: number): void { + this.moveHorizontally = index; + } + + public moveCursorVertically(index: number): void { + this.moveVertically = index; + } + + private doMoveCursorHorizontally(index: number): void { this.selectedLayers[this.activeRow] += index; if (this.selectedLayers[this.activeRow] < 0) { this.selectedLayers[this.activeRow] = 0 @@ -128,27 +131,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.saveInLocalStorage(); } - private moveCursorVertically(index:number): void { - - if(index === -1 && this.activeRow === 5){ - const button = this.customizeSceneElement.getChildByID('customizeSceneFormSubmit') as HTMLButtonElement; - button.innerHTML = `Next `; - } - - if(index === 1 && this.activeRow === 4){ - const button = this.customizeSceneElement.getChildByID('customizeSceneFormSubmit') as HTMLButtonElement; - button.innerText = 'Finish'; - } - - if(index === -1 && this.activeRow === 1){ - const button = this.customizeSceneElement.getChildByID('customizeSceneFormBack') as HTMLButtonElement; - button.innerText = `Return`; - } - - if(index === 1 && this.activeRow === 0){ - const button = this.customizeSceneElement.getChildByID('customizeSceneFormBack') as HTMLButtonElement; - button.innerHTML = `Back `; - } + private doMoveCursorVertically(index:number): void { this.activeRow += index; if (this.activeRow < 0) { @@ -197,20 +180,20 @@ export class CustomizeScene extends AbstractCharacterScene { * @param selectedItem, The number of the item select (0 for black body...) */ private generateCharacter(x: number, y: number, layerNumber: number, selectedItem: number) { - return new Container(this, x, y,this.getContainerChildren(layerNumber,selectedItem)); + return new CustomizedCharacter(this, x, y, this.getContainerChildren(layerNumber,selectedItem)); } - private getContainerChildren(layerNumber: number, selectedItem: number): Array { - const children: Array = new Array(); + private getContainerChildren(layerNumber: number, selectedItem: number): Array { + const children: Array = new Array(); for (let j = 0; j <= layerNumber; j++) { if (j === layerNumber) { - children.push(this.generateLayers(0, 0, this.layers[j][selectedItem].name)); + children.push(this.layers[j][selectedItem].name); } else { const layer = this.selectedLayers[j]; if (layer === undefined) { continue; } - children.push(this.generateLayers(0, 0, this.layers[j][layer].name)); + children.push(this.layers[j][layer].name); } } return children; @@ -247,30 +230,44 @@ export class CustomizeScene extends AbstractCharacterScene { * @return a new sprite */ private generateLayers(x: number, y: number, name: string): Sprite { - return new Sprite(this, x, y, name); + //return new Sprite(this, x, y, name); + return this.add.sprite(0, 0, name); } private updateSelectedLayer() { for(let i = 0; i < this.containersRow.length; i++){ for(let j = 0; j < this.containersRow[i].length; j++){ - const children = this.getContainerChildren(i, j); - this.containersRow[i][j].removeAll(true); - this.containersRow[i][j].add(children); + const children = this.getContainerChildren(i, j); + this.containersRow[i][j].updateSprites(children); } } } update(time: number, delta: number): void { + if(this.lazyloadingAttempt){ + this.moveLayers(); + this.lazyloadingAttempt = false; + } + + if (this.moveHorizontally !== 0) { + this.doMoveCursorHorizontally(this.moveHorizontally); + this.moveHorizontally = 0; + } + if (this.moveVertically !== 0) { + this.doMoveCursorVertically(this.moveVertically); + this.moveVertically = 0; + } } + + + public onResize(): void { this.moveLayers(); this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3; - - this.centerXDomElement(this.customizeSceneElement, 150); } private nextSceneToCamera(){ @@ -288,12 +285,16 @@ export class CustomizeScene extends AbstractCharacterScene { gameManager.setCharacterLayers(layers); this.scene.sleep(CustomizeSceneName); - this.scene.remove(SelectCharacterSceneName); + waScaleManager.restoreZoom(); + this.events.removeListener('wake'); gameManager.tryResumingGame(this, EnableCameraSceneName); + customCharacterSceneVisibleStore.set(false); } private backToPreviousScene(){ this.scene.sleep(CustomizeSceneName); + waScaleManager.restoreZoom(); this.scene.run(SelectCharacterSceneName); + customCharacterSceneVisibleStore.set(false); } } diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index 065973b6..ba27cd07 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -3,280 +3,48 @@ import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; import {mediaManager} from "../../WebRtc/MediaManager"; import {SoundMeter} from "../Components/SoundMeter"; -import {SoundMeterSprite} from "../Components/SoundMeterSprite"; import {HtmlUtils} from "../../WebRtc/HtmlUtils"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; import Zone = Phaser.GameObjects.Zone; import { MenuScene } from "../Menu/MenuScene"; import {ResizableScene} from "./ResizableScene"; +import { + enableCameraSceneVisibilityStore, +} from "../../Stores/MediaStore"; export const EnableCameraSceneName = "EnableCameraScene"; -enum LoginTextures { - playButton = "play_button", - icon = "icon", - mainFont = "main_font", - arrowRight = "arrow_right", - arrowUp = "arrow_up" -} - -const enableCameraSceneKey = 'enableCameraScene'; export class EnableCameraScene extends ResizableScene { - private textField!: TextField; - private cameraNameField!: TextField; - private arrowLeft!: Image; - private arrowRight!: Image; - private arrowDown!: Image; - private arrowUp!: Image; - private microphonesList: MediaDeviceInfo[] = new Array(); - private camerasList: MediaDeviceInfo[] = new Array(); - private cameraSelected: number = 0; - private microphoneSelected: number = 0; - private soundMeter: SoundMeter; - private soundMeterSprite!: SoundMeterSprite; - private microphoneNameField!: TextField; - - private enableCameraSceneElement!: Phaser.GameObjects.DOMElement; - - private mobileTapZone!: Zone; constructor() { super({ key: EnableCameraSceneName }); - this.soundMeter = new SoundMeter(); } preload() { - - this.load.html(enableCameraSceneKey, 'resources/html/EnableCameraScene.html'); - - this.load.image(LoginTextures.playButton, "resources/objects/play_button.png"); - this.load.image(LoginTextures.arrowRight, "resources/objects/arrow_right.png"); - this.load.image(LoginTextures.arrowUp, "resources/objects/arrow_up.png"); - // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap - this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); } create() { - this.enableCameraSceneElement = this.add.dom(-1000, 0).createFromCache(enableCameraSceneKey); - this.centerXDomElement(this.enableCameraSceneElement, 300); - - MenuScene.revealMenusAfterInit(this.enableCameraSceneElement, enableCameraSceneKey); - - const continuingButton = this.enableCameraSceneElement.getChildByID('enableCameraSceneFormSubmit') as HTMLButtonElement; - continuingButton.addEventListener('click', (e) => { - e.preventDefault(); - this.login(); - }); - - if (touchScreenManager.supportTouchScreen) { - new PinchManager(this); - } - //this.scale.setZoom(ZOOM_LEVEL); - //Phaser.Display.Align.In.BottomCenter(this.pressReturnField, zone); - - /* FIX ME */ - this.textField = new TextField(this, this.scale.width / 2, 20, ''); - - // For mobile purposes - we need a big enough touchable area. - this.mobileTapZone = this.add.zone(this.scale.width / 2,this.scale.height - 30,200,50) - .setInteractive().on("pointerdown", () => { - this.login(); - }); - - this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, ''); - - this.microphoneNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 40, ''); - - this.arrowRight = new Image(this, 0, 0, LoginTextures.arrowRight); - this.arrowRight.setVisible(false); - this.arrowRight.setInteractive().on('pointerdown', this.nextCam.bind(this)); - this.add.existing(this.arrowRight); - - this.arrowLeft = new Image(this, 0, 0, LoginTextures.arrowRight); - this.arrowLeft.setVisible(false); - this.arrowLeft.flipX = true; - this.arrowLeft.setInteractive().on('pointerdown', this.previousCam.bind(this)); - this.add.existing(this.arrowLeft); - - this.arrowUp = new Image(this, 0, 0, LoginTextures.arrowUp); - this.arrowUp.setVisible(false); - this.arrowUp.setInteractive().on('pointerdown', this.previousMic.bind(this)); - this.add.existing(this.arrowUp); - - this.arrowDown = new Image(this, 0, 0, LoginTextures.arrowUp); - this.arrowDown.setVisible(false); - this.arrowDown.flipY = true; - this.arrowDown.setInteractive().on('pointerdown', this.nextMic.bind(this)); - this.add.existing(this.arrowDown); - this.input.keyboard.on('keyup-ENTER', () => { this.login(); }); - HtmlUtils.getElementByIdOrFail('webRtcSetup').classList.add('active'); - - const mediaPromise = mediaManager.getCamera(); - mediaPromise.then(this.getDevices.bind(this)); - mediaPromise.then(this.setupStream.bind(this)); - - this.input.keyboard.on('keydown-RIGHT', this.nextCam.bind(this)); - this.input.keyboard.on('keydown-LEFT', this.previousCam.bind(this)); - this.input.keyboard.on('keydown-DOWN', this.nextMic.bind(this)); - this.input.keyboard.on('keydown-UP', this.previousMic.bind(this)); - - this.soundMeterSprite = new SoundMeterSprite(this, 50, 50); - this.soundMeterSprite.setVisible(false); - this.add.existing(this.soundMeterSprite); - - this.onResize(); - } - - private previousCam(): void { - if (this.cameraSelected === 0 || this.camerasList.length === 0) { - return; - } - this.cameraSelected--; - mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this)); - } - - private nextCam(): void { - if (this.cameraSelected === this.camerasList.length - 1 || this.camerasList.length === 0) { - return; - } - this.cameraSelected++; - // TODO: the change of camera should be OBSERVED (reactive) - mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this)); - } - - private previousMic(): void { - if (this.microphoneSelected === 0 || this.microphonesList.length === 0) { - return; - } - this.microphoneSelected--; - mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this)); - } - - private nextMic(): void { - if (this.microphoneSelected === this.microphonesList.length - 1 || this.microphonesList.length === 0) { - return; - } - this.microphoneSelected++; - // TODO: the change of camera should be OBSERVED (reactive) - mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this)); - } - - /** - * Function called each time a camera is changed - */ - private setupStream(stream: MediaStream): void { - const img = HtmlUtils.getElementByIdOrFail('webRtcSetupNoVideo'); - img.style.display = 'none'; - - const div = HtmlUtils.getElementByIdOrFail('myCamVideoSetup'); - div.srcObject = stream; - - this.soundMeter.connectToSource(stream, new window.AudioContext()); - this.soundMeterSprite.setVisible(true); - - this.updateWebCamName(); - } - - private updateWebCamName(): void { - if (this.camerasList.length > 1) { - let label = this.camerasList[this.cameraSelected].label; - // remove text in parenthesis - label = label.replace(/\([^()]*\)/g, '').trim(); - // remove accents - label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - this.cameraNameField.text = label; - - this.arrowRight.setVisible(this.cameraSelected < this.camerasList.length - 1); - this.arrowLeft.setVisible(this.cameraSelected > 0); - } - if (this.microphonesList.length > 1) { - let label = this.microphonesList[this.microphoneSelected].label; - // remove text in parenthesis - label = label.replace(/\([^()]*\)/g, '').trim(); - // remove accents - label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - - this.microphoneNameField.text = label; - - this.arrowDown.setVisible(this.microphoneSelected < this.microphonesList.length - 1); - this.arrowUp.setVisible(this.microphoneSelected > 0); - - } + enableCameraSceneVisibilityStore.showEnableCameraScene(); } public onResize(): void { - let div = HtmlUtils.getElementByIdOrFail('myCamVideoSetup'); - let bounds = div.getBoundingClientRect(); - if (!div.srcObject) { - div = HtmlUtils.getElementByIdOrFail('webRtcSetup'); - bounds = div.getBoundingClientRect(); - } - - this.textField.x = this.game.renderer.width / 2; - this.mobileTapZone.x = this.game.renderer.width / 2; - this.cameraNameField.x = this.game.renderer.width / 2; - this.microphoneNameField.x = this.game.renderer.width / 2; - - this.cameraNameField.y = bounds.top / this.scale.zoom - 8; - - this.soundMeterSprite.x = this.game.renderer.width / 2 - this.soundMeterSprite.getWidth() / 2; - this.soundMeterSprite.y = bounds.bottom / this.scale.zoom + 16; - - this.microphoneNameField.y = this.soundMeterSprite.y + 22; - - this.arrowRight.x = bounds.right / this.scale.zoom + 16; - this.arrowRight.y = (bounds.top + bounds.height / 2) / this.scale.zoom; - - this.arrowLeft.x = bounds.left / this.scale.zoom - 16; - this.arrowLeft.y = (bounds.top + bounds.height / 2) / this.scale.zoom; - - this.arrowDown.x = this.microphoneNameField.x + this.microphoneNameField.width / 2 + 16; - this.arrowDown.y = this.microphoneNameField.y; - - this.arrowUp.x = this.microphoneNameField.x - this.microphoneNameField.width / 2 - 16; - this.arrowUp.y = this.microphoneNameField.y; - - const actionBtn = document.querySelector('#enableCameraScene .action'); - if (actionBtn !== null) { - actionBtn.style.top = (this.scale.height - 65) + 'px'; - } } update(time: number, delta: number): void { - this.soundMeterSprite.setVolume(this.soundMeter.getVolume()); - mediaManager.updateScene(); - - this.centerXDomElement(this.enableCameraSceneElement, 300); } - private login(): void { - HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; - this.soundMeter.stop(); + public login(): void { + enableCameraSceneVisibilityStore.hideEnableCameraScene(); - mediaManager.stopCamera(); - mediaManager.stopMicrophone(); - - this.scene.sleep(EnableCameraSceneName) + this.scene.sleep(EnableCameraSceneName); gameManager.goToStartingMap(this.scene); } - - private async getDevices() { - const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices(); - for (const mediaDeviceInfo of mediaDeviceInfos) { - if (mediaDeviceInfo.kind === 'audioinput') { - this.microphonesList.push(mediaDeviceInfo); - } else if (mediaDeviceInfo.kind === 'videoinput') { - this.camerasList.push(mediaDeviceInfo); - } - } - this.updateWebCamName(); - } } diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 435592f2..39a8f5f3 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -1,17 +1,12 @@ import {gameManager} from "../Game/GameManager"; import {SelectCharacterSceneName} from "./SelectCharacterScene"; import {ResizableScene} from "./ResizableScene"; -import { localUserStore } from "../../Connexion/LocalUserStore"; -import {MenuScene} from "../Menu/MenuScene"; -import { isUserNameValid } from "../../Connexion/LocalUser"; +import {loginSceneVisibleStore} from "../../Stores/LoginSceneStore"; export const LoginSceneName = "LoginScene"; -const loginSceneKey = 'loginScene'; - export class LoginScene extends ResizableScene { - private loginSceneElement!: Phaser.GameObjects.DOMElement; private name: string = ''; constructor() { @@ -22,65 +17,25 @@ export class LoginScene extends ResizableScene { } preload() { - this.load.html(loginSceneKey, 'resources/html/loginScene.html'); } create() { - this.loginSceneElement = this.add.dom(-1000, 0).createFromCache(loginSceneKey); - this.centerXDomElement(this.loginSceneElement, 200); - MenuScene.revealMenusAfterInit(this.loginSceneElement, loginSceneKey); - - const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement; - const inputElement = this.loginSceneElement.getChildByID('loginSceneName') as HTMLInputElement; - inputElement.value = localUserStore.getName() ?? ''; - inputElement.focus(); - inputElement.addEventListener('keypress', (event: KeyboardEvent) => { - if(inputElement.value.length > 7){ - event.preventDefault(); - return; - } - pErrorElement.innerHTML = ''; - if(inputElement.value && !isUserNameValid(inputElement.value)){ - pErrorElement.innerHTML = 'Invalid user name: only letters and numbers are allowed. No spaces.'; - } - if (event.key === 'Enter') { - event.preventDefault(); - this.login(inputElement); - return; - } - }); - - const continuingButton = this.loginSceneElement.getChildByID('loginSceneFormSubmit') as HTMLButtonElement; - continuingButton.addEventListener('click', (e) => { - e.preventDefault(); - this.login(inputElement); - }); + loginSceneVisibleStore.set(true); } - private login(inputElement: HTMLInputElement): void { - const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement; - this.name = inputElement.value; - if (this.name === '') { - pErrorElement.innerHTML = 'The name is empty'; - return - } - if(!isUserNameValid(this.name)){ - pErrorElement.innerHTML = 'Invalid user name: only letters and numbers are allowed. No spaces.'; - return - } - if (this.name === '') return - gameManager.setPlayerName(this.name); + public login(name: string): void { + name = name.trim(); + gameManager.setPlayerName(name); this.scene.stop(LoginSceneName) gameManager.tryResumingGame(this, SelectCharacterSceneName); - this.scene.remove(LoginSceneName) + this.scene.remove(LoginSceneName); + loginSceneVisibleStore.set(false); } update(time: number, delta: number): void { - } - public onResize(ev: UIEvent): void { - this.centerXDomElement(this.loginSceneElement, 200); + public onResize(): void { } } diff --git a/front/src/Phaser/Login/ResizableScene.ts b/front/src/Phaser/Login/ResizableScene.ts index 39e2d74b..d06cb66c 100644 --- a/front/src/Phaser/Login/ResizableScene.ts +++ b/front/src/Phaser/Login/ResizableScene.ts @@ -2,7 +2,7 @@ import {Scene} from "phaser"; import DOMElement = Phaser.GameObjects.DOMElement; export abstract class ResizableScene extends Scene { - public abstract onResize(ev: UIEvent): void; + public abstract onResize(): void; /** * Centers the DOM element on the X axis. @@ -17,7 +17,7 @@ export abstract class ResizableScene extends Scene { && object.node && object.node.getBoundingClientRect().width > 0 ? (object.node.getBoundingClientRect().width / 2 / this.scale.zoom) - : (300 / this.scale.zoom) + : (defaultWidth / this.scale.zoom) ); } } diff --git a/front/src/Phaser/Login/SelectCharacterMobileScene.ts b/front/src/Phaser/Login/SelectCharacterMobileScene.ts index e29ab798..0d8e49d5 100644 --- a/front/src/Phaser/Login/SelectCharacterMobileScene.ts +++ b/front/src/Phaser/Login/SelectCharacterMobileScene.ts @@ -1,66 +1,53 @@ -import {gameManager} from "../Game/GameManager"; -import {TextField} from "../Components/TextField"; -import Image = Phaser.GameObjects.Image; -import Rectangle = Phaser.GameObjects.Rectangle; -import {EnableCameraSceneName} from "./EnableCameraScene"; -import {CustomizeSceneName} from "./CustomizeScene"; -import {localUserStore} from "../../Connexion/LocalUserStore"; -import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager"; -import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; -import {AbstractCharacterScene} from "./AbstractCharacterScene"; -import {areCharacterLayersValid} from "../../Connexion/LocalUser"; -import {touchScreenManager} from "../../Touch/TouchScreenManager"; -import {PinchManager} from "../UserInput/PinchManager"; -import {MenuScene} from "../Menu/MenuScene"; -import { SelectCharacterScene, SelectCharacterSceneName } from "./SelectCharacterScene"; +import { SelectCharacterScene } from "./SelectCharacterScene"; export class SelectCharacterMobileScene extends SelectCharacterScene { create(){ super.create(); + this.onResize(); this.selectedRectangle.destroy(); } - - protected defineSetupPlayer(numero: number){ + + protected defineSetupPlayer(num: number){ const deltaX = 30; const deltaY = 2; let [playerX, playerY] = this.getCharacterPosition(); let playerVisible = true; let playerScale = 1.5; - let playserOpactity = 1; + let playerOpacity = 1; - if( this.currentSelectUser !== numero ){ + if( this.currentSelectUser !== num ){ playerVisible = false; } - if( numero === (this.currentSelectUser + 1) ){ + if( num === (this.currentSelectUser + 1) ){ playerY -= deltaY; playerX += deltaX; playerScale = 0.8; - playserOpactity = 0.6; + playerOpacity = 0.6; playerVisible = true; } - if( numero === (this.currentSelectUser + 2) ){ + if( num === (this.currentSelectUser + 2) ){ playerY -= deltaY; playerX += (deltaX * 2); playerScale = 0.8; - playserOpactity = 0.6; + playerOpacity = 0.6; playerVisible = true; } - if( numero === (this.currentSelectUser - 1) ){ + if( num === (this.currentSelectUser - 1) ){ playerY -= deltaY; playerX -= deltaX; playerScale = 0.8; - playserOpactity = 0.6; + playerOpacity = 0.6; playerVisible = true; } - if( numero === (this.currentSelectUser - 2) ){ + if( num === (this.currentSelectUser - 2) ){ playerY -= deltaY; playerX -= (deltaX * 2); playerScale = 0.8; - playserOpactity = 0.6; + playerOpacity = 0.6; playerVisible = true; } - return {playerX, playerY, playerScale, playserOpactity, playerVisible} + return {playerX, playerY, playerScale, playerOpacity, playerVisible} } /** diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 0366dde5..0d01bd88 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -1,18 +1,20 @@ import {gameManager} from "../Game/GameManager"; -import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; import {EnableCameraSceneName} from "./EnableCameraScene"; import {CustomizeSceneName} from "./CustomizeScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager"; import {addLoader} from "../Components/Loader"; -import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; +import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {areCharacterLayersValid} from "../../Connexion/LocalUser"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; import {MenuScene} from "../Menu/MenuScene"; -import { SelectCharacterMobileScene } from "./SelectCharacterMobileScene"; +import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore"; +import {customCharacterSceneVisibleStore} from "../../Stores/CustomCharacterStore"; +import {waScaleManager} from "../Services/WaScaleManager"; +import {isMobile} from "../../Enum/EnvironmentVariable"; //todo: put this constants in a dedicated file export const SelectCharacterSceneName = "SelectCharacterScene"; @@ -29,6 +31,10 @@ export class SelectCharacterScene extends AbstractCharacterScene { protected selectCharacterSceneElement!: Phaser.GameObjects.DOMElement; protected currentSelectUser = 0; + protected pointerClicked: boolean = false; + protected pointerTimer: number = 0; + + protected lazyloadingAttempt = true; //permit to update texture loaded after renderer constructor() { super({ @@ -43,44 +49,36 @@ export class SelectCharacterScene extends AbstractCharacterScene { bodyResourceDescriptions.forEach((bodyResourceDescription) => { this.playerModels.push(bodyResourceDescription); }); - }) + this.lazyloadingAttempt = true; + }); this.playerModels = loadAllDefaultModels(this.load); + this.lazyloadingAttempt = false; //this function must stay at the end of preload function addLoader(this); } create() { - - this.selectCharacterSceneElement = this.add.dom(-1000, 0).createFromCache(selectCharacterKey); - this.centerXDomElement(this.selectCharacterSceneElement, 150); - MenuScene.revealMenusAfterInit(this.selectCharacterSceneElement, selectCharacterKey); - - this.selectCharacterSceneElement.addListener('click'); - this.selectCharacterSceneElement.on('click', (event:MouseEvent) => { - event.preventDefault(); - if((event?.target as HTMLInputElement).id === 'selectCharacterButtonLeft') { - this.moveToLeft(); - }else if((event?.target as HTMLInputElement).id === 'selectCharacterButtonRight') { - this.moveToRight(); - }else if((event?.target as HTMLInputElement).id === 'selectCharacterSceneFormSubmit') { - this.nextSceneToCameraScene(); - }else if((event?.target as HTMLInputElement).id === 'selectCharacterSceneFormCustomYourOwnSubmit') { - this.nextSceneToCustomizeScene(); - } + selectCharacterSceneVisibleStore.set(true); + this.events.addListener('wake', () => { + waScaleManager.saveZoom(); + waScaleManager.zoomModifier = isMobile() ? 2 : 1; + selectCharacterSceneVisibleStore.set(true); }); if (touchScreenManager.supportTouchScreen) { new PinchManager(this); } + waScaleManager.saveZoom(); + waScaleManager.zoomModifier = isMobile() ? 2 : 1; + const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF); this.selectedRectangle.setDepth(2); /*create user*/ this.createCurrentPlayer(); - const playerNumber = localUserStore.getPlayerCharacterIndex(); this.input.keyboard.on('keyup-ENTER', () => { return this.nextSceneToCameraScene(); @@ -108,9 +106,12 @@ export class SelectCharacterScene extends AbstractCharacterScene { return; } this.scene.stop(SelectCharacterSceneName); + waScaleManager.restoreZoom(); gameManager.setCharacterLayers([this.selectedPlayer.texture.key]); gameManager.tryResumingGame(this, EnableCameraSceneName); - this.scene.remove(SelectCharacterSceneName); + this.players = []; + selectCharacterSceneVisibleStore.set(false); + this.events.removeListener('wake'); } protected nextSceneToCustomizeScene(): void { @@ -118,13 +119,20 @@ export class SelectCharacterScene extends AbstractCharacterScene { return; } this.scene.sleep(SelectCharacterSceneName); + waScaleManager.restoreZoom(); this.scene.run(CustomizeSceneName); + selectCharacterSceneVisibleStore.set(false); } - createCurrentPlayer(): void { + createCurrentPlayer(): void { for (let i = 0; i c.texture.key === playerResource.name)){ + continue; + } + const [middleX, middleY] = this.getCharacterPosition(); const player = this.physics.add.sprite(middleX, middleY, playerResource.name, 0); this.setUpPlayer(player, i); @@ -135,15 +143,22 @@ export class SelectCharacterScene extends AbstractCharacterScene { repeat: -1 }); player.setInteractive().on("pointerdown", () => { - if(this.currentSelectUser === i){ + if (this.pointerClicked) { return; } + if (this.currentSelectUser === i) { + return; + } + //To not trigger two time the pointerdown events : + // We set a boolean to true so that pointerdown events does nothing when the boolean is true + // We set a timer that we decrease in update function to not trigger the pointerdown events twice + this.pointerClicked = true; + this.pointerTimer = 250; this.currentSelectUser = i; this.moveUser(); }); this.players.push(player); } - this.selectedPlayer = this.players[this.currentSelectUser]; this.selectedPlayer.play(this.playerModels[this.currentSelectUser].name); } @@ -188,35 +203,35 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.moveUser(); } - protected defineSetupPlayer(numero: number){ + protected defineSetupPlayer(num: number){ const deltaX = 32; const deltaY = 32; let [playerX, playerY] = this.getCharacterPosition(); // player X and player y are middle of the - playerX = ( (playerX - (deltaX * 2.5)) + ((deltaX) * (numero % this.nbCharactersPerRow)) ); // calcul position on line users - playerY = ( (playerY - (deltaY * 2)) + ((deltaY) * ( Math.floor(numero / this.nbCharactersPerRow) )) ); // calcul position on column users + playerX = ( (playerX - (deltaX * 2.5)) + ((deltaX) * (num % this.nbCharactersPerRow)) ); // calcul position on line users + playerY = ( (playerY - (deltaY * 2)) + ((deltaY) * ( Math.floor(num / this.nbCharactersPerRow) )) ); // calcul position on column users const playerVisible = true; const playerScale = 1; - const playserOpactity = 1; + const playerOpacity = 1; // if selected - if( numero === this.currentSelectUser ){ + if( num === this.currentSelectUser ){ this.selectedRectangle.setX(playerX); this.selectedRectangle.setY(playerY); } - return {playerX, playerY, playerScale, playserOpactity, playerVisible} + return {playerX, playerY, playerScale, playerOpacity, playerVisible} } - protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, numero: number){ + protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, num: number){ - const {playerX, playerY, playerScale, playserOpactity, playerVisible} = this.defineSetupPlayer(numero); + const {playerX, playerY, playerScale, playerOpacity, playerVisible} = this.defineSetupPlayer(num); player.setBounce(0.2); - player.setCollideWorldBounds(true); + player.setCollideWorldBounds(false); player.setVisible( playerVisible ); player.setScale(playerScale, playerScale); - player.setAlpha(playserOpactity); + player.setAlpha(playerOpacity); player.setX(playerX); player.setY(playerY); } @@ -240,12 +255,23 @@ export class SelectCharacterScene extends AbstractCharacterScene { } update(time: number, delta: number): void { + // pointerTimer is set to 250 when pointerdown events is trigger + // After 250ms, pointerClicked is set to false and the pointerdown events can be trigger again + this.pointerTimer -= delta; + if (this.pointerTimer <= 0) { + this.pointerClicked = false; + } + + if(this.lazyloadingAttempt){ + //re-render players list + this.createCurrentPlayer(); + this.moveUser(); + this.lazyloadingAttempt = false; + } } - public onResize(ev: UIEvent): void { + public onResize(): void { //move position of user this.moveUser(); - - this.centerXDomElement(this.selectCharacterSceneElement, 150); } } diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index 4ef378d6..4c29f942 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -5,23 +5,26 @@ import { gameManager} from "../Game/GameManager"; import { ResizableScene } from "./ResizableScene"; import { EnableCameraSceneName } from "./EnableCameraScene"; import { localUserStore } from "../../Connexion/LocalUserStore"; -import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; +import type { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; import { MenuScene } from "../Menu/MenuScene"; +import {selectCompanionSceneVisibleStore} from "../../Stores/SelectCompanionStore"; +import {waScaleManager} from "../Services/WaScaleManager"; +import {isMobile} from "../../Enum/EnvironmentVariable"; export const SelectCompanionSceneName = "SelectCompanionScene"; -const selectCompanionSceneKey = 'selectCompanionScene'; - export class SelectCompanionScene extends ResizableScene { private selectedCompanion!: Phaser.Physics.Arcade.Sprite; private companions: Array = new Array(); private companionModels: Array = []; + private saveZoom: number = 0; - private selectCompanionSceneElement!: Phaser.GameObjects.DOMElement; private currentCompanion = 0; + private pointerClicked: boolean = false; + private pointerTimer: number = 0; constructor() { super({ @@ -30,8 +33,6 @@ export class SelectCompanionScene extends ResizableScene { } preload() { - this.load.html(selectCompanionSceneKey, 'resources/html/SelectCompanionScene.html'); - getAllCompanionResources(this.load).forEach(model => { this.companionModels.push(model); }); @@ -42,30 +43,17 @@ export class SelectCompanionScene extends ResizableScene { create() { - this.selectCompanionSceneElement = this.add.dom(-1000, 0).createFromCache(selectCompanionSceneKey); - this.centerXDomElement(this.selectCompanionSceneElement, 150); - MenuScene.revealMenusAfterInit(this.selectCompanionSceneElement, selectCompanionSceneKey); + selectCompanionSceneVisibleStore.set(true); - this.selectCompanionSceneElement.addListener('click'); - this.selectCompanionSceneElement.on('click', (event:MouseEvent) => { - event.preventDefault(); - if((event?.target as HTMLInputElement).id === 'selectCharacterButtonLeft') { - this.moveToLeft(); - }else if((event?.target as HTMLInputElement).id === 'selectCharacterButtonRight') { - this.moveToRight(); - }else if((event?.target as HTMLInputElement).id === 'selectCompanionSceneFormSubmit') { - this.nextScene(); - }else if((event?.target as HTMLInputElement).id === 'selectCompanionSceneFormBack') { - this._nextScene(); - } - }); + waScaleManager.saveZoom(); + waScaleManager.zoomModifier = isMobile() ? 2 : 1; if (touchScreenManager.supportTouchScreen) { new PinchManager(this); } // input events - this.input.keyboard.on('keyup-ENTER', this.nextScene.bind(this)); + this.input.keyboard.on('keyup-ENTER', this.selectCompanion.bind(this)); this.input.keyboard.on('keydown-RIGHT', this.moveToRight.bind(this)); this.input.keyboard.on('keydown-LEFT', this.moveToLeft.bind(this)); @@ -86,21 +74,28 @@ export class SelectCompanionScene extends ResizableScene { } update(time: number, delta: number): void { - + // pointerTimer is set to 250 when pointerdown events is trigger + // After 250ms, pointerClicked is set to false and the pointerdown events can be trigger again + this.pointerTimer -= delta; + if (this.pointerTimer <= 0) { + this.pointerClicked = false; + } } - private nextScene(): void { + public selectCompanion(): void { localUserStore.setCompanion(this.companionModels[this.currentCompanion].name); gameManager.setCompanion(this.companionModels[this.currentCompanion].name); - this._nextScene(); + this.closeScene(); } - private _nextScene(){ + public closeScene(){ // next scene this.scene.stop(SelectCompanionSceneName); + waScaleManager.restoreZoom(); gameManager.tryResumingGame(this, EnableCameraSceneName); this.scene.remove(SelectCompanionSceneName); + selectCompanionSceneVisibleStore.set(false); } private createCurrentCompanion(): void { @@ -117,6 +112,14 @@ export class SelectCompanionScene extends ResizableScene { }); companion.setInteractive().on("pointerdown", () => { + if (this.pointerClicked) { + return; + } + //To not trigger two time the pointerdown events : + // We set a boolean to true so that pointerdown events does nothing when the boolean is true + // We set a timer that we decrease in update function to not trigger the pointerdown events twice + this.pointerClicked = true; + this.pointerTimer = 250; this.currentCompanion = i; this.moveCompanion(); }); @@ -126,10 +129,8 @@ export class SelectCompanionScene extends ResizableScene { this.selectedCompanion = this.companions[this.currentCompanion]; } - public onResize(ev: UIEvent): void { + public onResize(): void { this.moveCompanion(); - - this.centerXDomElement(this.selectCompanionSceneElement, 150); } private updateSelectedCompanion(): void { @@ -147,15 +148,7 @@ export class SelectCompanionScene extends ResizableScene { this.updateSelectedCompanion(); } - private moveToLeft(){ - if(this.currentCompanion === 0){ - return; - } - this.currentCompanion -= 1; - this.moveCompanion(); - } - - private moveToRight(){ + public moveToRight(){ if(this.currentCompanion === (this.companions.length - 1)){ return; } @@ -163,38 +156,46 @@ export class SelectCompanionScene extends ResizableScene { this.moveCompanion(); } - private defineSetupCompanion(numero: number){ + public moveToLeft(){ + if(this.currentCompanion === 0){ + return; + } + this.currentCompanion -= 1; + this.moveCompanion(); + } + + private defineSetupCompanion(num: number){ const deltaX = 30; const deltaY = 2; let [companionX, companionY] = this.getCompanionPosition(); let companionVisible = true; let companionScale = 1.5; let companionOpactity = 1; - if( this.currentCompanion !== numero ){ + if( this.currentCompanion !== num ){ companionVisible = false; } - if( numero === (this.currentCompanion + 1) ){ + if( num === (this.currentCompanion + 1) ){ companionY -= deltaY; companionX += deltaX; companionScale = 0.8; companionOpactity = 0.6; companionVisible = true; } - if( numero === (this.currentCompanion + 2) ){ + if( num === (this.currentCompanion + 2) ){ companionY -= deltaY; companionX += (deltaX * 2); companionScale = 0.8; companionOpactity = 0.6; companionVisible = true; } - if( numero === (this.currentCompanion - 1) ){ + if( num === (this.currentCompanion - 1) ){ companionY -= deltaY; companionX -= deltaX; companionScale = 0.8; companionOpactity = 0.6; companionVisible = true; } - if( numero === (this.currentCompanion - 2) ){ + if( num === (this.currentCompanion - 2) ){ companionY -= deltaY; companionX -= (deltaX * 2); companionScale = 0.8; diff --git a/front/src/Phaser/Map/LayersIterator.ts b/front/src/Phaser/Map/LayersIterator.ts index 501a5f7b..c39f05d8 100644 --- a/front/src/Phaser/Map/LayersIterator.ts +++ b/front/src/Phaser/Map/LayersIterator.ts @@ -1,4 +1,4 @@ -import {ITiledMap, ITiledMapLayer} from "./ITiledMap"; +import type {ITiledMap, ITiledMapLayer} from "./ITiledMap"; /** * Iterates over the layers of a map, flattening the grouped layers diff --git a/front/src/Phaser/Menu/HelpCameraSettingsScene.ts b/front/src/Phaser/Menu/HelpCameraSettingsScene.ts deleted file mode 100644 index 429e197b..00000000 --- a/front/src/Phaser/Menu/HelpCameraSettingsScene.ts +++ /dev/null @@ -1,147 +0,0 @@ -import {mediaManager} from "../../WebRtc/MediaManager"; -import {HtmlUtils} from "../../WebRtc/HtmlUtils"; -import {localUserStore} from "../../Connexion/LocalUserStore"; -import {DirtyScene} from "../Game/DirtyScene"; - -export const HelpCameraSettingsSceneName = 'HelpCameraSettingsScene'; -const helpCameraSettings = 'helpCameraSettings'; -/** - * The scene that show how to permit Camera and Microphone access if there are not already allowed - */ -export class HelpCameraSettingsScene extends DirtyScene { - private helpCameraSettingsElement!: Phaser.GameObjects.DOMElement; - private helpCameraSettingsOpened: boolean = false; - - constructor() { - super({key: HelpCameraSettingsSceneName}); - } - - preload() { - this.load.html(helpCameraSettings, 'resources/html/helpCameraSettings.html'); - } - - create(){ - this.createHelpCameraSettings(); - } - - private createHelpCameraSettings() : void { - const middleX = this.getMiddleX(); - this.helpCameraSettingsElement = this.add.dom(middleX, -800, undefined, {overflow: 'scroll'}).createFromCache(helpCameraSettings); - this.revealMenusAfterInit(this.helpCameraSettingsElement, helpCameraSettings); - this.helpCameraSettingsElement.addListener('click'); - this.helpCameraSettingsElement.on('click', (event:MouseEvent) => { - if((event?.target as HTMLInputElement).id === 'mailto') { - return; - } - event.preventDefault(); - if((event?.target as HTMLInputElement).id === 'helpCameraSettingsFormRefresh') { - window.location.reload(); - }else if((event?.target as HTMLInputElement).id === 'helpCameraSettingsFormContinue') { - this.closeHelpCameraSettingsOpened(); - } - }); - - if(!localUserStore.getHelpCameraSettingsShown() && (!mediaManager.constraintsMedia.audio || !mediaManager.constraintsMedia.video)){ - this.openHelpCameraSettingsOpened(); - localUserStore.setHelpCameraSettingsShown(); - } - - mediaManager.setHelpCameraSettingsCallBack(() => { - this.openHelpCameraSettingsOpened(); - }); - } - - private openHelpCameraSettingsOpened(): void{ - HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; - this.helpCameraSettingsOpened = true; - try{ - if(window.navigator.userAgent.includes('Firefox')){ - HtmlUtils.getElementByIdOrFail('browserHelpSetting').innerHTML =''; - }else if(window.navigator.userAgent.includes('Chrome')){ - HtmlUtils.getElementByIdOrFail('browserHelpSetting').innerHTML =''; - } - }catch(err) { - console.error('openHelpCameraSettingsOpened => getElementByIdOrFail => error', err); - } - const middleY = this.getMiddleY(); - const middleX = this.getMiddleX(); - this.tweens.add({ - targets: this.helpCameraSettingsElement, - y: middleY, - x: middleX, - duration: 1000, - ease: 'Power3', - overflow: 'scroll' - }); - - this.dirty = true; - } - - private closeHelpCameraSettingsOpened(): void{ - const middleX = this.getMiddleX(); - /*const helpCameraSettingsInfo = this.helpCameraSettingsElement.getChildByID('helpCameraSettings') as HTMLParagraphElement; - helpCameraSettingsInfo.innerText = ''; - helpCameraSettingsInfo.style.display = 'none';*/ - this.helpCameraSettingsOpened = false; - this.tweens.add({ - targets: this.helpCameraSettingsElement, - y: -1000, - x: middleX, - duration: 1000, - ease: 'Power3', - overflow: 'scroll' - }); - - this.dirty = true; - } - - private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) { - //Dom elements will appear inside the viewer screen when creating before being moved out of it, which create a flicker effect. - //To prevent this, we put a 'hidden' attribute on the root element, we remove it only after the init is done. - setTimeout(() => { - (menuElement.getChildByID(rootDomId) as HTMLElement).hidden = false; - }, 250); - } - - update(time: number, delta: number): void { - this.dirty = false; - } - - public onResize(ev: UIEvent): void { - super.onResize(ev); - const middleX = this.getMiddleX(); - const middleY = this.getMiddleY(); - this.tweens.add({ - targets: this.helpCameraSettingsElement, - x: middleX, - y: middleY, - duration: 1000, - ease: 'Power3' - }); - } - - private getMiddleX() : number{ - return (this.scale.width / 2) - - ( - this.helpCameraSettingsElement - && this.helpCameraSettingsElement.node - && this.helpCameraSettingsElement.node.getBoundingClientRect().width > 0 - ? (this.helpCameraSettingsElement.node.getBoundingClientRect().width / (2 * this.scale.zoom)) - : (400 / 2) - ); - } - - private getMiddleY() : number{ - const middleY = ((this.scale.height) - ( - (this.helpCameraSettingsElement - && this.helpCameraSettingsElement.node - && this.helpCameraSettingsElement.node.getBoundingClientRect().height > 0 - ? this.helpCameraSettingsElement.node.getBoundingClientRect().height : 400 /*FIXME to use a const will be injected in HTMLElement*/)/this.scale.zoom)) / 2; - return (middleY > 0 ? middleY : 0); - } - - public isDirty(): boolean { - return this.dirty; - } -} - diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index f26c00c6..54fa395a 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -9,6 +9,8 @@ import {connectionManager} from "../../Connexion/ConnectionManager"; import {GameConnexionTypes} from "../../Url/UrlManager"; import {WarningContainer, warningContainerHtml, warningContainerKey} from "../Components/WarningContainer"; import {worldFullWarningStream} from "../../Connexion/WorldFullWarningStream"; +import {menuIconVisible} from "../../Stores/MenuStore"; +import {videoConstraintStore} from "../../Stores/MediaStore"; export const MenuSceneName = 'MenuScene'; const gameMenuKey = 'gameMenu'; @@ -53,6 +55,7 @@ export class MenuScene extends Phaser.Scene { } create() { + menuIconVisible.set(true); this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey); this.menuElement.setOrigin(0); MenuScene.revealMenusAfterInit(this.menuElement, 'gameMenu'); @@ -322,7 +325,7 @@ export class MenuScene extends Phaser.Scene { if (valueVideo !== this.videoQualityValue) { this.videoQualityValue = valueVideo; localUserStore.setVideoQualityValue(valueVideo); - mediaManager.updateCameraQuality(valueVideo); + videoConstraintStore.setFrameRate(valueVideo); } this.closeGameQualityMenu(); } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 3db7f051..7ed366f7 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -1,18 +1,18 @@ import {PlayerAnimationDirections} from "./Animation"; -import {GameScene} from "../Game/GameScene"; +import type {GameScene} from "../Game/GameScene"; import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; import {Character} from "../Entity/Character"; +import {userMovingStore} from "../../Stores/GameStore"; +import {RadialMenu, RadialMenuClickEvent, RadialMenuItem} from "../Components/RadialMenu"; export const hasMovedEventName = "hasMoved"; -export interface CurrentGamerInterface extends Character{ - moveUser(delta: number) : void; - say(text : string) : void; - isMoving(): boolean; -} +export const requestEmoteEventName = "requestEmote"; -export class Player extends Character implements CurrentGamerInterface { +export class Player extends Character { private previousDirection: string = PlayerAnimationDirections.Down; private wasMoving: boolean = false; + private emoteMenu: RadialMenu|null = null; + private updateListener: () => void; constructor( Scene: GameScene, @@ -26,14 +26,18 @@ export class Player extends Character implements CurrentGamerInterface { companion: string|null, companionTexturePromise?: Promise ) { - super(Scene, x, y, texturesPromise, name, direction, moving, 1); + super(Scene, x, y, texturesPromise, name, direction, moving, 1, true, companion, companionTexturePromise); //the current player model should be push away by other players to prevent conflict this.getBody().setImmovable(false); - if (typeof companion === 'string') { - this.addCompanion(companion, companionTexturePromise); - } + this.updateListener = () => { + if (this.emoteMenu) { + this.emoteMenu.x = this.x; + this.emoteMenu.y = this.y; + } + }; + this.scene.events.addListener('postupdate', this.updateListener); } moveUser(delta: number): void { @@ -83,9 +87,39 @@ export class Player extends Character implements CurrentGamerInterface { this.previousDirection = direction; } this.wasMoving = moving; + userMovingStore.set(moving); } public isMoving(): boolean { return this.wasMoving; } + + openOrCloseEmoteMenu(emotes:RadialMenuItem[]) { + if(this.emoteMenu) { + this.closeEmoteMenu(); + } else { + this.openEmoteMenu(emotes); + } + } + + openEmoteMenu(emotes:RadialMenuItem[]): void { + this.cancelPreviousEmote(); + this.emoteMenu = new RadialMenu(this.scene, this.x, this.y, emotes) + this.emoteMenu.on(RadialMenuClickEvent, (item: RadialMenuItem) => { + this.closeEmoteMenu(); + this.emit(requestEmoteEventName, item.name); + this.playEmote(item.name); + }); + } + + closeEmoteMenu(): void { + if (!this.emoteMenu) return; + this.emoteMenu.destroy(); + this.emoteMenu = null; + } + + destroy() { + this.scene.events.removeListener('postupdate', this.updateListener); + super.destroy(); + } } diff --git a/front/src/Phaser/Services/HdpiManager.ts b/front/src/Phaser/Services/HdpiManager.ts index 867c7a53..33f7e3a8 100644 --- a/front/src/Phaser/Services/HdpiManager.ts +++ b/front/src/Phaser/Services/HdpiManager.ts @@ -1,4 +1,3 @@ -import ScaleManager = Phaser.Scale.ScaleManager; interface Size { width: number; @@ -13,8 +12,7 @@ export class HdpiManager { * @param minRecommendedGamePixelsNumber The minimum number of pixels we want to display "by default" to the user * @param absoluteMinPixelNumber The very minimum of game pixels to display. Below, we forbid zooming more */ - public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) { - } + public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) {} /** * Returns the optimal size in "game pixels" based on the screen size in "real pixels". @@ -36,16 +34,12 @@ export class HdpiManager { }; } - let i = 1; - - while (realPixelNumber > this.minRecommendedGamePixelsNumber * i * i) { - i++; - } + const optimalZoomLevel = this.getOptimalZoomLevel(realPixelNumber); // Has the canvas more pixels than the screen? This is forbidden - if ((i - 1) * this._zoomModifier < 1) { + if (optimalZoomLevel * this._zoomModifier < 1) { // Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter) - this._zoomModifier = 1 / (i - 1); + this._zoomModifier = 1 / optimalZoomLevel; return { game: { @@ -59,8 +53,8 @@ export class HdpiManager { } } - const gameWidth = Math.ceil(realPixelScreenSize.width / (i - 1) / this._zoomModifier); - const gameHeight = Math.ceil(realPixelScreenSize.height / (i - 1) / this._zoomModifier); + const gameWidth = Math.ceil(realPixelScreenSize.width / optimalZoomLevel / this._zoomModifier); + const gameHeight = Math.ceil(realPixelScreenSize.height / optimalZoomLevel / this._zoomModifier); // Let's ensure we display a minimum of pixels, even if crazily zoomed in. if (gameWidth * gameHeight < this.absoluteMinPixelNumber) { @@ -68,7 +62,7 @@ export class HdpiManager { const minGameWidth = Math.sqrt(this.absoluteMinPixelNumber * realPixelScreenSize.width / realPixelScreenSize.height); // Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter) - this._zoomModifier = realPixelScreenSize.width / minGameWidth / (i - 1); + this._zoomModifier = realPixelScreenSize.width / minGameWidth / optimalZoomLevel; return { game: { @@ -89,12 +83,24 @@ export class HdpiManager { height: gameHeight, }, real: { - width: Math.ceil(realPixelScreenSize.width / (i - 1)) * (i - 1), - height: Math.ceil(realPixelScreenSize.height / (i - 1)) * (i - 1), + width: Math.ceil(realPixelScreenSize.width / optimalZoomLevel) * optimalZoomLevel, + height: Math.ceil(realPixelScreenSize.height / optimalZoomLevel) * optimalZoomLevel, } } } + /** + * We only accept integer but we make an exception for 1.5 + */ + private getOptimalZoomLevel(realPixelNumber: number): number { + const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber); + if (1.5 <= result && result < 2) { + return 1.5 + } else { + return Math.floor(result); + } + } + public get zoomModifier(): number { return this._zoomModifier; } diff --git a/front/src/Phaser/Services/WaScaleManager.ts b/front/src/Phaser/Services/WaScaleManager.ts index dfac135e..abfd2a8b 100644 --- a/front/src/Phaser/Services/WaScaleManager.ts +++ b/front/src/Phaser/Services/WaScaleManager.ts @@ -1,18 +1,25 @@ import {HdpiManager} from "./HdpiManager"; import ScaleManager = Phaser.Scale.ScaleManager; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; +import type {Game} from "../Game/Game"; +import {ResizableScene} from "../Login/ResizableScene"; +import {HtmlUtils} from "../../WebRtc/HtmlUtils"; class WaScaleManager { private hdpiManager: HdpiManager; private scaleManager!: ScaleManager; + private game!: Game; + private actualZoom: number = 1; + private _saveZoom: number = 1; public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) { this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber); } - public setScaleManager(scaleManager: ScaleManager) { - this.scaleManager = scaleManager; + public setGame(game: Game): void { + this.scaleManager = game.scale; + this.game = game; } public applyNewSize() { @@ -25,13 +32,29 @@ class WaScaleManager { const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({width: width * devicePixelRatio, height: height * devicePixelRatio}); - this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio); + this.actualZoom = realSize.width / gameSize.width / devicePixelRatio; + this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio) this.scaleManager.resize(gameSize.width, gameSize.height); // Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves const style = this.scaleManager.canvas.style; style.width = Math.ceil(realSize.width / devicePixelRatio) + 'px'; style.height = Math.ceil(realSize.height / devicePixelRatio) + 'px'; + + // Resize the game element at the same size at the canvas + const gameStyle = HtmlUtils.getElementByIdOrFail('game').style; + gameStyle.width = style.width; + gameStyle.height = style.height; + + // Note: onResize will be called twice (once here and once in Game.ts), but we have no better way. + for (const scene of this.game.scene.getScenes(true)) { + if (scene instanceof ResizableScene) { + // We are delaying the call to the "render" event because otherwise, the "camera" coordinates are not correctly updated. + scene.events.once(Phaser.Scenes.Events.RENDER, () => scene.onResize()); + } + } + + this.game.markDirty(); } public get zoomModifier(): number { @@ -42,6 +65,23 @@ class WaScaleManager { this.hdpiManager.zoomModifier = zoomModifier; this.applyNewSize(); } + + public saveZoom(): void { + this._saveZoom = this.hdpiManager.zoomModifier; + } + + public restoreZoom(): void{ + this.hdpiManager.zoomModifier = this._saveZoom; + this.applyNewSize(); + } + + /** + * This is used to scale back the ui components to counter-act the zoom. + */ + public get uiScalingFactor(): number { + return this.actualZoom > 1 ? 1 : 1.2; + } + } export const waScaleManager = new WaScaleManager(640*480, 196*196); diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index fe78dac8..70bb9b1b 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,5 +1,5 @@ -import { Direction } from "../../types"; -import {GameScene} from "../Game/GameScene"; +import type { Direction } from "../../types"; +import type {GameScene} from "../Game/GameScene"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {MobileJoystick} from "../Components/MobileJoystick"; @@ -173,7 +173,7 @@ export class UserInputManager { } destroy(): void { - this.joystick.destroy(); + this.joystick?.destroy(); } private initMouseWheel() { diff --git a/front/src/Stores/CustomCharacterStore.ts b/front/src/Stores/CustomCharacterStore.ts new file mode 100644 index 00000000..4bef7768 --- /dev/null +++ b/front/src/Stores/CustomCharacterStore.ts @@ -0,0 +1,3 @@ +import { derived, writable, Writable } from "svelte/store"; + +export const customCharacterSceneVisibleStore = writable(false); \ No newline at end of file diff --git a/front/src/Stores/ErrorStore.ts b/front/src/Stores/ErrorStore.ts new file mode 100644 index 00000000..2f1e3e40 --- /dev/null +++ b/front/src/Stores/ErrorStore.ts @@ -0,0 +1,33 @@ +import {writable} from "svelte/store"; + +/** + * A store that contains a list of error messages to be displayed. + */ +function createErrorStore() { + const { subscribe, set, update } = writable([]); + + return { + subscribe, + addErrorMessage: (e: string|Error): void => { + update((messages: string[]) => { + let message: string; + if (e instanceof Error) { + message = e.message; + } else { + message = e; + } + + if (!messages.includes(message)) { + messages.push(message); + } + + return messages; + }); + }, + clearMessages: (): void => { + set([]); + } + }; +} + +export const errorStore = createErrorStore(); diff --git a/front/src/Stores/Errors/BrowserTooOldError.ts b/front/src/Stores/Errors/BrowserTooOldError.ts new file mode 100644 index 00000000..bf934443 --- /dev/null +++ b/front/src/Stores/Errors/BrowserTooOldError.ts @@ -0,0 +1,8 @@ +export class BrowserTooOldError extends Error { + static NAME = 'BrowserTooOldError'; + + constructor() { + super('Unable to access your camera or microphone. Your browser is too old. Please consider upgrading your browser or try using a recent version of Chrome.'); + this.name = BrowserTooOldError.NAME; + } +} diff --git a/front/src/Stores/Errors/WebviewOnOldIOS.ts b/front/src/Stores/Errors/WebviewOnOldIOS.ts new file mode 100644 index 00000000..06c03f0e --- /dev/null +++ b/front/src/Stores/Errors/WebviewOnOldIOS.ts @@ -0,0 +1,8 @@ +export class WebviewOnOldIOS extends Error { + static NAME = 'WebviewOnOldIOS'; + + constructor() { + super('Your iOS version cannot use video/audio in the browser unless you are using Safari. Please switch to Safari or upgrade iOS to 14.3 or above.'); + this.name = WebviewOnOldIOS.NAME; + } +} diff --git a/front/src/Stores/GameStore.ts b/front/src/Stores/GameStore.ts new file mode 100644 index 00000000..8899aa12 --- /dev/null +++ b/front/src/Stores/GameStore.ts @@ -0,0 +1,5 @@ +import { writable } from "svelte/store"; + +export const userMovingStore = writable(false); + +export const requestVisitCardsStore = writable(null); diff --git a/front/src/Stores/HelpCameraSettingsStore.ts b/front/src/Stores/HelpCameraSettingsStore.ts new file mode 100644 index 00000000..88373dab --- /dev/null +++ b/front/src/Stores/HelpCameraSettingsStore.ts @@ -0,0 +1,3 @@ +import { writable } from "svelte/store"; + +export const helpCameraSettingsVisibleStore = writable(false); diff --git a/front/src/Stores/LoginSceneStore.ts b/front/src/Stores/LoginSceneStore.ts new file mode 100644 index 00000000..6e2ea18b --- /dev/null +++ b/front/src/Stores/LoginSceneStore.ts @@ -0,0 +1,3 @@ +import { writable } from "svelte/store"; + +export const loginSceneVisibleStore = writable(false); diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts new file mode 100644 index 00000000..d622511e --- /dev/null +++ b/front/src/Stores/MediaStore.ts @@ -0,0 +1,614 @@ +import {derived, get, Readable, readable, writable, Writable} from "svelte/store"; +import {peerStore} from "./PeerStore"; +import {localUserStore} from "../Connexion/LocalUserStore"; +import {ITiledMapGroupLayer, ITiledMapObjectLayer, ITiledMapTileLayer} from "../Phaser/Map/ITiledMap"; +import {userMovingStore} from "./GameStore"; +import {HtmlUtils} from "../WebRtc/HtmlUtils"; +import {BrowserTooOldError} from "./Errors/BrowserTooOldError"; +import {errorStore} from "./ErrorStore"; +import {isIOS} from "../WebRtc/DeviceUtils"; +import {WebviewOnOldIOS} from "./Errors/WebviewOnOldIOS"; + +/** + * A store that contains the camera state requested by the user (on or off). + */ +function createRequestedCameraState() { + const { subscribe, set, update } = writable(true); + + return { + subscribe, + enableWebcam: () => set(true), + disableWebcam: () => set(false), + }; +} + +/** + * A store that contains the microphone state requested by the user (on or off). + */ +function createRequestedMicrophoneState() { + const { subscribe, set, update } = writable(true); + + return { + subscribe, + enableMicrophone: () => set(true), + disableMicrophone: () => set(false), + }; +} + +/** + * A store containing whether the current page is visible or not. + */ +export const visibilityStore = readable(document.visibilityState === 'visible', function start(set) { + const onVisibilityChange = () => { + set(document.visibilityState === 'visible'); + }; + + document.addEventListener('visibilitychange', onVisibilityChange); + + return function stop() { + document.removeEventListener('visibilitychange', onVisibilityChange); + }; +}); + +/** + * A store that contains whether the game overlay is shown or not. + * Typically, the overlay is hidden when entering Jitsi meet. + */ +function createGameOverlayVisibilityStore() { + const { subscribe, set, update } = writable(false); + + return { + subscribe, + showGameOverlay: () => set(true), + hideGameOverlay: () => set(false), + }; +} + +/** + * A store that contains whether the EnableCameraScene is shown or not. + */ +function createEnableCameraSceneVisibilityStore() { + const { subscribe, set, update } = writable(false); + + return { + subscribe, + showEnableCameraScene: () => set(true), + hideEnableCameraScene: () => set(false), + }; +} + +export const requestedCameraState = createRequestedCameraState(); +export const requestedMicrophoneState = createRequestedMicrophoneState(); +export const gameOverlayVisibilityStore = createGameOverlayVisibilityStore(); +export const enableCameraSceneVisibilityStore = createEnableCameraSceneVisibilityStore(); + +/** + * A store that contains "true" if the webcam should be stopped for privacy reasons - i.e. if the the user left the the page while not in a discussion. + */ +function createPrivacyShutdownStore() { + let privacyEnabled = false; + + const { subscribe, set, update } = writable(privacyEnabled); + + visibilityStore.subscribe((isVisible) => { + if (!isVisible && get(peerStore).size === 0) { + privacyEnabled = true; + set(true); + } + if (isVisible) { + privacyEnabled = false; + set(false); + } + }); + + peerStore.subscribe((peers) => { + if (peers.size === 0 && get(visibilityStore) === false) { + privacyEnabled = true; + set(true); + } + }); + + + return { + subscribe, + }; +} + +export const privacyShutdownStore = createPrivacyShutdownStore(); + + +/** + * A store containing whether the webcam was enabled in the last 10 seconds + */ +const enabledWebCam10secondsAgoStore = readable(false, function start(set) { + let timeout: NodeJS.Timeout|null = null; + + const unsubscribe = requestedCameraState.subscribe((enabled) => { + if (enabled === true) { + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(() => { + set(false); + }, 10000); + set(true); + } else { + set(false); + } + }) + + return function stop() { + unsubscribe(); + }; +}); + +/** + * A store containing whether the webcam was enabled in the last 5 seconds + */ +const userMoved5SecondsAgoStore = readable(false, function start(set) { + let timeout: NodeJS.Timeout|null = null; + + const unsubscribe = userMovingStore.subscribe((moving) => { + if (moving === true) { + if (timeout) { + clearTimeout(timeout); + } + set(true); + } else { + timeout = setTimeout(() => { + set(false); + }, 5000); + + } + }) + + return function stop() { + unsubscribe(); + }; +}); + + +/** + * A store containing whether the mouse is getting close the bottom right corner. + */ +const mouseInBottomRight = readable(false, function start(set) { + let lastInBottomRight = false; + const gameDiv = HtmlUtils.getElementByIdOrFail('game'); + + const detectInBottomRight = (event: MouseEvent) => { + const rect = gameDiv.getBoundingClientRect(); + const inBottomRight = event.x - rect.left > rect.width * 3 / 4 && event.y - rect.top > rect.height * 3 / 4; + if (inBottomRight !== lastInBottomRight) { + lastInBottomRight = inBottomRight; + set(inBottomRight); + } + }; + + document.addEventListener('mousemove', detectInBottomRight); + + return function stop() { + document.removeEventListener('mousemove', detectInBottomRight); + } +}); + +/** + * A store that contains "true" if the webcam should be stopped for energy efficiency reason - i.e. we are not moving and not in a conversation. + */ +export const cameraEnergySavingStore = derived([userMoved5SecondsAgoStore, peerStore, enabledWebCam10secondsAgoStore, mouseInBottomRight], ([$userMoved5SecondsAgoStore,$peerStore, $enabledWebCam10secondsAgoStore, $mouseInBottomRight]) => { + return !$mouseInBottomRight && !$userMoved5SecondsAgoStore && $peerStore.size === 0 && !$enabledWebCam10secondsAgoStore; +}); + +/** + * A store that contains video constraints. + */ +function createVideoConstraintStore() { + const { subscribe, set, update } = writable({ + width: { min: 640, ideal: 1280, max: 1920 }, + height: { min: 400, ideal: 720 }, + frameRate: { ideal: localUserStore.getVideoQualityValue() }, + facingMode: "user", + resizeMode: 'crop-and-scale', + aspectRatio: 1.777777778 + } as MediaTrackConstraints); + + return { + subscribe, + setDeviceId: (deviceId: string|undefined) => update((constraints) => { + if (deviceId !== undefined) { + constraints.deviceId = { + exact: deviceId + }; + } else { + delete constraints.deviceId; + } + + return constraints; + }), + setFrameRate: (frameRate: number) => update((constraints) => { + constraints.frameRate = { ideal: frameRate }; + + return constraints; + }) + }; +} + +export const videoConstraintStore = createVideoConstraintStore(); + +/** + * A store that contains video constraints. + */ +function createAudioConstraintStore() { + const { subscribe, set, update } = writable({ + //TODO: make these values configurable in the game settings menu and store them in localstorage + autoGainControl: false, + echoCancellation: true, + noiseSuppression: true + } as boolean|MediaTrackConstraints); + + let selectedDeviceId = null; + + return { + subscribe, + setDeviceId: (deviceId: string|undefined) => update((constraints) => { + selectedDeviceId = deviceId; + + if (typeof(constraints) === 'boolean') { + constraints = {} + } + if (deviceId !== undefined) { + constraints.deviceId = { + exact: selectedDeviceId + }; + } else { + delete constraints.deviceId; + } + + return constraints; + }) + }; +} + +export const audioConstraintStore = createAudioConstraintStore(); + + +let timeout: NodeJS.Timeout; + +let previousComputedVideoConstraint: boolean|MediaTrackConstraints = false; +let previousComputedAudioConstraint: boolean|MediaTrackConstraints = false; + +/** + * A store containing the media constraints we want to apply. + */ +export const mediaStreamConstraintsStore = derived( + [ + requestedCameraState, + requestedMicrophoneState, + gameOverlayVisibilityStore, + enableCameraSceneVisibilityStore, + videoConstraintStore, + audioConstraintStore, + privacyShutdownStore, + cameraEnergySavingStore, + ], ( + [ + $requestedCameraState, + $requestedMicrophoneState, + $gameOverlayVisibilityStore, + $enableCameraSceneVisibilityStore, + $videoConstraintStore, + $audioConstraintStore, + $privacyShutdownStore, + $cameraEnergySavingStore, + ], set + ) => { + + let currentVideoConstraint: boolean|MediaTrackConstraints = $videoConstraintStore; + let currentAudioConstraint: boolean|MediaTrackConstraints = $audioConstraintStore; + + if ($enableCameraSceneVisibilityStore) { + set({ + video: currentVideoConstraint, + audio: currentAudioConstraint, + }); + return; + } + + // Disable webcam if the user requested so + if ($requestedCameraState === false) { + currentVideoConstraint = false; + } + + // Disable microphone if the user requested so + if ($requestedMicrophoneState === false) { + currentAudioConstraint = false; + } + + // Disable webcam and microphone when in a Jitsi + if ($gameOverlayVisibilityStore === false) { + currentVideoConstraint = false; + currentAudioConstraint = false; + } + + // Disable webcam for privacy reasons (the game is not visible and we were talking to noone) + if ($privacyShutdownStore === true) { + currentVideoConstraint = false; + } + + // Disable webcam for energy reasons (the user is not moving and we are talking to noone) + if ($cameraEnergySavingStore === true) { + currentVideoConstraint = false; + currentAudioConstraint = false; + } + + // Let's make the changes only if the new value is different from the old one. + if (previousComputedVideoConstraint != currentVideoConstraint || previousComputedAudioConstraint != currentAudioConstraint) { + previousComputedVideoConstraint = currentVideoConstraint; + previousComputedAudioConstraint = currentAudioConstraint; + // Let's copy the objects. + if (typeof previousComputedVideoConstraint !== 'boolean') { + previousComputedVideoConstraint = {...previousComputedVideoConstraint}; + } + if (typeof previousComputedAudioConstraint !== 'boolean') { + previousComputedAudioConstraint = {...previousComputedAudioConstraint}; + } + + if (timeout) { + clearTimeout(timeout); + } + + // Let's wait a little bit to avoid sending too many constraint changes. + timeout = setTimeout(() => { + set({ + video: currentVideoConstraint, + audio: currentAudioConstraint, + }); + }, 100); + } +}, { + video: false, + audio: false +} as MediaStreamConstraints); + +export type LocalStreamStoreValue = StreamSuccessValue | StreamErrorValue; + +interface StreamSuccessValue { + type: "success", + stream: MediaStream|null, + // The constraints that we got (and not the one that have been requested) + constraints: MediaStreamConstraints +} + +interface StreamErrorValue { + type: "error", + error: Error, + constraints: MediaStreamConstraints +} + +let currentStream : MediaStream|null = null; + +/** + * Stops the camera from filming + */ +function stopCamera(): void { + if (currentStream) { + for (const track of currentStream.getVideoTracks()) { + track.stop(); + } + } +} + +/** + * Stops the microphone from listening + */ +function stopMicrophone(): void { + if (currentStream) { + for (const track of currentStream.getAudioTracks()) { + track.stop(); + } + } +} + +/** + * A store containing the MediaStream object (or null if nothing requested, or Error if an error occurred) + */ +export const localStreamStore = derived, LocalStreamStoreValue>(mediaStreamConstraintsStore, ($mediaStreamConstraintsStore, set) => { + const constraints = { ...$mediaStreamConstraintsStore }; + + if (navigator.mediaDevices === undefined) { + if (window.location.protocol === 'http:') { + //throw new Error('Unable to access your camera or microphone. You need to use a HTTPS connection.'); + set({ + type: 'error', + error: new Error('Unable to access your camera or microphone. You need to use a HTTPS connection.'), + constraints + }); + return; + } else if (isIOS()) { + set({ + type: 'error', + error: new WebviewOnOldIOS(), + constraints + }); + return; + } else { + set({ + type: 'error', + error: new BrowserTooOldError(), + constraints + }); + return; + } + } + + if (constraints.audio === false) { + stopMicrophone(); + } + if (constraints.video === false) { + stopCamera(); + } + + if (constraints.audio === false && constraints.video === false) { + currentStream = null; + set({ + type: 'success', + stream: null, + constraints + }); + return; + } + + (async () => { + try { + stopMicrophone(); + stopCamera(); + currentStream = await navigator.mediaDevices.getUserMedia(constraints); + set({ + type: 'success', + stream: currentStream, + constraints + }); + return; + } catch (e) { + if (constraints.video !== false) { + console.info("Error. Unable to get microphone and/or camera access. Trying audio only.", $mediaStreamConstraintsStore, e); + // TODO: does it make sense to pop this error when retrying? + set({ + type: 'error', + error: e, + constraints + }); + // Let's try without video constraints + requestedCameraState.disableWebcam(); + } else { + console.info("Error. Unable to get microphone and/or camera access.", $mediaStreamConstraintsStore, e); + set({ + type: 'error', + error: e, + constraints + }); + } + + /*constraints.video = false; + if (constraints.audio === false) { + console.info("Error. Unable to get microphone and/or camera access.", $mediaStreamConstraintsStore, e); + set({ + type: 'error', + error: e, + constraints + }); + // Let's make as if the user did not ask. + requestedCameraState.disableWebcam(); + } else { + console.info("Error. Unable to get microphone and/or camera access. Trying audio only.", $mediaStreamConstraintsStore, e); + try { + currentStream = await navigator.mediaDevices.getUserMedia(constraints); + set({ + type: 'success', + stream: currentStream, + constraints + }); + return; + } catch (e2) { + console.info("Error. Unable to get microphone fallback access.", $mediaStreamConstraintsStore, e2); + set({ + type: 'error', + error: e, + constraints + }); + } + }*/ + } + })(); +}); + +/** + * A store containing the real active media constrained (not the one requested by the user, but the one we got from the system) + */ +export const obtainedMediaConstraintStore = derived(localStreamStore, ($localStreamStore) => { + return $localStreamStore.constraints; +}); + +/** + * Device list + */ +export const deviceListStore = readable([], function start(set) { + let deviceListCanBeQueried = false; + + const queryDeviceList = () => { + // Note: so far, we are ignoring any failures. + navigator.mediaDevices.enumerateDevices().then((mediaDeviceInfos) => { + set(mediaDeviceInfos); + }).catch((e) => { + console.error(e); + throw e; + }); + }; + + const unsubscribe = localStreamStore.subscribe((streamResult) => { + if (streamResult.type === "success" && streamResult.stream !== null) { + if (deviceListCanBeQueried === false) { + queryDeviceList(); + deviceListCanBeQueried = true; + } + } + }); + + if (navigator.mediaDevices) { + navigator.mediaDevices.addEventListener('devicechange', queryDeviceList); + } + + return function stop() { + unsubscribe(); + if (navigator.mediaDevices) { + navigator.mediaDevices.removeEventListener('devicechange', queryDeviceList); + } + }; +}); + +export const cameraListStore = derived(deviceListStore, ($deviceListStore) => { + return $deviceListStore.filter(device => device.kind === 'videoinput'); +}); + +export const microphoneListStore = derived(deviceListStore, ($deviceListStore) => { + return $deviceListStore.filter(device => device.kind === 'audioinput'); +}); + +// TODO: detect the new webcam and automatically switch on it. +cameraListStore.subscribe((devices) => { + // If the selected camera is unplugged, let's remove the constraint on deviceId + const constraints = get(videoConstraintStore); + if (!constraints.deviceId) { + return; + } + + // If we cannot find the device ID, let's remove it. + // @ts-ignore + if (!devices.find(device => device.deviceId === constraints.deviceId.exact)) { + videoConstraintStore.setDeviceId(undefined); + } +}); + +microphoneListStore.subscribe((devices) => { + // If the selected camera is unplugged, let's remove the constraint on deviceId + const constraints = get(audioConstraintStore); + if (typeof constraints === 'boolean') { + return; + } + if (!constraints.deviceId) { + return; + } + + // If we cannot find the device ID, let's remove it. + // @ts-ignore + if (!devices.find(device => device.deviceId === constraints.deviceId.exact)) { + audioConstraintStore.setDeviceId(undefined); + } +}); + +localStreamStore.subscribe(streamResult => { + if (streamResult.type === 'error') { + if (streamResult.error.name === BrowserTooOldError.NAME || streamResult.error.name === WebviewOnOldIOS.NAME) { + errorStore.addErrorMessage(streamResult.error); + } + } +}); diff --git a/front/src/Stores/MenuStore.ts b/front/src/Stores/MenuStore.ts new file mode 100644 index 00000000..c7c02130 --- /dev/null +++ b/front/src/Stores/MenuStore.ts @@ -0,0 +1,3 @@ +import { derived, writable, Writable } from "svelte/store"; + +export const menuIconVisible = writable(false); diff --git a/front/src/Stores/PeerStore.ts b/front/src/Stores/PeerStore.ts new file mode 100644 index 00000000..a582e692 --- /dev/null +++ b/front/src/Stores/PeerStore.ts @@ -0,0 +1,36 @@ +import { derived, writable, Writable } from "svelte/store"; +import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; +import type {SimplePeer} from "../WebRtc/SimplePeer"; + +/** + * A store that contains the camera state requested by the user (on or off). + */ +function createPeerStore() { + let users = new Map(); + + const { subscribe, set, update } = writable(users); + + return { + subscribe, + connectToSimplePeer: (simplePeer: SimplePeer) => { + users = new Map(); + set(users); + simplePeer.registerPeerConnectionListener({ + onConnect(user: UserSimplePeerInterface) { + update(users => { + users.set(user.userId, user); + return users; + }); + }, + onDisconnect(userId: number) { + update(users => { + users.delete(userId); + return users; + }); + } + }) + } + }; +} + +export const peerStore = createPeerStore(); diff --git a/front/src/Stores/ScreenSharingStore.ts b/front/src/Stores/ScreenSharingStore.ts new file mode 100644 index 00000000..ec5aa46f --- /dev/null +++ b/front/src/Stores/ScreenSharingStore.ts @@ -0,0 +1,193 @@ +import {derived, get, Readable, readable, writable, Writable} from "svelte/store"; +import {peerStore} from "./PeerStore"; +import {localUserStore} from "../Connexion/LocalUserStore"; +import {ITiledMapGroupLayer, ITiledMapObjectLayer, ITiledMapTileLayer} from "../Phaser/Map/ITiledMap"; +import {userMovingStore} from "./GameStore"; +import {HtmlUtils} from "../WebRtc/HtmlUtils"; +import { + audioConstraintStore, cameraEnergySavingStore, + enableCameraSceneVisibilityStore, + gameOverlayVisibilityStore, LocalStreamStoreValue, privacyShutdownStore, + requestedCameraState, + requestedMicrophoneState, videoConstraintStore +} from "./MediaStore"; + +declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any + +/** + * A store that contains the camera state requested by the user (on or off). + */ +function createRequestedScreenSharingState() { + const { subscribe, set, update } = writable(false); + + return { + subscribe, + enableScreenSharing: () => set(true), + disableScreenSharing: () => set(false), + }; +} + +export const requestedScreenSharingState = createRequestedScreenSharingState(); + +let currentStream : MediaStream|null = null; + +/** + * Stops the camera from filming + */ +function stopScreenSharing(): void { + if (currentStream) { + for (const track of currentStream.getVideoTracks()) { + track.stop(); + } + } + currentStream = null; +} + +let previousComputedVideoConstraint: boolean|MediaTrackConstraints = false; +let previousComputedAudioConstraint: boolean|MediaTrackConstraints = false; + +/** + * A store containing the media constraints we want to apply. + */ +export const screenSharingConstraintsStore = derived( + [ + requestedScreenSharingState, + gameOverlayVisibilityStore, + peerStore, + ], ( + [ + $requestedScreenSharingState, + $gameOverlayVisibilityStore, + $peerStore, + ], set + ) => { + + let currentVideoConstraint: boolean|MediaTrackConstraints = true; + let currentAudioConstraint: boolean|MediaTrackConstraints = false; + + // Disable screen sharing if the user requested so + if (!$requestedScreenSharingState) { + currentVideoConstraint = false; + currentAudioConstraint = false; + } + + // Disable screen sharing when in a Jitsi + if (!$gameOverlayVisibilityStore) { + currentVideoConstraint = false; + currentAudioConstraint = false; + } + + // Disable screen sharing if no peers + if ($peerStore.size === 0) { + currentVideoConstraint = false; + currentAudioConstraint = false; + } + + // Let's make the changes only if the new value is different from the old one. + if (previousComputedVideoConstraint != currentVideoConstraint || previousComputedAudioConstraint != currentAudioConstraint) { + previousComputedVideoConstraint = currentVideoConstraint; + previousComputedAudioConstraint = currentAudioConstraint; + // Let's copy the objects. + /*if (typeof previousComputedVideoConstraint !== 'boolean') { + previousComputedVideoConstraint = {...previousComputedVideoConstraint}; + } + if (typeof previousComputedAudioConstraint !== 'boolean') { + previousComputedAudioConstraint = {...previousComputedAudioConstraint}; + }*/ + + set({ + video: currentVideoConstraint, + audio: currentAudioConstraint, + }); + } + }, { + video: false, + audio: false + } as MediaStreamConstraints); + + +/** + * A store containing the MediaStream object for ScreenSharing (or null if nothing requested, or Error if an error occurred) + */ +export const screenSharingLocalStreamStore = derived, LocalStreamStoreValue>(screenSharingConstraintsStore, ($screenSharingConstraintsStore, set) => { + const constraints = $screenSharingConstraintsStore; + + if ($screenSharingConstraintsStore.video === false && $screenSharingConstraintsStore.audio === false) { + stopScreenSharing(); + requestedScreenSharingState.disableScreenSharing(); + set({ + type: 'success', + stream: null, + constraints + }); + return; + } + + let currentStreamPromise: Promise; + if (navigator.getDisplayMedia) { + currentStreamPromise = navigator.getDisplayMedia({constraints}); + } else if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) { + currentStreamPromise = navigator.mediaDevices.getDisplayMedia({constraints}); + } else { + stopScreenSharing(); + set({ + type: 'error', + error: new Error('Your browser does not support sharing screen'), + constraints + }); + return; + } + + (async () => { + try { + stopScreenSharing(); + currentStream = await currentStreamPromise; + + // If stream ends (for instance if user clicks the stop screen sharing button in the browser), let's close the view + for (const track of currentStream.getTracks()) { + track.onended = () => { + stopScreenSharing(); + requestedScreenSharingState.disableScreenSharing(); + previousComputedVideoConstraint = false; + previousComputedAudioConstraint = false; + set({ + type: 'success', + stream: null, + constraints: { + video: false, + audio: false + } + }); + }; + } + + set({ + type: 'success', + stream: currentStream, + constraints + }); + return; + } catch (e) { + currentStream = null; + requestedScreenSharingState.disableScreenSharing(); + console.info("Error. Unable to share screen.", e); + set({ + type: 'error', + error: e, + constraints + }); + } + })(); +}); + +/** + * A store containing whether the screen sharing button should be displayed or hidden. + */ +export const screenSharingAvailableStore = derived(peerStore, ($peerStore, set) => { + if (!navigator.getDisplayMedia && (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia)) { + set(false); + return; + } + + set($peerStore.size !== 0); +}); diff --git a/front/src/Stores/SelectCharacterStore.ts b/front/src/Stores/SelectCharacterStore.ts new file mode 100644 index 00000000..094eaef3 --- /dev/null +++ b/front/src/Stores/SelectCharacterStore.ts @@ -0,0 +1,3 @@ +import { derived, writable, Writable } from "svelte/store"; + +export const selectCharacterSceneVisibleStore = writable(false); \ No newline at end of file diff --git a/front/src/Stores/SelectCompanionStore.ts b/front/src/Stores/SelectCompanionStore.ts new file mode 100644 index 00000000..e66f5de3 --- /dev/null +++ b/front/src/Stores/SelectCompanionStore.ts @@ -0,0 +1,3 @@ +import { derived, writable, Writable } from "svelte/store"; + +export const selectCompanionSceneVisibleStore = writable(false); diff --git a/front/src/Stores/SoundPlayingStore.ts b/front/src/Stores/SoundPlayingStore.ts new file mode 100644 index 00000000..cf1d681c --- /dev/null +++ b/front/src/Stores/SoundPlayingStore.ts @@ -0,0 +1,22 @@ +import { writable } from "svelte/store"; + +/** + * A store that contains the URL of the sound currently playing + */ +function createSoundPlayingStore() { + const { subscribe, set, update } = writable(null); + + return { + subscribe, + playSound: (url: string) => { + set(url); + }, + soundEnded: () => { + set(null); + } + + + }; +} + +export const soundPlayingStore = createSoundPlayingStore(); diff --git a/front/src/Url/UrlManager.ts b/front/src/Url/UrlManager.ts index 9e10b4ee..b502467f 100644 --- a/front/src/Url/UrlManager.ts +++ b/front/src/Url/UrlManager.ts @@ -1,4 +1,4 @@ -import {Room} from "../Connexion/Room"; +import type {Room} from "../Connexion/Room"; export enum GameConnexionTypes { anonymous=1, diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 95885a53..1fb28487 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -1,6 +1,7 @@ import {HtmlUtils} from "./HtmlUtils"; import {Subject} from "rxjs"; import {iframeListener} from "../Api/IframeListener"; +import {touchScreenManager} from "../Touch/TouchScreenManager"; enum iframeStates { closed = 1, @@ -11,12 +12,17 @@ enum iframeStates { const cowebsiteDivId = 'cowebsite'; // the id of the whole container. const cowebsiteMainDomId = 'cowebsite-main'; // the id of the parent div of the iframe. const cowebsiteAsideDomId = 'cowebsite-aside'; // the id of the parent div of the iframe. -const cowebsiteCloseButtonId = 'cowebsite-close'; +export const cowebsiteCloseButtonId = 'cowebsite-close'; const cowebsiteFullScreenButtonId = 'cowebsite-fullscreen'; const cowebsiteOpenFullScreenImageId = 'cowebsite-fullscreen-open'; const cowebsiteCloseFullScreenImageId = 'cowebsite-fullscreen-close'; const animationTime = 500; //time used by the css transitions, in ms. +interface TouchMoveCoordinates { + x: number; + y: number; +} + class CoWebsiteManager { private opened: iframeStates = iframeStates.closed; @@ -32,7 +38,8 @@ class CoWebsiteManager { private resizing: boolean = false; private cowebsiteMainDom: HTMLDivElement; private cowebsiteAsideDom: HTMLDivElement; - + private previousTouchMoveCoordinates: TouchMoveCoordinates|null = null; //only use on touchscreens to track touch movement + get width(): number { return this.cowebsiteDiv.clientWidth; } @@ -62,32 +69,61 @@ class CoWebsiteManager { this.cowebsiteMainDom = HtmlUtils.getElementByIdOrFail(cowebsiteMainDomId); this.cowebsiteAsideDom = HtmlUtils.getElementByIdOrFail(cowebsiteAsideDomId); - this.initResizeListeners(); + if (touchScreenManager.supportTouchScreen) { + this.initResizeListeners(true); + } + this.initResizeListeners(false); - HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId).addEventListener('click', () => { + const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId); + buttonCloseFrame.addEventListener('click', () => { + buttonCloseFrame.blur(); this.closeCoWebsite(); }); - HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId).addEventListener('click', () => { + + const buttonFullScreenFrame = HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId); + buttonFullScreenFrame.addEventListener('click', () => { + buttonFullScreenFrame.blur(); this.fullscreen(); }); } - private initResizeListeners() { - const movecallback = (event:MouseEvent) => { - this.verticalMode ? this.height += event.movementY / this.getDevicePixelRatio() : this.width -= event.movementX / this.getDevicePixelRatio(); + private initResizeListeners(touchMode:boolean) { + const movecallback = (event:MouseEvent|TouchEvent) => { + let x, y; + if (event.type === 'mousemove') { + x = (event as MouseEvent).movementX / this.getDevicePixelRatio(); + y = (event as MouseEvent).movementY / this.getDevicePixelRatio(); + } else { + const touchEvent = (event as TouchEvent).touches[0]; + const last = {x: touchEvent.pageX, y: touchEvent.pageY}; + const previous = this.previousTouchMoveCoordinates as TouchMoveCoordinates; + this.previousTouchMoveCoordinates = last; + x = last.x - previous.x; + y = last.y - previous.y; + } + + + this.verticalMode ? this.height += y : this.width -= x; this.fire(); } - this.cowebsiteAsideDom.addEventListener('mousedown', (event) => { + this.cowebsiteAsideDom.addEventListener( touchMode ? 'touchstart' : 'mousedown', (event) => { this.resizing = true; this.getIframeDom().style.display = 'none'; + if (touchMode) { + const touchEvent = (event as TouchEvent).touches[0]; + this.previousTouchMoveCoordinates = {x: touchEvent.pageX, y: touchEvent.pageY}; + } - document.addEventListener('mousemove', movecallback); + document.addEventListener(touchMode ? 'touchmove' : 'mousemove', movecallback); }); - document.addEventListener('mouseup', (event) => { + document.addEventListener(touchMode ? 'touchend' : 'mouseup', (event) => { if (!this.resizing) return; - document.removeEventListener('mousemove', movecallback); + if (touchMode) { + this.previousTouchMoveCoordinates = null; + } + document.removeEventListener(touchMode ? 'touchmove' : 'mousemove', movecallback); this.getIframeDom().style.display = 'block'; this.resizing = false; }); @@ -137,14 +173,14 @@ class CoWebsiteManager { if (allowPolicy) { iframe.allow = allowPolicy; } - const onloadPromise = new Promise((resolve) => { + const onloadPromise = new Promise((resolve) => { iframe.onload = () => resolve(); }); if (allowApi) { iframeListener.registerIframe(iframe); } this.cowebsiteMainDom.appendChild(iframe); - const onTimeoutPromise = new Promise((resolve) => { + const onTimeoutPromise = new Promise((resolve) => { setTimeout(() => resolve(), 2000); }); this.currentOperationPromise = this.currentOperationPromise.then(() =>Promise.race([onloadPromise, onTimeoutPromise])).then(() => { @@ -152,7 +188,10 @@ class CoWebsiteManager { setTimeout(() => { this.fire(); }, animationTime) - }).catch(() => this.closeCoWebsite()); + }).catch((err) => { + console.error('Error loadCoWebsite => ', err); + this.closeCoWebsite() + }); } /** @@ -166,7 +205,10 @@ class CoWebsiteManager { setTimeout(() => { this.fire(); }, animationTime); - }).catch(() => this.closeCoWebsite()); + }).catch((err) => { + console.error('Error insertCoWebsite => ', err); + this.closeCoWebsite(); + }); } public closeCoWebsite(): Promise { diff --git a/front/src/WebRtc/DeviceUtils.ts b/front/src/WebRtc/DeviceUtils.ts new file mode 100644 index 00000000..91a3dc31 --- /dev/null +++ b/front/src/WebRtc/DeviceUtils.ts @@ -0,0 +1,12 @@ +export function isIOS(): boolean { + return [ + 'iPad Simulator', + 'iPhone Simulator', + 'iPod Simulator', + 'iPad', + 'iPhone', + 'iPod' + ].includes(navigator.platform) + // iPad on iOS 13 detection + || (navigator.userAgent.includes("Mac") && "ontouchend" in document) +} diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index 3e565c21..504ee91b 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -1,6 +1,6 @@ import {HtmlUtils} from "./HtmlUtils"; -import {mediaManager, ReportCallback, ShowReportCallBack} from "./MediaManager"; -import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; +import type {ShowReportCallBack} from "./MediaManager"; +import type {UserInputManager} from "../Phaser/UserInput/UserInputManager"; import {connectionManager} from "../Connexion/ConnectionManager"; import {GameConnexionTypes} from "../Url/UrlManager"; import {iframeListener} from "../Api/IframeListener"; @@ -171,6 +171,8 @@ export class DiscussionManager { const date = new Date(); if(isMe){ name = 'Me'; + } else { + name = HtmlUtils.escapeHtml(name); } pMessage.innerHTML = `${name} diff --git a/front/src/WebRtc/HtmlUtils.ts b/front/src/WebRtc/HtmlUtils.ts index 86f38216..942e553f 100644 --- a/front/src/WebRtc/HtmlUtils.ts +++ b/front/src/WebRtc/HtmlUtils.ts @@ -35,7 +35,12 @@ export class HtmlUtils { const urlRegex = /(https?:\/\/[^\s]+)/g; text = HtmlUtils.escapeHtml(text); return text.replace(urlRegex, (url: string) => { - return '' + url + ''; + const link = document.createElement('a'); + link.href = url; + link.target = "_blank"; + const text = document.createTextNode(url); + link.appendChild(text); + return link.outerHTML; }); } diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 983b08e2..d2b9ebdd 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -1,6 +1,8 @@ import {JITSI_URL} from "../Enum/EnvironmentVariable"; import {mediaManager} from "./MediaManager"; import {coWebsiteManager} from "./CoWebsiteManager"; +import {requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore"; +import {get} from "svelte/store"; declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any interface jitsiConfigInterface { @@ -11,8 +13,8 @@ interface jitsiConfigInterface { const getDefaultConfig = () : jitsiConfigInterface => { return { - startWithAudioMuted: !mediaManager.constraintsMedia.audio, - startWithVideoMuted: mediaManager.constraintsMedia.video === false, + startWithAudioMuted: !get(requestedMicrophoneState), + startWithVideoMuted: !get(requestedCameraState), prejoinPageEnabled: false } } @@ -71,7 +73,6 @@ class JitsiFactory { private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any private audioCallback = this.onAudioChange.bind(this); private videoCallback = this.onVideoChange.bind(this); - private previousConfigMeet? : jitsiConfigInterface; private jitsiScriptLoaded: boolean = false; /** @@ -82,9 +83,6 @@ class JitsiFactory { } public start(roomName: string, playerName:string, jwt?: string, config?: object, interfaceConfig?: object, jitsiUrl?: string): void { - //save previous config - this.previousConfigMeet = getDefaultConfig(); - coWebsiteManager.insertCoWebsite((async cowebsiteDiv => { // Jitsi meet external API maintains some data in local storage // which is sent via the appData URL parameter when joining a @@ -133,34 +131,21 @@ class JitsiFactory { this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback); this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback); this.jitsiApi?.dispose(); - - //restore previous config - if(this.previousConfigMeet?.startWithAudioMuted){ - mediaManager.disableMicrophone(); - }else{ - mediaManager.enableMicrophone(); - } - - if(this.previousConfigMeet?.startWithVideoMuted){ - mediaManager.disableCamera(); - }else{ - mediaManager.enableCamera(); - } } private onAudioChange({muted}: {muted: boolean}): void { - if (muted && mediaManager.constraintsMedia.audio === true) { - mediaManager.disableMicrophone(); - } else if(!muted && mediaManager.constraintsMedia.audio === false) { - mediaManager.enableMicrophone(); + if (muted) { + requestedMicrophoneState.disableMicrophone(); + } else { + requestedMicrophoneState.enableMicrophone(); } } private onVideoChange({muted}: {muted: boolean}): void { - if (muted && mediaManager.constraintsMedia.video !== false) { - mediaManager.disableCamera(); - } else if(!muted && mediaManager.constraintsMedia.video === false) { - mediaManager.enableCamera(); + if (muted) { + requestedCameraState.disableWebcam(); + } else { + requestedCameraState.enableWebcam(); } } diff --git a/front/src/WebRtc/LayoutManager.ts b/front/src/WebRtc/LayoutManager.ts index eed12333..3d92baac 100644 --- a/front/src/WebRtc/LayoutManager.ts +++ b/front/src/WebRtc/LayoutManager.ts @@ -1,4 +1,4 @@ -import { UserInputManager } from "../Phaser/UserInput/UserInputManager"; +import type { UserInputManager } from "../Phaser/UserInput/UserInputManager"; import {HtmlUtils} from "./HtmlUtils"; export enum LayoutMode { @@ -324,7 +324,7 @@ class LayoutManager { public addActionButton(id: string, text: string, callBack: Function, userInputManager: UserInputManager){ //delete previous element this.removeActionButton(id, userInputManager); - + //create div and text html component const p = document.createElement('p'); p.classList.add('action-body'); diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index d9a91940..efc9660a 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -1,27 +1,18 @@ import {DivImportance, layoutManager} from "./LayoutManager"; import {HtmlUtils} from "./HtmlUtils"; import {discussionManager, SendMessageCallback} from "./DiscussionManager"; -import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; +import type {UserInputManager} from "../Phaser/UserInput/UserInputManager"; import {localUserStore} from "../Connexion/LocalUserStore"; -import {UserSimplePeerInterface} from "./SimplePeer"; +import type {UserSimplePeerInterface} from "./SimplePeer"; import {SoundMeter} from "../Phaser/Components/SoundMeter"; - -declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any - -let videoConstraint: boolean|MediaTrackConstraints = { - width: { min: 640, ideal: 1280, max: 1920 }, - height: { min: 400, ideal: 720 }, - frameRate: { ideal: localUserStore.getVideoQualityValue() }, - facingMode: "user", - resizeMode: 'crop-and-scale', - aspectRatio: 1.777777778 -}; -const audioConstraint: boolean|MediaTrackConstraints = { - //TODO: make these values configurable in the game settings menu and store them in localstorage - autoGainControl: false, - echoCancellation: true, - noiseSuppression: false -}; +import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable"; +import { + gameOverlayVisibilityStore, localStreamStore, +} from "../Stores/MediaStore"; +import { + screenSharingLocalStreamStore +} from "../Stores/ScreenSharingStore"; +import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore"; export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void; export type StartScreenSharingCallback = (media: MediaStream) => void; @@ -30,494 +21,120 @@ export type ReportCallback = (message: string) => void; export type ShowReportCallBack = (userId: string, userName: string|undefined) => void; export type HelpCameraSettingsCallBack = () => void; -// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only) +import {cowebsiteCloseButtonId} from "./CoWebsiteManager"; + export class MediaManager { - localStream: MediaStream|null = null; - localScreenCapture: MediaStream|null = null; private remoteVideo: Map = new Map(); - myCamVideo: HTMLVideoElement; - cinemaClose: HTMLImageElement; - cinema: HTMLImageElement; - monitorClose: HTMLImageElement; - monitor: HTMLImageElement; - microphoneClose: HTMLImageElement; - microphone: HTMLImageElement; - webrtcInAudio: HTMLAudioElement; - mySoundMeterElement: HTMLDivElement; - private webrtcOutAudio: HTMLAudioElement; - constraintsMedia : MediaStreamConstraints = { - audio: audioConstraint, - video: videoConstraint - }; - updatedLocalStreamCallBacks : Set = new Set(); + //FIX ME SOUNDMETER: check stalability of sound meter calculation + //mySoundMeterElement: HTMLDivElement; startScreenSharingCallBacks : Set = new Set(); stopScreenSharingCallBacks : Set = new Set(); showReportModalCallBacks : Set = new Set(); - helpCameraSettingsCallBacks : Set = new Set(); - - private microphoneBtn: HTMLDivElement; - private cinemaBtn: HTMLDivElement; - private monitorBtn: HTMLDivElement; - private previousConstraint : MediaStreamConstraints; private focused : boolean = true; - private lastUpdateScene : Date = new Date(); - private setTimeOutlastUpdateScene? : NodeJS.Timeout; - - private hasCamera = true; - private triggerCloseJistiFrame : Map = new Map(); private userInputManager?: UserInputManager; - private mySoundMeter?: SoundMeter|null; + //FIX ME SOUNDMETER: check stalability of sound meter calculation + /*private mySoundMeter?: SoundMeter|null; private soundMeters: Map = new Map(); - private soundMeterElements: Map = new Map(); + private soundMeterElements: Map = new Map();*/ constructor() { - this.myCamVideo = HtmlUtils.getElementByIdOrFail('myCamVideo'); - this.webrtcInAudio = HtmlUtils.getElementByIdOrFail('audio-webrtc-in'); - this.webrtcOutAudio = HtmlUtils.getElementByIdOrFail('audio-webrtc-out'); - this.webrtcInAudio.volume = 0.2; - this.webrtcOutAudio.volume = 0.2; - - this.microphoneBtn = HtmlUtils.getElementByIdOrFail('btn-micro'); - this.microphoneClose = HtmlUtils.getElementByIdOrFail('microphone-close'); - this.microphoneClose.style.display = "none"; - this.microphoneClose.addEventListener('click', (e: MouseEvent) => { - e.preventDefault(); - this.enableMicrophone(); - //update tracking - }); - this.microphone = HtmlUtils.getElementByIdOrFail('microphone'); - this.microphone.addEventListener('click', (e: MouseEvent) => { - e.preventDefault(); - this.disableMicrophone(); - //update tracking - }); - - this.cinemaBtn = HtmlUtils.getElementByIdOrFail('btn-video'); - this.cinemaClose = HtmlUtils.getElementByIdOrFail('cinema-close'); - this.cinemaClose.style.display = "none"; - this.cinemaClose.addEventListener('click', (e: MouseEvent) => { - e.preventDefault(); - this.enableCamera(); - //update tracking - }); - this.cinema = HtmlUtils.getElementByIdOrFail('cinema'); - this.cinema.addEventListener('click', (e: MouseEvent) => { - e.preventDefault(); - this.disableCamera(); - //update tracking - }); - - this.monitorBtn = HtmlUtils.getElementByIdOrFail('btn-monitor'); - this.monitorClose = HtmlUtils.getElementByIdOrFail('monitor-close'); - this.monitorClose.style.display = "block"; - this.monitorClose.addEventListener('click', (e: MouseEvent) => { - e.preventDefault(); - this.enableScreenSharing(); - //update tracking - }); - this.monitor = HtmlUtils.getElementByIdOrFail('monitor'); - this.monitor.style.display = "none"; - this.monitor.addEventListener('click', (e: MouseEvent) => { - e.preventDefault(); - this.disableScreenSharing(); - //update tracking - }); - - this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia)); this.pingCameraStatus(); - this.checkActiveUser(); //todo: desactivated in case of bug - - this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter')); + //FIX ME SOUNDMETER: check stability of sound meter calculation + /*this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter')); this.mySoundMeterElement.childNodes.forEach((value: ChildNode, index) => { this.mySoundMeterElement.children.item(index)?.classList.remove('active'); + });*/ + + //Check of ask notification navigator permission + this.getNotification(); + + localStreamStore.subscribe((result) => { + if (result.type === 'error') { + console.error(result.error); + layoutManager.addInformation('warning', 'Camera access denied. Click here and check your browser permissions.', () => { + helpCameraSettingsVisibleStore.set(true); + }, this.userInputManager); + return; + } }); + + let isScreenSharing = false; + screenSharingLocalStreamStore.subscribe((result) => { + if (result.type === 'error') { + console.error(result.error); + layoutManager.addInformation('warning', 'Screen sharing denied. Click here and check your browser permissions.', () => { + helpCameraSettingsVisibleStore.set(true); + }, this.userInputManager); + return; + } + + if (result.stream !== null) { + isScreenSharing = true; + this.addScreenSharingActiveVideo('me', DivImportance.Normal); + HtmlUtils.getElementByIdOrFail('screen-sharing-me').srcObject = result.stream; + } else { + if (isScreenSharing) { + isScreenSharing = false; + this.removeActiveScreenSharingVideo('me'); + } + } + + }); + + /*screenSharingAvailableStore.subscribe((available) => { + if (available) { + document.querySelector('.btn-monitor')?.classList.remove('hide'); + } else { + document.querySelector('.btn-monitor')?.classList.add('hide'); + } + });*/ } public updateScene(){ - this.lastUpdateScene = new Date(); - this.updateSoudMeter(); + //FIX ME SOUNDMETER: check stability of sound meter calculation + //this.updateSoudMeter(); } - public blurCamera() { - if(!this.focused){ - return; - } - this.focused = false; - this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia)); - this.disableCamera(); - } - - public focusCamera() { - if(this.focused){ - return; - } - this.focused = true; - this.applyPreviousConfig(); - } - - public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { - this.updatedLocalStreamCallBacks.add(callback); - } - - public onStartScreenSharing(callback: StartScreenSharingCallback): void { - this.startScreenSharingCallBacks.add(callback); - } - - public onStopScreenSharing(callback: StopScreenSharingCallback): void { - this.stopScreenSharingCallBacks.add(callback); - } - - removeUpdateLocalStreamEventListener(callback: UpdatedLocalStreamCallback): void { - this.updatedLocalStreamCallBacks.delete(callback); - } - - private triggerUpdatedLocalStreamCallbacks(stream: MediaStream|null): void { - for (const callback of this.updatedLocalStreamCallBacks) { - callback(stream); - } - } - - private triggerStartedScreenSharingCallbacks(stream: MediaStream): void { - for (const callback of this.startScreenSharingCallBacks) { - callback(stream); - } - } - - private triggerStoppedScreenSharingCallbacks(stream: MediaStream): void { - for (const callback of this.stopScreenSharingCallBacks) { - callback(stream); - } - } - - public showGameOverlay(){ + public showGameOverlay(): void { const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay'); gameOverlay.classList.add('active'); - const buttonCloseFrame = HtmlUtils.getElementByIdOrFail('cowebsite-close'); + const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId); const functionTrigger = () => { this.triggerCloseJitsiFrameButton(); } - buttonCloseFrame.removeEventListener('click', functionTrigger); + buttonCloseFrame.removeEventListener('click', () => { + buttonCloseFrame.blur(); + functionTrigger(); + }); + + gameOverlayVisibilityStore.showGameOverlay(); } - public hideGameOverlay(){ + public hideGameOverlay(): void { const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay'); gameOverlay.classList.remove('active'); - const buttonCloseFrame = HtmlUtils.getElementByIdOrFail('cowebsite-close'); + const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId); const functionTrigger = () => { this.triggerCloseJitsiFrameButton(); } - buttonCloseFrame.addEventListener('click', functionTrigger); - } - - public updateCameraQuality(value: number) { - this.enableCameraStyle(); - const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint)); - newVideoConstraint.frameRate = {exact: value, ideal: value}; - videoConstraint = newVideoConstraint; - this.constraintsMedia.video = videoConstraint; - this.getCamera().then((stream: MediaStream) => { - this.triggerUpdatedLocalStreamCallbacks(stream); - }); - } - - public enableCamera() { - this.constraintsMedia.video = videoConstraint; - - this.getCamera().then((stream: MediaStream) => { - //TODO show error message tooltip upper of camera button - //TODO message : please check camera permission of your navigator - if(stream.getVideoTracks().length === 0) { - throw Error('Video track is empty, please check camera permission of your navigator') - } - this.enableCameraStyle(); - this.triggerUpdatedLocalStreamCallbacks(stream); - }).catch((err) => { - console.error(err); - this.disableCameraStyle(); - - layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => { - this.showHelpCameraSettingsCallBack(); - }, this.userInputManager); - }); - } - - public async disableCamera() { - this.disableCameraStyle(); - - if (this.constraintsMedia.audio !== false) { - const stream = await this.getCamera(); - this.triggerUpdatedLocalStreamCallbacks(stream); - } else { - this.triggerUpdatedLocalStreamCallbacks(null); - } - } - - public enableMicrophone() { - this.constraintsMedia.audio = audioConstraint; - - this.getCamera().then((stream) => { - //TODO show error message tooltip upper of camera button - //TODO message : please check microphone permission of your navigator - if(stream.getAudioTracks().length === 0) { - throw Error('Audio track is empty, please check microphone permission of your navigator') - } - this.enableMicrophoneStyle(); - this.triggerUpdatedLocalStreamCallbacks(stream); - }).catch((err) => { - console.error(err); - this.disableMicrophoneStyle(); - - layoutManager.addInformation('warning', 'Microphone access denied. Click here and check navigators permissions.', () => { - this.showHelpCameraSettingsCallBack(); - }, this.userInputManager); - }); - } - - public async disableMicrophone() { - this.disableMicrophoneStyle(); - this.stopMicrophone(); - - if (this.constraintsMedia.video !== false) { - const stream = await this.getCamera(); - this.triggerUpdatedLocalStreamCallbacks(stream); - } else { - this.triggerUpdatedLocalStreamCallbacks(null); - } - } - - private applyPreviousConfig() { - this.constraintsMedia = this.previousConstraint; - if(!this.constraintsMedia.video){ - this.disableCameraStyle(); - }else{ - this.enableCameraStyle(); - } - if(!this.constraintsMedia.audio){ - this.disableMicrophoneStyle() - }else{ - this.enableMicrophoneStyle() - } - - this.getCamera().then((stream: MediaStream) => { - this.triggerUpdatedLocalStreamCallbacks(stream); - }); - } - - private enableCameraStyle(){ - this.cinemaClose.style.display = "none"; - this.cinemaBtn.classList.remove("disabled"); - this.cinema.style.display = "block"; - } - - private disableCameraStyle(){ - this.cinemaClose.style.display = "block"; - this.cinema.style.display = "none"; - this.cinemaBtn.classList.add("disabled"); - this.constraintsMedia.video = false; - this.myCamVideo.srcObject = null; - this.stopCamera(); - } - - private enableMicrophoneStyle(){ - this.microphoneClose.style.display = "none"; - this.microphone.style.display = "block"; - this.microphoneBtn.classList.remove("disabled"); - } - - private disableMicrophoneStyle(){ - this.microphoneClose.style.display = "block"; - this.microphone.style.display = "none"; - this.microphoneBtn.classList.add("disabled"); - this.constraintsMedia.audio = false; - } - - private enableScreenSharing() { - this.getScreenMedia().then((stream) => { - this.triggerStartedScreenSharingCallbacks(stream); - this.monitorClose.style.display = "none"; - this.monitor.style.display = "block"; - this.monitorBtn.classList.add("enabled"); - }, () => { - this.monitorClose.style.display = "block"; - this.monitor.style.display = "none"; - this.monitorBtn.classList.remove("enabled"); - - layoutManager.addInformation('warning', 'Screen sharing access denied. Click here and check navigators permissions.', () => { - this.showHelpCameraSettingsCallBack(); - }, this.userInputManager); + buttonCloseFrame.addEventListener('click', () => { + buttonCloseFrame.blur(); + functionTrigger(); }); - } - - private disableScreenSharing() { - this.monitorClose.style.display = "block"; - this.monitor.style.display = "none"; - this.monitorBtn.classList.remove("enabled"); - this.removeActiveScreenSharingVideo('me'); - this.localScreenCapture?.getTracks().forEach((track: MediaStreamTrack) => { - track.stop(); - }); - if (this.localScreenCapture === null) { - console.warn('Weird: trying to remove a screen sharing that is not enabled'); - return; - } - const localScreenCapture = this.localScreenCapture; - this.getCamera().then((stream) => { - this.triggerStoppedScreenSharingCallbacks(localScreenCapture); - }).catch((err) => { //catch error get camera - console.error(err); - this.triggerStoppedScreenSharingCallbacks(localScreenCapture); - }); - this.localScreenCapture = null; - } - - //get screen - getScreenMedia() : Promise{ - try { - return this._startScreenCapture() - .then((stream: MediaStream) => { - this.localScreenCapture = stream; - - // If stream ends (for instance if user clicks the stop screen sharing button in the browser), let's close the view - for (const track of stream.getTracks()) { - track.onended = () => { - this.disableScreenSharing(); - }; - } - - this.addScreenSharingActiveVideo('me', DivImportance.Normal); - HtmlUtils.getElementByIdOrFail('screen-sharing-me').srcObject = stream; - - return stream; - }) - .catch((err: unknown) => { - console.error("Error => getScreenMedia => ", err); - throw err; - }); - }catch (err) { - return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars - reject(err); - }); - } - } - - private _startScreenCapture() { - if (navigator.getDisplayMedia) { - return navigator.getDisplayMedia({video: true}); - } else if (navigator.mediaDevices.getDisplayMedia) { - return navigator.mediaDevices.getDisplayMedia({video: true}); - } else { - return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars - reject("error sharing screen"); - }); - } - } - - //get camera - async getCamera(): Promise { - if (navigator.mediaDevices === undefined) { - if (window.location.protocol === 'http:') { - throw new Error('Unable to access your camera or microphone. You need to use a HTTPS connection.'); - } else { - throw new Error('Unable to access your camera or microphone. Your browser is too old.'); - } - } - - return this.getLocalStream().catch((err) => { - console.info('Error get camera, trying with video option at null =>', err); - this.disableCameraStyle(); - return this.getLocalStream().then((stream : MediaStream) => { - this.hasCamera = false; - return stream; - }).catch((err) => { - this.disableMicrophoneStyle(); - console.info("error get media ", this.constraintsMedia.video, this.constraintsMedia.audio, err); - throw err; - }); - }); - - //TODO resize remote cam - /*console.log(this.localStream.getTracks()); - let videoMediaStreamTrack = this.localStream.getTracks().find((media : MediaStreamTrack) => media.kind === "video"); - let {width, height} = videoMediaStreamTrack.getSettings(); - console.info(`${width}x${height}`); // 6*/ - } - - private getLocalStream() : Promise { - return navigator.mediaDevices.getUserMedia(this.constraintsMedia).then((stream : MediaStream) => { - this.localStream = stream; - this.myCamVideo.srcObject = this.localStream; - - //init sound meter - this.mySoundMeter = null; - if(this.constraintsMedia.audio){ - this.mySoundMeter = new SoundMeter(); - this.mySoundMeter.connectToSource(stream, new AudioContext()); - } - return stream; - }).catch((err: Error) => { - throw err; - }); - } - - /** - * Stops the camera from filming - */ - public stopCamera(): void { - if (this.localStream) { - for (const track of this.localStream.getVideoTracks()) { - track.stop(); - } - } - } - - /** - * Stops the microphone from listening - */ - public stopMicrophone(): void { - if (this.localStream) { - for (const track of this.localStream.getAudioTracks()) { - track.stop(); - } - } - this.mySoundMeter?.stop(); - } - - setCamera(id: string): Promise { - let video = this.constraintsMedia.video; - if (typeof(video) === 'boolean' || video === undefined) { - video = {} - } - video.deviceId = { - exact: id - }; - - return this.getCamera(); - } - - setMicrophone(id: string): Promise { - let audio = this.constraintsMedia.audio; - if (typeof(audio) === 'boolean' || audio === undefined) { - audio = {} - } - audio.deviceId = { - exact: id - }; - - return this.getCamera(); + gameOverlayVisibilityStore.hideGameOverlay(); } addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){ - this.webrtcInAudio.play(); const userId = ''+user.userId userName = userName.toUpperCase(); @@ -533,7 +150,7 @@ export class MediaManager { Report/Block - +
@@ -546,7 +163,7 @@ export class MediaManager { `; layoutManager.add(DivImportance.Normal, userId, html); - + this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail(userId)); //permit to create participant in discussion part @@ -564,13 +181,13 @@ export class MediaManager { showReportUser(); }); } - + addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){ userId = this.getScreenSharingId(userId); const html = `
- +
`; @@ -590,7 +207,7 @@ export class MediaManager { } element.classList.add('active') //todo: why does a method 'disable' add a class 'active'? } - + enabledMicrophoneByUserId(userId: number){ const element = document.getElementById(`microphone-${userId}`); if(!element){ @@ -598,7 +215,7 @@ export class MediaManager { } element.classList.remove('active') //todo: why does a method 'enable' remove a class 'active'? } - + disabledVideoByUserId(userId: number) { let element = document.getElementById(`${userId}`); if (element) { @@ -609,7 +226,7 @@ export class MediaManager { element.style.display = "block"; } } - + enabledVideoByUserId(userId: number){ let element = document.getElementById(`${userId}`); if(element){ @@ -632,11 +249,12 @@ export class MediaManager { } remoteVideo.srcObject = stream; + //FIX ME SOUNDMETER: check stalability of sound meter calculation //sound metter - const soundMeter = new SoundMeter(); + /*const soundMeter = new SoundMeter(); soundMeter.connectToSource(stream, new AudioContext()); this.soundMeters.set(userId, soundMeter); - this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail('soundMeter-'+userId)); + this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail('soundMeter-'+userId));*/ } addStreamRemoteScreenSharing(userId: string, stream : MediaStream){ // In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet @@ -647,14 +265,15 @@ export class MediaManager { this.addStreamRemoteVideo(this.getScreenSharingId(userId), stream); } - + removeActiveVideo(userId: string){ layoutManager.remove(userId); this.remoteVideo.delete(userId); - this.soundMeters.get(userId)?.stop(); + //FIX ME SOUNDMETER: check stalability of sound meter calculation + /*this.soundMeters.get(userId)?.stop(); this.soundMeters.delete(userId); - this.soundMeterElements.delete(userId); + this.soundMeterElements.delete(userId);*/ //permit to remove user in discussion part this.removeParticipant(userId); @@ -662,10 +281,6 @@ export class MediaManager { removeActiveScreenSharingVideo(userId: string) { this.removeActiveVideo(this.getScreenSharingId(userId)) } - - playWebrtcOutSound(): void { - this.webrtcOutAudio.play(); - } isConnecting(userId: string): void { const connectingSpinnerDiv = this.getSpinner(userId); @@ -708,7 +323,7 @@ export class MediaManager { const connnectingSpinnerDiv = element.getElementsByClassName('connecting-spinner').item(0) as HTMLDivElement|null; return connnectingSpinnerDiv; } - + private getColorByString(str: String) : String|null { let hash = 0; if (str.length === 0) return null; @@ -776,42 +391,17 @@ export class MediaManager { this.userInputManager = userInputManager; discussionManager.setUserInputManager(userInputManager); } - //check if user is active - private checkActiveUser(){ - if(this.setTimeOutlastUpdateScene){ - clearTimeout(this.setTimeOutlastUpdateScene); - } - this.setTimeOutlastUpdateScene = setTimeout(() => { - const now = new Date(); - //if last update is more of 10 sec - if( (now.getTime() - this.lastUpdateScene.getTime()) > 10000) { - this.blurCamera(); - }else{ - this.focusCamera(); - } - this.checkActiveUser(); - }, this.focused ? 10000 : 1000); - } public setShowReportModalCallBacks(callback: ShowReportCallBack){ this.showReportModalCallBacks.add(callback); } - public setHelpCameraSettingsCallBack(callback: HelpCameraSettingsCallBack){ - this.helpCameraSettingsCallBacks.add(callback); - } - - private showHelpCameraSettingsCallBack(){ - for(const callBack of this.helpCameraSettingsCallBacks){ - callBack(); - } - } - - updateSoudMeter(){ + //FIX ME SOUNDMETER: check stalability of sound meter calculation + /*updateSoudMeter(){ try{ const volume = parseInt(((this.mySoundMeter ? this.mySoundMeter.getVolume() : 0) / 10).toFixed(0)); this.setVolumeSoundMeter(volume, this.mySoundMeterElement); - + for(const indexUserId of this.soundMeters.keys()){ const soundMeter = this.soundMeters.get(indexUserId); const soundMeterElement = this.soundMeterElements.get(indexUserId); @@ -824,7 +414,7 @@ export class MediaManager { }catch(err){ //console.error(err); } - } + }*/ private setVolumeSoundMeter(volume: number, element: HTMLDivElement){ if(volume <= 0 && !element.classList.contains('active')){ @@ -847,6 +437,52 @@ export class MediaManager { elementChildre.classList.add('active'); }); } + + public getNotification(){ + //Get notification + if (!DISABLE_NOTIFICATIONS && window.Notification && Notification.permission !== "granted") { + if (this.checkNotificationPromise()) { + Notification.requestPermission().catch((err) => { + console.error(`Notification permission error`, err); + }); + } else { + Notification.requestPermission(); + } + } + } + + /** + * Return true if the browser supports the modern version of the Notification API (which is Promise based) or false + * if we are on Safari... + * + * See https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API + */ + private checkNotificationPromise(): boolean { + try { + Notification.requestPermission().then(); + } catch(e) { + return false; + } + + return true; + } + + public createNotification(userName: string){ + if(this.focused){ + return; + } + if (window.Notification && Notification.permission === "granted") { + const title = 'WorkAdventure'; + const options = { + body: `Hi! ${userName} wants to discuss with you, don't be afraid!`, + icon: '/resources/logos/logo-WA-min.png', + image: '/resources/logos/logo-WA-min.png', + badge: '/resources/logos/logo-WA-min.png', + }; + new Notification(title, options); + //new Notification(`Hi! ${userName} wants to discuss with you, don't be afraid!`); + } + } } export const mediaManager = new MediaManager(); diff --git a/front/src/WebRtc/ScreenSharingPeer.ts b/front/src/WebRtc/ScreenSharingPeer.ts index cb48e7be..d797f59b 100644 --- a/front/src/WebRtc/ScreenSharingPeer.ts +++ b/front/src/WebRtc/ScreenSharingPeer.ts @@ -1,9 +1,9 @@ -import * as SimplePeerNamespace from "simple-peer"; +import type * as SimplePeerNamespace from "simple-peer"; import {mediaManager} from "./MediaManager"; import {STUN_SERVER, TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable"; -import {RoomConnection} from "../Connexion/RoomConnection"; +import type {RoomConnection} from "../Connexion/RoomConnection"; import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer"; -import {UserSimplePeerInterface} from "./SimplePeer"; +import type {UserSimplePeerInterface} from "./SimplePeer"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); @@ -19,7 +19,7 @@ export class ScreenSharingPeer extends Peer { public _connected: boolean = false; private userId: number; - constructor(user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) { + constructor(user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection, stream: MediaStream | null) { super({ initiator: initiator ? initiator : false, //reconnectTimer: 10000, @@ -60,6 +60,7 @@ export class ScreenSharingPeer extends Peer { const message = JSON.parse(chunk.toString('utf8')); if (message.streamEnded !== true) { console.error('Unexpected message on screen sharing peer connection'); + return; } mediaManager.removeActiveScreenSharingVideo("" + this.userId); }); @@ -81,7 +82,9 @@ export class ScreenSharingPeer extends Peer { this._onFinish(); }); - this.pushScreenSharingToRemoteUser(); + if (stream) { + this.addStream(stream); + } } private sendWebrtcScreenSharingSignal(data: unknown) { @@ -141,16 +144,6 @@ export class ScreenSharingPeer extends Peer { } } - private pushScreenSharingToRemoteUser() { - const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture; - if(!localScreenCapture){ - return; - } - - this.addStream(localScreenCapture); - return; - } - public stopPushingScreenSharingToRemoteUser(stream: MediaStream) { this.removeStream(stream); this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, streamEnded: true}))); diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 7690c27d..2a502bab 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -1,4 +1,4 @@ -import { +import type { WebRtcDisconnectMessageInterface, WebRtcSignalReceivedMessageInterface, } from "../Connexion/ConnexionModels"; @@ -10,10 +10,15 @@ import { } from "./MediaManager"; import {ScreenSharingPeer} from "./ScreenSharingPeer"; import {MESSAGE_TYPE_BLOCKED, MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer"; -import {RoomConnection} from "../Connexion/RoomConnection"; +import type {RoomConnection} from "../Connexion/RoomConnection"; import {connectionManager} from "../Connexion/ConnectionManager"; import {GameConnexionTypes} from "../Url/UrlManager"; import {blackListManager} from "./BlackListManager"; +import {get} from "svelte/store"; +import {localStreamStore, LocalStreamStoreValue, obtainedMediaConstraintStore} from "../Stores/MediaStore"; +import {screenSharingLocalStreamStore} from "../Stores/ScreenSharingStore"; +import {DivImportance, layoutManager} from "./LayoutManager"; +import {HtmlUtils} from "./HtmlUtils"; export interface UserSimplePeerInterface{ userId: number; @@ -37,9 +42,9 @@ export class SimplePeer { private PeerScreenSharingConnectionArray: Map = new Map(); private PeerConnectionArray: Map = new Map(); - private readonly sendLocalVideoStreamCallback: UpdatedLocalStreamCallback; private readonly sendLocalScreenSharingStreamCallback: StartScreenSharingCallback; private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback; + private readonly unsubscribers: (() => void)[] = []; private readonly peerConnectionListeners: Array = new Array(); private readonly userId: number; private lastWebrtcUserName: string|undefined; @@ -47,13 +52,32 @@ export class SimplePeer { constructor(private Connection: RoomConnection, private enableReporting: boolean, private myName: string) { // We need to go through this weird bound function pointer in order to be able to "free" this reference later. - this.sendLocalVideoStreamCallback = this.sendLocalVideoStream.bind(this); this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this); this.stopLocalScreenSharingStreamCallback = this.stopLocalScreenSharingStream.bind(this); - mediaManager.onUpdateLocalStream(this.sendLocalVideoStreamCallback); - mediaManager.onStartScreenSharing(this.sendLocalScreenSharingStreamCallback); - mediaManager.onStopScreenSharing(this.stopLocalScreenSharingStreamCallback); + this.unsubscribers.push(localStreamStore.subscribe((streamResult) => { + this.sendLocalVideoStream(streamResult); + })); + + let localScreenCapture: MediaStream|null = null; + + this.unsubscribers.push(screenSharingLocalStreamStore.subscribe((streamResult) => { + if (streamResult.type === 'error') { + // Let's ignore screen sharing errors, we will deal with those in a different way. + return; + } + + if (streamResult.stream !== null) { + localScreenCapture = streamResult.stream; + this.sendLocalScreenSharingStream(localScreenCapture); + } else { + if (localScreenCapture) { + this.stopLocalScreenSharingStream(localScreenCapture); + localScreenCapture = null; + } + } + })); + this.userId = Connection.getUserId(); this.initialise(); } @@ -82,11 +106,10 @@ export class SimplePeer { }); mediaManager.showGameOverlay(); - mediaManager.getCamera().finally(() => { - //receive message start - this.Connection.receiveWebrtcStart((message: UserSimplePeerInterface) => { - this.receiveWebrtcStart(message); - }); + + //receive message start + this.Connection.receiveWebrtcStart((message: UserSimplePeerInterface) => { + this.receiveWebrtcStart(message); }); this.Connection.disconnectMessage((data: WebRtcDisconnectMessageInterface): void => { @@ -105,13 +128,19 @@ export class SimplePeer { if(!user.initiator){ return; } - this.createPeerConnection(user); + const streamResult = get(localStreamStore); + let stream : MediaStream | null = null; + if (streamResult.type === 'success' && streamResult.stream) { + stream = streamResult.stream; + } + + this.createPeerConnection(user, stream); } /** * create peer connection to bind users */ - private createPeerConnection(user : UserSimplePeerInterface) : VideoPeer | null { + private createPeerConnection(user : UserSimplePeerInterface, localStream: MediaStream | null) : VideoPeer | null { const peerConnection = this.PeerConnectionArray.get(user.userId) if (peerConnection) { if (peerConnection.destroyed) { @@ -121,11 +150,11 @@ export class SimplePeer { if (!peerConnexionDeleted) { throw 'Error to delete peer connection'; } - this.createPeerConnection(user); + //return this.createPeerConnection(user, localStream); } else { peerConnection.toClose = false; + return null; } - return null; } let name = user.name; @@ -143,7 +172,7 @@ export class SimplePeer { this.lastWebrtcUserName = user.webRtcUser; this.lastWebrtcPassword = user.webRtcPassword; - const peer = new VideoPeer(user, user.initiator ? user.initiator : false, this.Connection); + const peer = new VideoPeer(user, user.initiator ? user.initiator : false, this.Connection, localStream); //permit to send message mediaManager.addSendMessageCallback(user.userId,(message: string) => { @@ -154,10 +183,16 @@ export class SimplePeer { // When a connection is established to a video stream, and if a screen sharing is taking place, // the user sharing screen should also initiate a connection to the remote user! peer.on('connect', () => { - if (mediaManager.localScreenCapture) { - this.sendLocalScreenSharingStreamToUser(user.userId); + const streamResult = get(screenSharingLocalStreamStore); + if (streamResult.type === 'success' && streamResult.stream !== null) { + this.sendLocalScreenSharingStreamToUser(user.userId, streamResult.stream); } }); + + //Create a notification for first user in circle discussion + if(this.PeerConnectionArray.size === 0){ + mediaManager.createNotification(user.name??''); + } this.PeerConnectionArray.set(user.userId, peer); for (const peerConnectionListener of this.peerConnectionListeners) { @@ -169,7 +204,7 @@ export class SimplePeer { /** * create peer connection to bind users */ - private createPeerScreenSharingConnection(user : UserSimplePeerInterface) : ScreenSharingPeer | null{ + private createPeerScreenSharingConnection(user : UserSimplePeerInterface, stream: MediaStream | null) : ScreenSharingPeer | null{ const peerConnection = this.PeerScreenSharingConnectionArray.get(user.userId); if(peerConnection){ if(peerConnection.destroyed){ @@ -179,7 +214,7 @@ export class SimplePeer { if(!peerConnexionDeleted){ throw 'Error to delete peer connection'; } - this.createPeerConnection(user); + this.createPeerConnection(user, stream); }else { peerConnection.toClose = false; } @@ -198,7 +233,7 @@ export class SimplePeer { user.webRtcPassword = this.lastWebrtcPassword; } - const peer = new ScreenSharingPeer(user, user.initiator ? user.initiator : false, this.Connection); + const peer = new ScreenSharingPeer(user, user.initiator ? user.initiator : false, this.Connection, stream); this.PeerScreenSharingConnectionArray.set(user.userId, peer); for (const peerConnectionListener of this.peerConnectionListeners) { @@ -211,7 +246,6 @@ export class SimplePeer { * This is triggered twice. Once by the server, and once by a remote client disconnecting */ private closeConnection(userId : number) { - mediaManager.playWebrtcOutSound(); try { const peer = this.PeerConnectionArray.get(userId); if (peer === undefined) { @@ -228,7 +262,7 @@ export class SimplePeer { const userIndex = this.Users.findIndex(user => user.userId === userId); if(userIndex < 0){ - throw 'Couln\'t delete user'; + throw 'Couldn\'t delete user'; } else { this.Users.splice(userIndex, 1); } @@ -288,7 +322,9 @@ export class SimplePeer { * Unregisters any held event handler. */ public unregister() { - mediaManager.removeUpdateLocalStreamEventListener(this.sendLocalVideoStreamCallback); + for (const unsubscriber of this.unsubscribers) { + unsubscriber(); + } } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -296,7 +332,13 @@ export class SimplePeer { try { //if offer type, create peer connection if(data.signal.type === "offer"){ - this.createPeerConnection(data); + const streamResult = get(localStreamStore); + let stream : MediaStream | null = null; + if (streamResult.type === 'success' && streamResult.stream) { + stream = streamResult.stream; + } + + this.createPeerConnection(data, stream); } const peer = this.PeerConnectionArray.get(data.userId); if (peer !== undefined) { @@ -312,18 +354,26 @@ export class SimplePeer { private receiveWebrtcScreenSharingSignal(data: WebRtcSignalReceivedMessageInterface) { if (blackListManager.isBlackListed(data.userId)) return; console.log("receiveWebrtcScreenSharingSignal", data); + const streamResult = get(screenSharingLocalStreamStore); + let stream : MediaStream | null = null; + if (streamResult.type === 'success' && streamResult.stream !== null) { + stream = streamResult.stream; + } + try { //if offer type, create peer connection if(data.signal.type === "offer"){ - this.createPeerScreenSharingConnection(data); + this.createPeerScreenSharingConnection(data, stream); } const peer = this.PeerScreenSharingConnectionArray.get(data.userId); if (peer !== undefined) { peer.signal(data.signal); } else { console.error('Could not find peer whose ID is "'+data.userId+'" in receiveWebrtcScreenSharingSignal'); - console.info('tentative to create new peer connexion'); - this.sendLocalScreenSharingStreamToUser(data.userId); + console.info('Attempt to create new peer connexion'); + if (stream) { + this.sendLocalScreenSharingStreamToUser(data.userId, stream); + } } } catch (e) { console.error(`receiveWebrtcSignal => ${data.userId}`, e); @@ -333,14 +383,19 @@ export class SimplePeer { } } - private pushVideoToRemoteUser(userId : number) { + private pushVideoToRemoteUser(userId: number, streamResult: LocalStreamStoreValue) { try { const PeerConnection = this.PeerConnectionArray.get(userId); if (!PeerConnection) { throw new Error('While adding media, cannot find user with ID ' + userId); } - const localStream: MediaStream | null = mediaManager.localStream; - PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...mediaManager.constraintsMedia}))); + + PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...streamResult.constraints}))); + + if (streamResult.type === 'error') { + return; + } + const localStream: MediaStream | null = streamResult.stream; if(!localStream){ return; @@ -357,15 +412,11 @@ export class SimplePeer { } } - private pushScreenSharingToRemoteUser(userId : number) { + private pushScreenSharingToRemoteUser(userId: number, localScreenCapture: MediaStream) { const PeerConnection = this.PeerScreenSharingConnectionArray.get(userId); if (!PeerConnection) { throw new Error('While pushing screen sharing, cannot find user with ID ' + userId); } - const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture; - if(!localScreenCapture){ - return; - } for (const track of localScreenCapture.getTracks()) { PeerConnection.addTrack(track, localScreenCapture); @@ -373,23 +424,18 @@ export class SimplePeer { return; } - public sendLocalVideoStream(){ + public sendLocalVideoStream(streamResult: LocalStreamStoreValue){ for (const user of this.Users) { - this.pushVideoToRemoteUser(user.userId); + this.pushVideoToRemoteUser(user.userId, streamResult); } } /** * Triggered locally when clicking on the screen sharing button */ - public sendLocalScreenSharingStream() { - if (!mediaManager.localScreenCapture) { - console.error('Could not find localScreenCapture to share') - return; - } - + public sendLocalScreenSharingStream(localScreenCapture: MediaStream) { for (const user of this.Users) { - this.sendLocalScreenSharingStreamToUser(user.userId); + this.sendLocalScreenSharingStreamToUser(user.userId, localScreenCapture); } } @@ -402,11 +448,11 @@ export class SimplePeer { } } - private sendLocalScreenSharingStreamToUser(userId: number): void { + private sendLocalScreenSharingStreamToUser(userId: number, localScreenCapture: MediaStream): void { if (blackListManager.isBlackListed(userId)) return; // If a connection already exists with user (because it is already sharing a screen with us... let's use this connection) if (this.PeerScreenSharingConnectionArray.has(userId)) { - this.pushScreenSharingToRemoteUser(userId); + this.pushScreenSharingToRemoteUser(userId, localScreenCapture); return; } @@ -414,7 +460,7 @@ export class SimplePeer { userId, initiator: true }; - const PeerConnectionScreenSharing = this.createPeerScreenSharingConnection(screenSharingUser); + const PeerConnectionScreenSharing = this.createPeerScreenSharingConnection(screenSharingUser, localScreenCapture); if (!PeerConnectionScreenSharing) { return; } diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index 2952ea62..5ca8952c 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -1,10 +1,12 @@ -import * as SimplePeerNamespace from "simple-peer"; +import type * as SimplePeerNamespace from "simple-peer"; import {mediaManager} from "./MediaManager"; import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable"; -import {RoomConnection} from "../Connexion/RoomConnection"; +import type {RoomConnection} from "../Connexion/RoomConnection"; import {blackListManager} from "./BlackListManager"; -import {Subscription} from "rxjs"; -import {UserSimplePeerInterface} from "./SimplePeer"; +import type {Subscription} from "rxjs"; +import type {UserSimplePeerInterface} from "./SimplePeer"; +import {get} from "svelte/store"; +import {obtainedMediaConstraintStore} from "../Stores/MediaStore"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); @@ -25,7 +27,7 @@ export class VideoPeer extends Peer { private onBlockSubscribe: Subscription; private onUnBlockSubscribe: Subscription; - constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) { + constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection, localStream: MediaStream | null) { super({ initiator: initiator ? initiator : false, //reconnectTimer: 10000, @@ -105,7 +107,7 @@ export class VideoPeer extends Peer { this._onFinish(); }); - this.pushVideoToRemoteUser(); + this.pushVideoToRemoteUser(localStream); this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userId) => { if (userId === this.userId) { this.toggleRemoteStream(false); @@ -188,10 +190,9 @@ export class VideoPeer extends Peer { } } - private pushVideoToRemoteUser() { + private pushVideoToRemoteUser(localStream: MediaStream | null) { try { - const localStream: MediaStream | null = mediaManager.localStream; - this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...mediaManager.constraintsMedia}))); + this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...get(obtainedMediaConstraintStore)}))); if(!localStream){ return; diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index ad7cda70..ea987f21 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,14 +1,18 @@ -import { ChatEvent } from "./Api/Events/ChatEvent"; +import type { ChatEvent } from "./Api/Events/ChatEvent"; import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent"; import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent"; import { Subject } from "rxjs"; import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent"; -import { OpenPopupEvent } from "./Api/Events/OpenPopupEvent"; +import type { OpenPopupEvent } from "./Api/Events/OpenPopupEvent"; import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent"; -import { ClosePopupEvent } from "./Api/Events/ClosePopupEvent"; -import { OpenTabEvent } from "./Api/Events/OpenTabEvent"; -import { GoToPageEvent } from "./Api/Events/GoToPageEvent"; -import { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent"; +import type { ClosePopupEvent } from "./Api/Events/ClosePopupEvent"; +import type { OpenTabEvent } from "./Api/Events/OpenTabEvent"; +import type { GoToPageEvent } from "./Api/Events/GoToPageEvent"; +import type { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent"; +import type {PlaySoundEvent} from "./Api/Events/PlaySoundEvent"; +import type {StopSoundEvent} from "./Api/Events/StopSoundEvent"; +import type {LoadSoundEvent} from "./Api/Events/LoadSoundEvent"; +import SoundConfig = Phaser.Types.Sound.SoundConfig; import { LoadPageEvent } from './Api/Events/LoadPageEvent'; interface WorkAdventureApi { @@ -22,10 +26,11 @@ interface WorkAdventureApi { exitSceneTo(url : string): void; openCoWebSite(url : string): void; closeCoWebSite(): void; - disablePlayerControl(): void; - restorePlayerControl(): void; + disablePlayerControls(): void; + restorePlayerControls(): void; displayBubble(): void; removeBubble(): void; + loadSound(url : string): Sound; } declare global { @@ -59,7 +64,7 @@ interface ButtonDescriptor { callback: ButtonClickedCallback, } -class Popup { +export class Popup { constructor(private id: number) { } @@ -76,6 +81,41 @@ class Popup { } } +export class Sound { + constructor(private url: string) { + window.parent.postMessage({ + "type" : 'loadSound', + "data": { + url: this.url, + } as LoadSoundEvent + + },'*'); + } + + public play(config : SoundConfig) { + window.parent.postMessage({ + "type" : 'playSound', + "data": { + url: this.url, + config + } as PlaySoundEvent + + },'*'); + return this.url; + } + public stop() { + window.parent.postMessage({ + "type" : 'stopSound', + "data": { + url: this.url, + } as StopSoundEvent + + },'*'); + return this.url; + } + +} + window.WA = { /** * Send a message in the chat. @@ -90,12 +130,12 @@ window.WA = { } as ChatEvent }, '*'); }, - disablePlayerControl(): void { - window.parent.postMessage({ 'type': 'disablePlayerControl' }, '*'); + disablePlayerControls(): void { + window.parent.postMessage({ 'type': 'disablePlayerControls' }, '*'); }, - restorePlayerControl(): void { - window.parent.postMessage({ 'type': 'restorePlayerControl' }, '*'); + restorePlayerControls(): void { + window.parent.postMessage({ 'type': 'restorePlayerControls' }, '*'); }, displayBubble(): void { @@ -115,7 +155,11 @@ window.WA = { }, '*'); }, - goToPage(url: string): void { + loadSound(url: string) : Sound { + return new Sound(url); + }, + + goToPage(url : string) : void{ window.parent.postMessage({ "type": 'goToPage', "data": { diff --git a/front/src/index.ts b/front/src/index.ts index 4065788e..90d4c612 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -1,6 +1,6 @@ import 'phaser'; import GameConfig = Phaser.Types.Core.GameConfig; -import "../dist/resources/style/index.scss"; +import "../style/index.scss"; import {DEBUG_MODE, isMobile} from "./Enum/EnvironmentVariable"; import {LoginScene} from "./Phaser/Login/LoginScene"; @@ -9,11 +9,10 @@ import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; import {SelectCompanionScene} from "./Phaser/Login/SelectCompanionScene"; import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; -import {ResizableScene} from "./Phaser/Login/ResizableScene"; +import WebFontLoaderPlugin from 'phaser3-rex-plugins/plugins/webfontloader-plugin.js'; import {EntryScene} from "./Phaser/Login/EntryScene"; import {coWebsiteManager} from "./WebRtc/CoWebsiteManager"; import {MenuScene} from "./Phaser/Menu/MenuScene"; -import {HelpCameraSettingsScene} from "./Phaser/Menu/HelpCameraSettingsScene"; import {localUserStore} from "./Connexion/LocalUserStore"; import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene"; import {iframeListener} from "./Api/IframeListener"; @@ -21,6 +20,8 @@ import { SelectCharacterMobileScene } from './Phaser/Login/SelectCharacterMobile import {HdpiManager} from "./Phaser/Services/HdpiManager"; import {waScaleManager} from "./Phaser/Services/WaScaleManager"; import {Game} from "./Phaser/Game/Game"; +import App from './Components/App.svelte'; +import {HtmlUtils} from "./WebRtc/HtmlUtils"; const {width, height} = coWebsiteManager.getGameSize(); @@ -94,7 +95,7 @@ const config: GameConfig = { ErrorScene, CustomizeScene, MenuScene, - HelpCameraSettingsScene], + ], //resolution: window.devicePixelRatio / 2, fps: fps, dom: { @@ -105,6 +106,13 @@ const config: GameConfig = { roundPixels: true, antialias: false }, + plugins: { + global: [{ + key: 'rexWebFontLoader', + plugin: WebFontLoaderPlugin, + start: true + }] + }, physics: { default: "arcade", arcade: { @@ -127,19 +135,12 @@ const config: GameConfig = { //const game = new Phaser.Game(config); const game = new Game(config); -waScaleManager.setScaleManager(game.scale); +waScaleManager.setGame(game); window.addEventListener('resize', function (event) { coWebsiteManager.resetStyle(); waScaleManager.applyNewSize(); - - // Let's trigger the onResize method of any active scene that is a ResizableScene - for (const scene of game.scene.getScenes(true)) { - if (scene instanceof ResizableScene) { - scene.onResize(event); - } - } }); coWebsiteManager.onResize.subscribe(() => { @@ -147,3 +148,12 @@ coWebsiteManager.onResize.subscribe(() => { }); iframeListener.init(); + +const app = new App({ + target: HtmlUtils.getElementByIdOrFail('svelte-overlay'), + props: { + game: game + }, +}) + +export default app diff --git a/front/src/rex-plugins.d.ts b/front/src/rex-plugins.d.ts index d5457702..2e160315 100644 --- a/front/src/rex-plugins.d.ts +++ b/front/src/rex-plugins.d.ts @@ -7,6 +7,10 @@ declare module 'phaser3-rex-plugins/plugins/gestures-plugin.js' { const content: any; // eslint-disable-line export default content; } +declare module 'phaser3-rex-plugins/plugins/webfontloader-plugin.js' { + const content: any; // eslint-disable-line + export default content; +} declare module 'phaser3-rex-plugins/plugins/gestures.js' { export const Pinch: any; // eslint-disable-line } diff --git a/front/src/types.ts b/front/src/types.ts index 6b99434d..6b9e97e6 100644 --- a/front/src/types.ts +++ b/front/src/types.ts @@ -1,4 +1,4 @@ -import Phaser from "phaser"; +import type Phaser from "phaser"; export type CursorKey = { isDown: boolean diff --git a/front/dist/resources/style/cowebsite-mobile.scss b/front/style/cowebsite-mobile.scss similarity index 96% rename from front/dist/resources/style/cowebsite-mobile.scss rename to front/style/cowebsite-mobile.scss index 85b41511..48a6689e 100644 --- a/front/dist/resources/style/cowebsite-mobile.scss +++ b/front/style/cowebsite-mobile.scss @@ -28,6 +28,7 @@ aside { height: 30px; + min-height: 30px; cursor: ns-resize; flex-direction: column; diff --git a/front/dist/resources/style/cowebsite.scss b/front/style/cowebsite.scss similarity index 92% rename from front/dist/resources/style/cowebsite.scss rename to front/style/cowebsite.scss index 515dc0df..e508b58d 100644 --- a/front/dist/resources/style/cowebsite.scss +++ b/front/style/cowebsite.scss @@ -4,7 +4,7 @@ position: fixed; transition: transform 0.5s; background-color: white; - + &.loading { background-color: gray; } @@ -13,9 +13,11 @@ iframe { width: 100%; height: 100%; + max-width: 100vw; + max-height: 100vh; } } - + aside { background: gray; align-items: center; @@ -32,7 +34,7 @@ position: absolute; background: none; border: none; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; img { height: 25px; diff --git a/front/style/fonts.scss b/front/style/fonts.scss new file mode 100644 index 00000000..a49d3967 --- /dev/null +++ b/front/style/fonts.scss @@ -0,0 +1,9 @@ +@import "~@fontsource/press-start-2p/index.css"; + +*{ + font-family: PixelFont-7,monospace; +} + +.nes-btn { + font-family: "Press Start 2P"; +} diff --git a/front/dist/resources/logos/cursor_normal.png b/front/style/images/cursor_normal.png similarity index 100% rename from front/dist/resources/logos/cursor_normal.png rename to front/style/images/cursor_normal.png diff --git a/front/dist/resources/logos/cursor_pointer.png b/front/style/images/cursor_pointer.png similarity index 100% rename from front/dist/resources/logos/cursor_pointer.png rename to front/style/images/cursor_pointer.png diff --git a/front/style/index.scss b/front/style/index.scss new file mode 100644 index 00000000..7ed141cd --- /dev/null +++ b/front/style/index.scss @@ -0,0 +1,5 @@ +@import "cowebsite.scss"; +@import "cowebsite-mobile.scss"; +@import "style"; +@import "mobile-style.scss"; +@import "fonts.scss"; diff --git a/front/dist/resources/style/mobile-style.scss b/front/style/mobile-style.scss similarity index 70% rename from front/dist/resources/style/mobile-style.scss rename to front/style/mobile-style.scss index 21753ebd..1b37053a 100644 --- a/front/dist/resources/style/mobile-style.scss +++ b/front/style/mobile-style.scss @@ -1,9 +1,24 @@ +@media (hover: none) { + /** + * If we cannot hover over elements, let's display camera button in full. + */ + .btn-cam-action { + div { + transform: translateY(0px); + } + } +} + @media screen and (max-width: 700px), screen and (max-height: 700px){ - video#myCamVideo { + video.myCamVideo { width: 150px; } + .div-myCamVideo.hide { + right: -160px; + } + .sidebar { width: 20%; min-width: 200px; @@ -22,21 +37,6 @@ } } - .btn-cam-action { - min-width: 150px; - - &:hover{ - transform: translateY(20px); - } - div { - margin: 0 1%; - &:hover { - background-color: #666; - } - margin-bottom: 30px; - } - } - .main-section { position: absolute; width: 100%; diff --git a/front/dist/resources/style/style.css b/front/style/style.scss similarity index 87% rename from front/dist/resources/style/style.css rename to front/style/style.scss index 717e1d69..beed1db5 100644 --- a/front/dist/resources/style/style.css +++ b/front/style/style.scss @@ -1,9 +1,9 @@ *{ font-family: 'Open Sans', sans-serif; - cursor: url('/resources/logos/cursor_normal.png'), auto; + cursor: url('./images/cursor_normal.png'), auto; } * a, button, select{ - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } body{ overflow: hidden; @@ -39,7 +39,7 @@ body .message-info.warning{ position: relative; transition: all 0.2s ease; background-color: #00000099; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } .video-container i{ position: absolute; @@ -75,7 +75,7 @@ body .message-info.warning{ .video-container button.report{ display: block; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; background: none; background-color: rgba(0, 0, 0, 0); border: none; @@ -108,7 +108,7 @@ body .message-info.warning{ left: 5px; margin: 0; padding: 0; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; width: 25px; height: 25px; } @@ -118,7 +118,7 @@ body .message-info.warning{ left: 36px; color: white; font-size: 16px; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } .video-container img.active { display: block !important; @@ -126,26 +126,34 @@ body .message-info.warning{ .video-container video{ height: 100%; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } .video-container video:focus{ outline: none; } -.video-container#div-myCamVideo{ +.video-container.div-myCamVideo{ border: none; + background-color: transparent; } -#div-myCamVideo { +.div-myCamVideo { position: absolute; right: 15px; bottom: 30px; border-radius: 15px 15px 15px 15px; max-height: 20%; + transition: right 350ms; } -video#myCamVideo{ +.div-myCamVideo.hide { + right: -20vw; +} + +video.myCamVideo{ + background-color: #00000099; + max-height: 20vh; width: 15vw; -webkit-transform: scaleX(-1); transform: scaleX(-1); @@ -196,18 +204,20 @@ video#myCamVideo{ display: inline-flex; bottom: 10px; right: 15px; - width: 15vw; + width: 180px; height: 40px; text-align: center; align-content: center; align-items: center; - justify-content: center; + justify-content: flex-end; justify-items: center; } /*btn animation*/ .btn-cam-action div{ - cursor: url('/resources/logos/cursor_pointer.png'), pointer; - /*position: absolute;*/ + cursor: url('./images/cursor_pointer.png'), pointer; + display: flex; + align-items: center; + justify-content: center; border: solid 0px black; width: 44px; height: 44px; @@ -216,7 +226,6 @@ video#myCamVideo{ border-radius: 48px; transform: translateY(20px); transition-timing-function: ease-in-out; - margin-bottom: 20px; margin: 0 4%; } .btn-cam-action div.disabled { @@ -248,6 +257,12 @@ video#myCamVideo{ transition: all .2s; /*right: 224px;*/ } +.btn-monitor.hide { + transform: translateY(60px); +} +.btn-cam-action:hover .btn-monitor.hide{ + transform: translateY(60px); +} .btn-copy{ pointer-events: auto; transition: all .3s; @@ -257,10 +272,8 @@ video#myCamVideo{ .btn-cam-action div img{ height: 22px; width: 30px; - top: calc(48px - 37px); - left: calc(48px - 41px); position: relative; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } /* Spinner */ @@ -321,46 +334,15 @@ video#myCamVideo{ } } -.webrtcsetup{ - display: none; - position: absolute; - top: 140px; - left: 0; - right: 0; - margin-left: auto; - margin-right: auto; - height: 50%; - width: 50%; - border: white 6px solid; -} -.webrtcsetup .background-img { - position: relative; - display: block; - width: 40%; - height: 60%; - margin-left: auto; - margin-right: auto; - top: 50%; - transform: translateY(-50%); -} -#myCamVideoSetup { - width: 100%; - height: 100%; -} -.webrtcsetup.active{ - display: block; -} - - /* New layout */ body { margin: 0; - height: 100vh; - width: 100vw; + height: 100%; + width: 100%; } .main-container { - height: 100vh; - width: 100vw; + height: 100%; + width: 100%; position: absolute; } @@ -385,7 +367,6 @@ body { } #game { - width: 100%; position: relative; /* Position relative is needed for the game-overlay. */ } @@ -572,7 +553,7 @@ input[type=range]:focus::-ms-fill-upper { margin: 2%; flex-basis: 96%; transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; pointer-events: auto; /*flex-shrink: 2;*/ } @@ -590,7 +571,7 @@ input[type=range]:focus::-ms-fill-upper { .sidebar > div { margin: 2%; transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; border-radius: 15px 15px 15px 15px; pointer-events: auto; } @@ -600,13 +581,12 @@ input[type=range]:focus::-ms-fill-upper { } .sidebar > div video { - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } /* Let's make sure videos are vertically centered if they need to be cropped */ .media-container { display: flex; - justify-content: center; flex-direction: column; overflow: hidden; border-radius: 15px; @@ -625,7 +605,7 @@ input[type=range]:focus::-ms-fill-upper { margin: 1%; max-height: 96%; transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } .chat-mode > div:hover { @@ -715,7 +695,7 @@ input[type=range]:focus::-ms-fill-upper { margin-top: 6px; width: 30px; height: 30px; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; padding: 0 5px; transition: all .5s ease; transform: rotateY(0); @@ -739,7 +719,7 @@ input[type=range]:focus::-ms-fill-upper { .main-console div.console:hover, .message-container div.clear:hover { - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; top: calc(100% + 5px); transform: scale(1.2) translateY(3px); } @@ -772,7 +752,7 @@ input[type=range]:focus::-ms-fill-upper { transition: all .2s ease; } .main-console .btn-action .btn:hover{ - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; background-color: #ffda01; color: black; border: 1px solid black; @@ -787,7 +767,7 @@ input[type=range]:focus::-ms-fill-upper { .main-console .menu span { margin: 20px; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } .main-console .menu span.active { @@ -821,42 +801,13 @@ input[type=range]:focus::-ms-fill-upper { } .main-console section div.upload label img{ height: 150px; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } .main-console section div.upload label img{ - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } - -/*audio html when audio message playing*/ -.main-container .audio-playing { - position: absolute; - width: 200px; - height: 54px; - right: -210px; - top: 40px; - transition: all 0.1s ease-out; - background-color: black; - border-radius: 30px 0 0 30px; - display: inline-flex; -} - -.main-container .audio-playing.active{ - right: 0; -} -.main-container .audio-playing img{ - /*width: 30px;*/ - border-radius: 50%; - background-color: #ffda01; - padding: 10px; -} -.main-container .audio-playing p{ - color: white; - margin-left: 10px; - margin-top: 14px; -} - /* VIDEO QUALITY */ .main-console div.setting h1{ color: white; @@ -917,7 +868,7 @@ div.modal-report-user{ right: 0; left: auto; top: 0; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; width: 15px; height: 15px; margin: 10px; @@ -936,7 +887,7 @@ div.modal-report-user{ transition: all .2s ease; } .modal-report-user button:hover{ - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; background-color: #ffda01; color: black; border: 1px solid black; @@ -979,7 +930,7 @@ div.modal-report-user{ } .discussion .active-btn{ display: none; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; height: 50px; width: 50px; background-color: #2d2d2dba; @@ -1008,7 +959,7 @@ div.modal-report-user{ right: 10px; background: none; border: none; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } .discussion .close-btn img{ height: 15px; @@ -1033,7 +984,7 @@ div.modal-report-user{ background-color: #ffffff69; padding: 5px; border-radius: 15px; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; } .discussion .participants .participant:hover{ @@ -1066,7 +1017,7 @@ div.modal-report-user{ } .discussion .participants .participant button.report-btn{ - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; position: absolute; background-color: #2d2d2dba; right: 34px; @@ -1124,6 +1075,10 @@ div.modal-report-user{ color: white; } +.discussion .messages .message p a:visited{ + color: white; +} + .discussion .send-message{ position: absolute; bottom: 45px; @@ -1176,7 +1131,7 @@ div.action.danger{ animation-timing-function: ease-in-out; } div.action p.action-body{ - cursor: url('/resources/logos/cursor_pointer.png'), pointer; + cursor: url('./images/cursor_pointer.png'), pointer; padding: 10px; background-color: #2d2d2dba; color: #fff; @@ -1225,3 +1180,29 @@ div.action.danger p.action-body{ 50% {bottom: 30px;} 100% {bottom: 40px;} } + + +#svelte-overlay { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; + + & > div { + position: relative; + width: 100%; + height: 100%; + + & > div { + position: absolute; + width: 100%; + height: 100%; + } + + & > div.scrollable { + overflow-y: auto; + -webkit-overflow-scrolling: touch; + pointer-events: auto; + } + } +} diff --git a/front/tests/Phaser/Connexion/LocalUserTest.ts b/front/tests/Phaser/Connexion/LocalUserTest.ts index 25b54005..4ba20745 100644 --- a/front/tests/Phaser/Connexion/LocalUserTest.ts +++ b/front/tests/Phaser/Connexion/LocalUserTest.ts @@ -19,8 +19,14 @@ describe("isUserNameValid()", () => { it("should not validate spaces", () => { expect(isUserNameValid(' ')).toBe(false); }); - it("should not validate special characters", () => { - expect(isUserNameValid('a&-')).toBe(false); + it("should validate special characters", () => { + expect(isUserNameValid('%&-')).toBe(true); + }); + it("should validate accents", () => { + expect(isUserNameValid('éàëè')).toBe(true); + }); + it("should validate chinese characters", () => { + expect(isUserNameValid('中文鍵盤')).toBe(true); }); }); diff --git a/front/tests/Phaser/Services/HdpiManagerTest.ts b/front/tests/Phaser/Services/HdpiManagerTest.ts index 32a6b03a..2d1fee70 100644 --- a/front/tests/Phaser/Services/HdpiManagerTest.ts +++ b/front/tests/Phaser/Services/HdpiManagerTest.ts @@ -50,6 +50,6 @@ describe("Test HdpiManager", () => { const result = hdpiManager.getOptimalGameSize({ width: 1280, height: 768 }); expect(result.game.width).toEqual(1280); expect(result.game.height).toEqual(768); - expect(hdpiManager.zoomModifier).toEqual(1); + expect(hdpiManager.zoomModifier).toEqual(2 / 3); }); }); diff --git a/front/tsconfig-for-jasmine.json b/front/tsconfig-for-jasmine.json new file mode 100644 index 00000000..5fb31027 --- /dev/null +++ b/front/tsconfig-for-jasmine.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "include": ["./src/**/*", "./tests/**/*"], + "compilerOptions": { + "module": "CommonJS", + "lib": ["es2015","dom"], + "types": ["svelte", "node"] + }, +} diff --git a/front/tsconfig-for-webpack.json b/front/tsconfig-for-webpack.json new file mode 100644 index 00000000..f4195027 --- /dev/null +++ b/front/tsconfig-for-webpack.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "esModuleInterop": true + } +} diff --git a/front/tsconfig.json b/front/tsconfig.json index fea8fd54..d4167a30 100644 --- a/front/tsconfig.json +++ b/front/tsconfig.json @@ -1,16 +1,22 @@ { +// "include": ["src/**/*", "webpack.config.ts"], + + "extends": "@tsconfig/svelte/tsconfig.json", "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "moduleResolution": "node", - "module": "CommonJS", - "target": "ES2015", + //"module": "CommonJS", + "module": "ESNext", + "target": "ES2017", "declaration": false, "downlevelIteration": true, "jsx": "react", "allowJs": true, "esModuleInterop": true, + "importsNotUsedAsValues": "error", + "strict": true, /* Enable all strict type-checking options. */ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ "strictNullChecks": true, /* Enable strict null checks. */ diff --git a/front/webpack.config.js b/front/webpack.config.js deleted file mode 100644 index f1a3df44..00000000 --- a/front/webpack.config.js +++ /dev/null @@ -1,88 +0,0 @@ -const path = require('path'); -const webpack = require('webpack'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); - -module.exports = { - entry: { - 'main': './src/index.ts', - 'iframe_api': './src/iframe_api.ts' - }, - devtool: 'inline-source-map', - devServer: { - contentBase: './dist', - host: '0.0.0.0', - sockPort: 80, - disableHostCheck: true, - historyApiFallback: { - rewrites: [ - { from: /^_\/.*$/, to: '/index.html' } - ], - disableDotRule: true - }, - }, - module: { - rules: [ - { - test: /\.tsx?$/, - use: 'ts-loader', - exclude: /node_modules/, - }, - { - test: /\.scss$/, - use: [MiniCssExtractPlugin.loader, 'css-loader?url=false', 'sass-loader'], - }, - ], - }, - resolve: { - extensions: [ '.tsx', '.ts', '.js' ], - }, - output: { - filename: (pathData) => { - // Add a content hash only for the main bundle. - // We want the iframe_api.js file to keep its name as it will be referenced from outside iframes. - return pathData.chunk.name === 'main' ? 'js/[name].[contenthash].js': '[name].js'; - }, - path: path.resolve(__dirname, 'dist'), - publicPath: '/' - }, - externals:[ - require('webpack-require-http') - ], - plugins: [ - new MiniCssExtractPlugin({filename: 'style.[contenthash].css'}), - new HtmlWebpackPlugin( - { - template: './dist/index.tmpl.html.tmp', - minify: { - collapseWhitespace: true, - keepClosingSlash: true, - removeComments: false, - removeRedundantAttributes: true, - removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributes: true, - useShortDoctype: true - }, - chunks: ['main'] - } - ), - new webpack.ProvidePlugin({ - Phaser: 'phaser' - }), - new webpack.EnvironmentPlugin({ - 'API_URL': null, - 'PUSHER_URL': undefined, - 'UPLOADER_URL': null, - 'ADMIN_URL': null, - 'DEBUG_MODE': null, - 'STUN_SERVER': null, - 'TURN_SERVER': null, - 'TURN_USER': null, - 'TURN_PASSWORD': null, - 'JITSI_URL': null, - 'JITSI_PRIVATE_MODE': null, - 'START_ROOM_URL': null - }) - ], - -}; diff --git a/front/webpack.config.ts b/front/webpack.config.ts new file mode 100644 index 00000000..3a69b74a --- /dev/null +++ b/front/webpack.config.ts @@ -0,0 +1,193 @@ +import type {Configuration} from "webpack"; +import type WebpackDevServer from "webpack-dev-server"; +import path from 'path'; +import webpack from 'webpack'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import sveltePreprocess from 'svelte-preprocess'; +import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"; +import NodePolyfillPlugin from 'node-polyfill-webpack-plugin'; +import {DISPLAY_TERMS_OF_USE} from "./src/Enum/EnvironmentVariable"; + +const mode = process.env.NODE_ENV ?? 'development'; +const isProduction = mode === 'production'; +const isDevelopment = !isProduction; + +module.exports = { + entry: { + 'main': './src/index.ts', + 'iframe_api': './src/iframe_api.ts' + }, + mode: mode, + devtool: isDevelopment ? 'inline-source-map' : 'source-map', + devServer: { + contentBase: './dist', + host: '0.0.0.0', + sockPort: 80, + disableHostCheck: true, + historyApiFallback: { + rewrites: [ + { from: /^_\/.*$/, to: '/index.html' } + ], + disableDotRule: true + }, + }, + module: { + rules: [ + { + test: /\.tsx?$/, + //use: 'ts-loader', + exclude: /node_modules/, + loader: 'ts-loader', + options: { + transpileOnly: true, + }, + }, + { + test: /\.scss$/, + exclude: /node_modules/, + use: [ + MiniCssExtractPlugin.loader, { + loader: 'css-loader', + options: { + //url: false, + sourceMap: true + } + }, 'sass-loader'], + }, + { + test: /\.css$/, + exclude: /node_modules/, + use: [ + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + //url: false, + sourceMap: true + } + } + ] + }, + { + test: /\.(html|svelte)$/, + exclude: /node_modules/, + use: { + loader: 'svelte-loader', + options: { + compilerOptions: { + // Dev mode must be enabled for HMR to work! + dev: isDevelopment + }, + emitCss: isProduction, + hotReload: isDevelopment, + hotOptions: { + // List of options and defaults: https://www.npmjs.com/package/svelte-loader-hot#usage + noPreserveState: false, + optimistic: true, + }, + preprocess: sveltePreprocess({ + scss: true, + sass: true, + }), + onwarn: function (warning: { code: string }, handleWarning: (warning: { code: string }) => void) { + // See https://github.com/sveltejs/svelte/issues/4946#issuecomment-662168782 + + if (warning.code === 'a11y-no-onchange') { return } + if (warning.code === 'a11y-autofocus') { return } + + // process as usual + handleWarning(warning); + } + } + } + }, + + // Required to prevent errors from Svelte on Webpack 5+, omit on Webpack 4 + // See: https://github.com/sveltejs/svelte-loader#usage + { + test: /node_modules\/svelte\/.*\.mjs$/, + resolve: { + fullySpecified: false + } + }, + { + test: /\.(eot|svg|png|gif|jpg)$/, + exclude: /node_modules/, + type: 'asset' + }, + { + test: /\.(woff(2)?|ttf)$/, + type: 'asset', + generator: { + filename: 'fonts/[name][ext]' + } + + } + ], + }, + resolve: { + alias: { + svelte: path.resolve('node_modules', 'svelte') + }, + extensions: [ '.tsx', '.ts', '.js', '.svelte' ], + mainFields: ['svelte', 'browser', 'module', 'main'] + }, + output: { + filename: (pathData) => { + // Add a content hash only for the main bundle. + // We want the iframe_api.js file to keep its name as it will be referenced from outside iframes. + return pathData.chunk?.name === 'main' ? 'js/[name].[contenthash].js': '[name].js'; + }, + path: path.resolve(__dirname, 'dist'), + publicPath: '/' + }, + plugins: [ + new webpack.HotModuleReplacementPlugin(), + new ForkTsCheckerWebpackPlugin({ + eslint: { + files: './src/**/*.ts' + } + }), + new MiniCssExtractPlugin({filename: '[name].[contenthash].css'}), + new HtmlWebpackPlugin( + { + template: './dist/index.tmpl.html.tmp', + minify: { + collapseWhitespace: true, + keepClosingSlash: true, + removeComments: false, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true + }, + chunks: ['main'] + } + ), + new webpack.ProvidePlugin({ + Phaser: 'phaser' + }), + new NodePolyfillPlugin(), + new webpack.EnvironmentPlugin({ + 'API_URL': null, + 'SKIP_RENDER_OPTIMIZATIONS': false, + 'DISABLE_NOTIFICATIONS': false, + 'PUSHER_URL': undefined, + 'UPLOADER_URL': null, + 'ADMIN_URL': null, + 'DEBUG_MODE': null, + 'STUN_SERVER': null, + 'TURN_SERVER': null, + 'TURN_USER': null, + 'TURN_PASSWORD': null, + 'JITSI_URL': null, + 'JITSI_PRIVATE_MODE': null, + 'START_ROOM_URL': null, + 'MAX_USERNAME_LENGTH': 8, + 'MAX_PER_GROUP': 4, + 'DISPLAY_TERMS_OF_USE': false, + }) + ], + +} as Configuration & WebpackDevServer.Configuration; diff --git a/front/webpack.prod.js b/front/webpack.prod.js deleted file mode 100644 index b5695cc6..00000000 --- a/front/webpack.prod.js +++ /dev/null @@ -1,7 +0,0 @@ -const merge = require('webpack-merge'); -const common = require('./webpack.config.js'); - -module.exports = merge(common, { - mode: 'production', - devtool: 'source-map' -}); diff --git a/front/yarn.lock b/front/yarn.lock index 528d4320..dc43f559 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -2,36 +2,159 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== dependencies: "@babel/highlight" "^7.12.13" -"@babel/helper-validator-identifier@^7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" - integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== +"@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== -"@babel/highlight@^7.12.13": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" - integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" + "@babel/helper-validator-identifier" "^7.14.0" chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/runtime@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" + integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== + dependencies: + regenerator-runtime "^0.13.4" + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d" + integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g== + +"@eslint/eslintrc@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.1.tgz#442763b88cecbe3ee0ec7ca6d6dd6168550cbf14" + integrity sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@fontsource/press-start-2p@^4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@fontsource/press-start-2p/-/press-start-2p-4.3.0.tgz#37124387f7fbfe7792b5fc9a1906b80d9aeda4c6" + integrity sha512-gmS4070EoZp5/6NUJ+tBnvtDiSmFcR+S+ClAOJ8NGFXDWOkO12yMnyGJEJaDCNCAMX0s2TQCcmr6qWKx5ad3RQ== + +"@nodelib/fs.scandir@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" + integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== + dependencies: + "@nodelib/fs.stat" "2.0.4" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" + integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" + integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== + dependencies: + "@nodelib/fs.scandir" "2.1.4" + fastq "^1.6.0" + +"@tsconfig/svelte@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-1.0.10.tgz#30ec7feeee0bdf38b12a50f0686f8a2e7b6b9dc0" + integrity sha512-EBrpH2iXXfaf/9z81koiDYkp2mlwW2XzFcAqn6qh7VKyP8zBvHHAQzNhY+W9vH5arAjmGAm5g8ElWq6YmXm3ig== + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== -"@types/eslint-visitor-keys@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" - integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== +"@types/body-parser@*": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect-history-api-fallback@*": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.4.tgz#8c0f0e6e5d8252b699f5a662f51bdf82fd9d8bb8" + integrity sha512-Kf8v0wljR5GSCOCF/VQWdV3ZhKOVA73drXtY3geMTQgHy9dgqQ0dLrf31M0hcuWkhFzK5sP0kkS3mJzcKVtZbw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.34" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" + integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.0": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" + integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "7.2.10" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.10.tgz#4b7a9368d46c0f8cd5408c23288a59aa2394d917" + integrity sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.47": + version "0.0.47" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4" + integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": + version "4.17.19" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d" + integrity sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*": + version "4.17.11" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.11.tgz#debe3caa6f8e5fcda96b47bd54e2f40c4ee59545" + integrity sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" "@types/glob@^7.1.1": version "7.1.3" @@ -42,34 +165,75 @@ "@types/node" "*" "@types/google-protobuf@^3.7.3": - version "3.15.0" - resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.0.tgz#1df2507453a5da732b31846f0c8a64116aef21be" - integrity sha512-IshlR1QWD9LYQRlYMfF8dG6PqxrZHddlWB8O5+HCGwH1nv2JQ887fMwRRlXOmUEFehvg+k1THx2h7RbqPUPkGA== + version "3.15.2" + resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.2.tgz#70753e948cabeb416d71299dc35c3f562a10fb0f" + integrity sha512-ubeqvw7sl6CdgeiIilsXB2jIFoD/D0F+/LIEp7xEBEXRNtDJcf05FRINybsJtL7GlkWOUVn6gJs2W9OF+xI6lg== "@types/html-minifier-terser@^5.0.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50" integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== -"@types/jasmine@^3.5.10": - version "3.6.9" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.6.9.tgz#8785870f87839b7d91b45d7b226380f28dee5d9f" - integrity sha512-B53NIwMj/AO0O+xfSWLYmKB0Mo6TYxfv2Mk8/c1T2w/e38t55iaPR6p7pHXTTtqfTmevPK3i8T1YweYFTZlxDw== +"@types/http-proxy@^1.17.5": + version "1.17.6" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.6.tgz#62dc3fade227d6ac2862c8f19ee0da9da9fd8616" + integrity sha512-+qsjqR75S/ib0ig0R9WN+CDoZeOBU6F2XLewgC4KVgdXiNHiKKHFEMRHOrs5PbYE97D5vataw5wPj4KLYfUkuQ== + dependencies: + "@types/node" "*" -"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.6": +"@types/jasmine@^3.5.10": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.7.4.tgz#99a49aa9a5f8dc86fc249ed13ed59552c6ce862d" + integrity sha512-L3FKeEwMm8e8hqGvt7cSesVmGtavpRyHV1FNDq+Pm5pS4x5eFmE70ZERXCSBWAiLQqXBcZRUrwV59FZLQl/GxQ== + +"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + +"@types/mini-css-extract-plugin@^1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@types/mini-css-extract-plugin/-/mini-css-extract-plugin-1.4.3.tgz#4907ee3953fecd199fab24ef056dabef51ff19a2" + integrity sha512-jyOSVaF4ie2jUGr1uohqeyDrp7ktRthdFxDKzTgbPZtl0QI5geEopW7UKD/DEfn0XgV1KEq/RnZlUmnrEAWbmg== + dependencies: + "@types/node" "*" + tapable "^2.2.0" + webpack "^5" + "@types/minimatch@*": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== -"@types/node@*": - version "14.14.41" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615" - integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g== +"@types/node@*", "@types/node@^15.3.0": + version "15.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26" + integrity sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/pug@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.4.tgz#8772fcd0418e3cd2cc171555d73007415051f4b2" + integrity sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI= + +"@types/qs@*": + version "6.9.6" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" + integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== "@types/quill@^1.3.7": version "1.3.10" @@ -78,10 +242,30 @@ dependencies: parchment "^1.1.2" +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/sass@^1.16.0": + version "1.16.0" + resolved "https://registry.yarnpkg.com/@types/sass/-/sass-1.16.0.tgz#b41ac1c17fa68ffb57d43e2360486ef526b3d57d" + integrity sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA== + dependencies: + "@types/node" "*" + +"@types/serve-static@*": + version "1.13.9" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" + integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + "@types/simple-peer@^9.6.0": - version "9.6.2" - resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.6.2.tgz#a72afb6bd280fc4083d37e00247b30975304c071" - integrity sha512-FirllZ8397Qo8LfAgD4UuMgIOQmP1zdcP2eFNRDpaFDGQs42j2XxKOcTcvYZjXHv4HD7hgoNXzjB6iiEspKniw== + version "9.6.3" + resolved "https://registry.yarnpkg.com/@types/simple-peer/-/simple-peer-9.6.3.tgz#aa118a57e036f4ce2059a7e25367526a4764206d" + integrity sha512-zrXEBch9tF4NgkZDsGR3c1D0kq99M1bBCjzEyL0PVfEWzCIXrK64TuxRz3XKOx1B0KoEQ9kTs+AhMDuQaHy5RQ== dependencies: "@types/node" "*" @@ -95,7 +279,7 @@ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== -"@types/tapable@^1", "@types/tapable@^1.0.5": +"@types/tapable@^1": version "1.0.7" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.7.tgz#545158342f949e8fd3bfd813224971ecddc3fac4" integrity sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ== @@ -107,6 +291,17 @@ dependencies: source-map "^0.6.1" +"@types/webpack-dev-server@^3.11.4": + version "3.11.4" + resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.4.tgz#90d47dd660b696d409431ab8c1e9fa3615103a07" + integrity sha512-DCKORHjqNNVuMIDWFrlljftvc9CL0+09p3l7lBpb8dRqgN5SmvkWCY4MPKxoI6wJgdRqohmoNbptkxqSKAzLRg== + dependencies: + "@types/connect-history-api-fallback" "*" + "@types/express" "*" + "@types/serve-static" "*" + "@types/webpack" "^4" + http-proxy-middleware "^1.0.0" + "@types/webpack-sources@*": version "2.1.0" resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-2.1.0.tgz#8882b0bd62d1e0ce62f183d0d01b72e6e82e8c10" @@ -116,10 +311,10 @@ "@types/source-list-map" "*" source-map "^0.7.3" -"@types/webpack@^4.41.8": - version "4.41.27" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.27.tgz#f47da488c8037e7f1b2dbf2714fbbacb61ec0ffc" - integrity sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA== +"@types/webpack@^4": + version "4.41.28" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.28.tgz#0069a2159b7ad4d83d0b5801942c17d54133897b" + integrity sha512-Nn84RAiJjKRfPFFCVR8LC4ueTtTdfWAMZ03THIzZWRJB+rX24BD3LqPSFnbMscWauEsT4segAsylPDIaZyZyLQ== dependencies: "@types/anymatch" "*" "@types/node" "*" @@ -128,193 +323,213 @@ "@types/webpack-sources" "*" source-map "^0.6.0" -"@typescript-eslint/eslint-plugin@^2.26.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" - integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== - dependencies: - "@typescript-eslint/experimental-utils" "2.34.0" - functional-red-black-tree "^1.0.1" - regexpp "^3.0.0" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" - integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.34.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@^2.26.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" - integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== - dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.34.0" - "@typescript-eslint/typescript-estree" "2.34.0" - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/typescript-estree@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" - integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== +"@typescript-eslint/eslint-plugin@^4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz#29d3c9c81f6200b1fd6d8454cfb007ba176cde80" + integrity sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw== dependencies: + "@typescript-eslint/experimental-utils" "4.23.0" + "@typescript-eslint/scope-manager" "4.23.0" debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" + functional-red-black-tree "^1.0.1" lodash "^4.17.15" + regexpp "^3.0.0" semver "^7.3.2" tsutils "^3.17.1" -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== +"@typescript-eslint/experimental-utils@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz#f2059434cd6e5672bfeab2fb03b7c0a20622266f" + integrity sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA== dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/typescript-estree" "4.23.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== +"@typescript-eslint/parser@^4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.23.0.tgz#239315d38e42e852bef43a4b0b01bef78f78911c" + integrity sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug== dependencies: - "@webassemblyjs/wast-printer" "1.9.0" + "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/typescript-estree" "4.23.0" + debug "^4.1.1" -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== +"@typescript-eslint/scope-manager@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz#8792ef7eacac122e2ec8fa2d30a59b8d9a1f1ce4" + integrity sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w== dependencies: - "@webassemblyjs/ast" "1.9.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/visitor-keys" "4.23.0" -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== +"@typescript-eslint/types@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.23.0.tgz#da1654c8a5332f4d1645b2d9a1c64193cae3aa3b" + integrity sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw== -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== +"@typescript-eslint/typescript-estree@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz#0753b292097523852428a6f5a1aa8ccc1aae6cd9" + integrity sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/visitor-keys" "4.23.0" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + semver "^7.3.2" + tsutils "^3.17.1" -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== +"@typescript-eslint/visitor-keys@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz#7215cc977bd3b4ef22467b9023594e32f9e4e455" + integrity sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg== + dependencies: + "@typescript-eslint/types" "4.23.0" + eslint-visitor-keys "^2.0.0" + +"@webassemblyjs/ast@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f" + integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + +"@webassemblyjs/floating-point-hex-parser@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c" + integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA== + +"@webassemblyjs/helper-api-error@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4" + integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w== + +"@webassemblyjs/helper-buffer@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642" + integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA== + +"@webassemblyjs/helper-numbers@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9" + integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.0" + "@webassemblyjs/helper-api-error" "1.11.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1" + integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA== + +"@webassemblyjs/helper-wasm-section@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b" + integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + +"@webassemblyjs/ieee754@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf" + integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== +"@webassemblyjs/leb128@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b" + integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== +"@webassemblyjs/utf8@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf" + integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw== -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== +"@webassemblyjs/wasm-edit@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78" + integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/helper-wasm-section" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + "@webassemblyjs/wasm-opt" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + "@webassemblyjs/wast-printer" "1.11.0" -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== +"@webassemblyjs/wasm-gen@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe" + integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/ieee754" "1.11.0" + "@webassemblyjs/leb128" "1.11.0" + "@webassemblyjs/utf8" "1.11.0" -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== +"@webassemblyjs/wasm-opt@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978" + integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== +"@webassemblyjs/wasm-parser@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754" + integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-api-error" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/ieee754" "1.11.0" + "@webassemblyjs/leb128" "1.11.0" + "@webassemblyjs/utf8" "1.11.0" -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== +"@webassemblyjs/wast-printer@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e" + integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" + "@webassemblyjs/ast" "1.11.0" "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== +"@webpack-cli/configtest@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.3.tgz#204bcff87cda3ea4810881f7ea96e5f5321b87b9" + integrity sha512-WQs0ep98FXX2XBAfQpRbY0Ma6ADw8JR6xoIkaIiJIzClGOMqVRvPCWqndTxf28DgFopWan0EKtHtg/5W1h0Zkw== + +"@webpack-cli/info@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.2.4.tgz#7381fd41c9577b2d8f6c2594fad397ef49ad5573" + integrity sha512-ogE2T4+pLhTTPS/8MM3IjHn0IYplKM4HbVNMCWA9N4NrdPzunwenpCsqKEXyejMfRu6K8mhauIPYf8ZxWG5O6g== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - "@xtuc/long" "4.2.2" + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.4.0.tgz#f84fd07bcacefe56ce762925798871092f0f228e" + integrity sha512-xgT/HqJ+uLWGX+Mzufusl3cgjAcnqYYskaB7o0vRcwOEfuu6hMzSILQpnIzFMGsTaeaX4Nnekl+6fadLbl1/Vg== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -334,21 +549,21 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-jsx@^5.2.0: +acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - -acorn@^7.1.1: +acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.2.1: + version "8.2.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0" + integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -364,7 +579,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -374,17 +589,25 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.4.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.4.0.tgz#48984fdb2ce225cab15795f0772a8d85669075e4" + integrity sha512-7QD2l6+KBSLwf+7MuYocbWvRPdOu63/trReTLu2KFwkgctnub1auoF+Y1WYcm09CTM7quuscrzqmASaLHC/K4Q== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-html@0.0.7: version "0.0.7" @@ -413,7 +636,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -436,11 +659,6 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -468,6 +686,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -485,6 +708,11 @@ array-union@^1.0.1: dependencies: array-uniq "^1.0.1" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -510,23 +738,25 @@ asn1.js@^5.2.0: minimalistic-assert "^1.0.0" safer-buffer "^2.1.0" -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== +assert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" + integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== dependencies: - object-assign "^4.1.1" - util "0.10.3" + es6-object-assign "^1.1.0" + is-nan "^1.2.1" + object-is "^1.0.1" + util "^0.12.0" assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-each@^1.0.1: version "1.0.3" @@ -545,11 +775,31 @@ async@^2.6.2: dependencies: lodash "^4.17.14" +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +automation-events@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/automation-events/-/automation-events-4.0.1.tgz#93acef8a457cbea65f16fdcef8f349fd2fafe298" + integrity sha512-8bQx+PVtNDMD5F2H40cQs7oexZve3Z0xC9fWRQT4fltF65f/WsSpjM4jpulL4K4yLLB71oi4/xVJJCJ5I/Kjbw== + dependencies: + "@babel/runtime" "^7.14.0" + tslib "^2.2.0" + +available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + axios@^0.21.1: version "0.21.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" @@ -572,7 +822,7 @@ base64-arraybuffer@0.1.4: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= -base64-js@^1.0.2, base64-js@^1.3.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -622,11 +872,6 @@ blob@0.0.5: resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== -bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" @@ -767,6 +1012,17 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" +browserslist@^4.14.5: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== + dependencies: + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" + escalade "^3.1.1" + node-releases "^1.1.71" + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -782,15 +1038,6 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -814,27 +1061,6 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@^12.0.2: - version "12.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -881,7 +1107,12 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.2: +caniuse-lite@^1.0.30001219: + version "1.0.30001228" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa" + integrity sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A== + +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -890,25 +1121,15 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -charenc@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= - -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1: +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2: version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -942,11 +1163,6 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -977,18 +1193,6 @@ clean-css@^4.2.3: dependencies: source-map "~0.6.0" -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -998,6 +1202,15 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + clone@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" @@ -1035,7 +1248,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.2.2: +colorette@^1.2.1, colorette@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== @@ -1050,10 +1263,10 @@ commander@^4.1.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== component-bind@1.0.0: version "1.0.0" @@ -1095,22 +1308,12 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - connect-history-api-fallback@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== -console-browserify@^1.1.0: +console-browserify@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== @@ -1142,18 +1345,6 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -1164,6 +1355,17 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -1195,7 +1397,12 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -1206,12 +1413,16 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -crypt@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" -crypto-browserify@^3.11.0: +crypto-browserify@^3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== @@ -1228,10 +1439,10 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -css-loader@^5.1.3: - version "5.2.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.2.tgz#65f2c1482255f15847ecad6cbc515cae8a5b234e" - integrity sha512-IS722y7Lh2Yq+acMR74tdf3faMOLRP2RfLwS0VzSS7T98IHtacMWJLku3A0OBTFHB07zAa4nWBhA8gfxwQVWGQ== +css-loader@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.4.tgz#e985dcbce339812cb6104ef3670f08f9893a1536" + integrity sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw== dependencies: camelcase "^6.2.0" icss-utils "^5.1.0" @@ -1265,11 +1476,6 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cyclist@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" - integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= - debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1320,11 +1526,16 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -1333,7 +1544,7 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" -define-properties@^1.1.2, define-properties@^1.1.3: +define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -1393,15 +1604,15 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= +detect-indent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" + integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== detect-node@^2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79" - integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== diff@^4.0.1: version "4.0.2" @@ -1417,15 +1628,22 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + version "1.3.4" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" + integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== dependencies: ip "^1.1.0" safe-buffer "^5.0.1" @@ -1459,10 +1677,10 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== +domain-browser@^4.19.0: + version "4.19.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1" + integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ== domelementtype@1, domelementtype@^1.3.1: version "1.3.1" @@ -1497,21 +1715,16 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +electron-to-chromium@^1.3.723: + version "1.3.728" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.728.tgz#dbedd6373f595ae10a13d146b66bece4c1afa5bd" + integrity sha512-SHv4ziXruBpb1Nz4aTuqEHBYi/9GNCJMYIJgDEXrp/2V01nFXMNFUTli5Z85f5ivSkioLilQatqBYFB44wNJrA== + elliptic@^6.5.3: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -1545,7 +1758,7 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -1553,9 +1766,9 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: once "^1.4.0" engine.io-client@~3.5.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.5.1.tgz#b500458a39c0cd197a921e0e759721a746d0bdb9" - integrity sha512-oVu9kBkGbcggulyVF0kz6BV3ganqUeqXvD79WOFKa+11oK692w1NyFkuEj4xrkFRpZhn92QOqTk4RQq5LiBXbQ== + version "3.5.2" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.5.2.tgz#0ef473621294004e9ceebe73cef0af9e36f2f5fa" + integrity sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA== dependencies: component-emitter "~1.3.0" component-inherit "0.0.3" @@ -1566,7 +1779,7 @@ engine.io-client@~3.5.0: parseqs "0.0.6" parseuri "0.0.6" ws "~7.4.2" - xmlhttprequest-ssl "~1.5.4" + xmlhttprequest-ssl "~1.6.2" yeast "0.1.2" engine.io-parser@~2.2.0: @@ -1580,14 +1793,20 @@ engine.io-parser@~2.2.0: blob "0.0.5" has-binary2 "~1.0.2" -enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.1, enhanced-resolve@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" - integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== +enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.0: + version "5.8.2" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b" + integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA== dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" entities@^1.1.1: version "1.1.2" @@ -1599,19 +1818,31 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +envinfo@^7.7.3: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + err-code@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/err-code/-/err-code-3.0.1.tgz#a444c7b992705f2b120ee320b09972eef331c920" integrity sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA== -errno@^0.1.3, errno@~0.1.7: +errno@^0.1.3: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== dependencies: prr "~1.0.1" -es-abstract@^1.18.0-next.2: +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: version "1.18.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== @@ -1633,6 +1864,11 @@ es-abstract@^1.18.0-next.2: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.0" +es-module-lexer@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e" + integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA== + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -1642,6 +1878,16 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -1652,15 +1898,7 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^5.0.0: +eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -1668,90 +1906,88 @@ eslint-scope@^5.0.0: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-utils@^2.0.0: +eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" -eslint-visitor-keys@^1.1.0: +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint@^7.26.0: + version "7.26.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.26.0.tgz#d416fdcdcb3236cd8f282065312813f8c13982f6" + integrity sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg== dependencies: - "@babel/code-frame" "^7.0.0" + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.1" ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" + chalk "^4.0.0" + cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.3" - eslint-visitor-keys "^1.1.0" - espree "^6.1.2" - esquery "^1.0.1" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^12.1.0" + globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^7.0.0" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" + levn "^0.4.1" + lodash "^4.17.21" minimatch "^3.0.4" - mkdirp "^0.5.1" natural-compare "^1.4.0" - optionator "^0.8.3" + optionator "^0.9.1" progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.4" text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" - integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: - acorn "^7.1.1" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.1.0" + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1: +esquery@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0, esrecurse@^4.3.0: +esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== @@ -1793,7 +2029,7 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.0.0: +events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -1826,6 +2062,21 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -1839,13 +2090,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -1902,15 +2146,6 @@ extend@^3.0.1, extend@^3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -1935,16 +2170,40 @@ fast-diff@1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" integrity sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig== +fast-glob@^3.1.1: + version "3.2.5" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" + integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + +fastq@^1.6.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" + integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== + dependencies: + reusify "^1.0.4" + faye-websocket@^0.11.3: version "0.11.3" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" @@ -1952,24 +2211,12 @@ faye-websocket@^0.11.3: dependencies: websocket-driver ">=0.5.1" -figgy-pudding@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" - integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== - dependencies: - flat-cache "^2.0.1" + flat-cache "^3.0.4" file-uri-to-path@1.0.0: version "1.0.0" @@ -1993,6 +2240,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +filter-obj@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-2.0.2.tgz#fff662368e505d69826abb113f0f6a98f56e9d5f" + integrity sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg== + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -2006,15 +2258,6 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -2022,48 +2265,61 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" + locate-path "^5.0.0" + path-exists "^4.0.0" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== follow-redirects@^1.0.0, follow-redirects@^1.10.0: - version "1.13.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" - integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== + version "1.14.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" + integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +fork-ts-checker-webpack-plugin@^6.2.9: + version "6.2.9" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.2.9.tgz#08f51b685a48b09ab3ec079a8501762422443120" + integrity sha512-D/KSb/2VeiOy3odDerrC16WiZ1t5TLwiFfZmuDeTXcf3Km79M+f8nTCIdKkokxybybrgMcStbx0QpGaseePxnA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -2081,23 +2337,20 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" +fs-monkey@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== fs.realpath@^1.0.0: version "1.0.0" @@ -2158,6 +2411,11 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2171,17 +2429,22 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@~5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.3, glob@^7.1.3, glob@^7.1.6: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -2190,42 +2453,6 @@ glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - globals@^12.1.0: version "12.4.0" resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" @@ -2233,6 +2460,25 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^13.6.0: + version "13.8.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" + integrity sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q== + dependencies: + type-fest "^0.20.2" + +globby@^11.0.1: + version "11.0.3" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" + integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -2245,11 +2491,11 @@ globby@^6.1.0: pinkie-promise "^2.0.0" google-protobuf@^3.13.0: - version "3.15.8" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.15.8.tgz#5f3948905e4951c867d6bc143f385a80e2a39efe" - integrity sha512-2jtfdqTaSxk0cuBJBtTTWsot4WtR9RVr2rXg7x7OoqiuOKopPrwXpM1G4dXIkLcUNRh3RKzz76C8IOkksZSeOw== + version "3.17.0" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.17.0.tgz#5623215f9d0345649720ae5e5e9713491e2456fc" + integrity sha512-xuxzzx6FXmkmddRZci2SmGeZcx+vykmqG60yJf/Rz+NEKaUs+nLhaiUQAWa7Z00a7bHfRjRxSIX9FcELaxWIVA== -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -2360,13 +2606,6 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -2395,20 +2634,16 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" -html-webpack-plugin@^4.3.0: - version "4.5.2" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12" - integrity sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A== +html-webpack-plugin@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz#8797327548e3de438e3494e0c6d06f181a7f20d1" + integrity sha512-rZsVvPXUYFyME0cuGkyOHfx9hmkFa4pWfxY/mdY38PsBEaVNsRoA+Id+8z6DBDgyv3zaw6XQszdF8HLwfQvcdQ== dependencies: "@types/html-minifier-terser" "^5.0.0" - "@types/tapable" "^1.0.5" - "@types/webpack" "^4.41.8" html-minifier-terser "^5.0.1" - loader-utils "^1.2.3" lodash "^4.17.20" pretty-error "^2.1.1" - tapable "^1.1.3" - util.promisify "1.0.0" + tapable "^2.0.0" htmlparser2@^3.10.1: version "3.10.1" @@ -2474,7 +2709,18 @@ http-proxy-middleware@0.19.1: lodash "^4.17.11" micromatch "^3.1.10" -http-proxy@^1.17.0: +http-proxy-middleware@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665" + integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg== + dependencies: + "@types/http-proxy" "^1.17.5" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.17.0, http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== @@ -2488,7 +2734,12 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -iconv-lite@0.4.24, iconv-lite@^0.4.24: +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -2500,22 +2751,22 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ieee754@^1.1.4, ieee754@^1.2.1: +ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -import-fresh@^3.0.0: +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -2531,26 +2782,24 @@ import-local@^2.0.0: pkg-dir "^3.0.0" resolve-cwd "^2.0.0" +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= -infer-owner@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2559,45 +2808,16 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4, ini@^1.3.5: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -inquirer@^7.0.0: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -2606,10 +2826,10 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -interpret@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== ip-regex@^2.1.0: version "2.1.0" @@ -2652,10 +2872,15 @@ is-arguments@^1.0.4: dependencies: call-bind "^1.0.0" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + is-bigint@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" - integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== is-binary-path@^1.0.0: version "1.0.1" @@ -2672,13 +2897,13 @@ is-binary-path@~2.1.0: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" - integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" -is-buffer@^1.1.5, is-buffer@~1.1.6: +is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -2688,6 +2913,13 @@ is-callable@^1.1.4, is-callable@^1.2.3: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== +is-core-module@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -2703,9 +2935,9 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== is-descriptor@^0.1.0: version "0.1.6" @@ -2752,6 +2984,11 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" + integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -2766,15 +3003,23 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-nan@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-number-object@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== is-number@^3.0.0: version "3.0.0" @@ -2807,6 +3052,11 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -2815,31 +3065,47 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: isobject "^3.0.1" is-regex@^1.0.4, is-regex@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" - integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== dependencies: call-bind "^1.0.2" - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" has-symbols "^1.0.1" -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -2849,7 +3115,7 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -2889,6 +3155,15 @@ jasmine@^3.5.0: glob "^7.1.6" jasmine-core "~3.7.0" +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -2907,11 +3182,21 @@ json-parse-better-errors@^1.0.2: resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -2936,6 +3221,15 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -2970,32 +3264,28 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= linked-list-typescript@^1.0.11: version "1.0.15" resolved "https://registry.yarnpkg.com/linked-list-typescript/-/linked-list-typescript-1.0.15.tgz#faeed93cf9203f102e2158c29edcddda320abe82" integrity sha512-RIyUu9lnJIyIaMe63O7/aFv/T2v3KsMFuXMBbUQCHX+cgtGro86ETDj5ed0a8gQL2+DFjzYYsgVG4I36/cUwgw== -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@^1.0.2, loader-utils@^1.2.3, loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" +loader-runner@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" + integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== loader-utils@^2.0.0: version "2.0.0" @@ -3014,7 +3304,24 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3024,10 +3331,10 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== -lokijs@^1.5.11: - version "1.5.11" - resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.11.tgz#2b2ea82ec66050e4b112c6cfc588dac22d362b13" - integrity sha512-YYyuBPxMn/oS0tFznQDbIX5XL1ltMcwFqCboDr8voYE4VCDzR5vAsrvQDhlnua4lBeqMqHmLvUXRTmRUzUKH1Q== +lokijs@^1.5.12: + version "1.5.12" + resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.12.tgz#cb55b37009bdf09ee7952a6adddd555b893653a0" + integrity sha512-Q5ALD6JiS6xAUWCwX3taQmgwxyveCtIIuL08+ml0nHwT3k0S/GIFJN+Hd38b1qYIMaE5X++iqsqWVksz7SYW+Q== lower-case@^2.0.2: version "2.0.2" @@ -3036,13 +3343,6 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3050,14 +3350,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -make-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -3084,20 +3376,18 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -md5@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" - integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== - dependencies: - charenc "0.0.2" - crypt "0.0.2" - is-buffer "~1.1.6" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +memfs@^3.1.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.2.2.tgz#5de461389d596e3f23d48bb7c2afb6161f4df40e" + integrity sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q== + dependencies: + fs-monkey "1.0.3" + memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -3106,25 +3396,27 @@ memory-fs@^0.4.1: errno "^0.1.3" readable-stream "^2.0.1" -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -3143,7 +3435,7 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.0: +micromatch@^4.0.0, micromatch@^4.0.2: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== @@ -3164,7 +3456,7 @@ mime-db@1.47.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== -mime-types@~2.1.17, mime-types@~2.1.24: +mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24: version "2.1.30" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== @@ -3186,10 +3478,15 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mini-css-extract-plugin@^1.3.9: - version "1.5.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.5.0.tgz#69bee3b273d2d4ee8649a2eb409514b7df744a27" - integrity sha512-SIbuLMv6jsk1FnLIU5OUG/+VMGUprEjM1+o2trOAx8i5KOKMrhyezb1dJ4Ugsykb8Jgq8/w5NEopy6escV9G7g== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +mini-css-extract-plugin@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.0.tgz#b4db2525af2624899ed64a23b0016e0036411893" + integrity sha512-nPFKI7NSy6uONUo9yn2hIfb9vyYvkFu95qki0e21DQ9uaqNKDP15DGpK0KnV6wDroWxPHtExrdEwx/yDQ8nVRw== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" @@ -3217,22 +3514,6 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -3241,25 +3522,13 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: +mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -3293,20 +3562,15 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - nan@^2.12.1: version "2.14.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== -nanoid@^3.1.22: - version "3.1.22" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" - integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== +nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== nanomatch@^1.2.9: version "1.2.13" @@ -3335,7 +3599,7 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.5.0, neo-async@^2.6.1, neo-async@^2.6.2: +neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -3358,34 +3622,40 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== +node-polyfill-webpack-plugin@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-1.1.2.tgz#3e8e8640a920b566d5842df84f1c8c077554250e" + integrity sha512-ubwf7M73V13RDlKaDmMh1+giB/D8KL75umXEGabGtxSt/WRCirl01urhK1qsr6Cdt77escQ9SNb5OoTR8IwEHg== dependencies: - assert "^1.1.1" + assert "^2.0.0" browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" + buffer "^6.0.3" + console-browserify "^1.2.0" constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" + crypto-browserify "^3.12.0" + domain-browser "^4.19.0" + events "^3.3.0" + filter-obj "^2.0.2" https-browserify "^1.0.0" os-browserify "^0.3.0" - path-browserify "0.0.1" + path-browserify "^1.0.1" process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" + punycode "^2.1.1" + querystring-es3 "^0.2.1" + readable-stream "^3.6.0" + stream-browserify "^3.0.0" + stream-http "^3.2.0" + string_decoder "^1.3.0" + timers-browserify "^2.0.12" + tty-browserify "^0.0.1" url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" + util "^0.12.3" + vm-browserify "^1.1.2" + +node-releases@^1.1.71: + version "1.1.72" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" + integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== normalize-path@^2.1.1: version "2.1.1" @@ -3406,6 +3676,13 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -3413,7 +3690,7 @@ nth-check@^1.0.2: dependencies: boolbase "~1.0.0" -object-assign@^4.0.1, object-assign@^4.1.1: +object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -3428,9 +3705,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.9.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.2.tgz#b6385a3e2b7cae0b5eafcf90cddf85d128767f30" - integrity sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA== + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== object-is@^1.0.1: version "1.1.5" @@ -3462,15 +3739,6 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" -object.getownpropertydescriptors@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" - integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -3502,7 +3770,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -3516,17 +3784,17 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" original@^1.0.0: version "1.0.2" @@ -3540,23 +3808,25 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-limit@^2.0.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" +p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -3564,6 +3834,13 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -3591,15 +3868,6 @@ papaparse@^5.3.0: resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.0.tgz#ab1702feb96e79ab4309652f36db9536563ad05a" integrity sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg== -parallel-transform@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" - integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - dependencies: - cyclist "^1.0.1" - inherits "^2.0.3" - readable-stream "^2.1.5" - param-case@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -3631,10 +3899,15 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" parseqs@0.0.6: version "0.0.6" @@ -3664,10 +3937,10 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== path-dirname@^1.0.0: version "1.0.2" @@ -3679,6 +3952,11 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -3694,11 +3972,26 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + path@^0.12.7: version "0.12.7" resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" @@ -3719,12 +4012,12 @@ pbkdf2@^3.0.3: sha.js "^2.4.8" phaser3-rex-plugins@^1.1.42: - version "1.1.45" - resolved "https://registry.yarnpkg.com/phaser3-rex-plugins/-/phaser3-rex-plugins-1.1.45.tgz#9c617b4d0f17e7b61b2292d158fa93ba4c7c5e55" - integrity sha512-1wRHhmyw9HCPG6T3CYUR1Qoo73WWizMYxpW5xbjm3NQFgXTqeuqUgyqExLrglrxLqiuhXxSVTnbhWH61XDP5Xg== + version "1.1.47" + resolved "https://registry.yarnpkg.com/phaser3-rex-plugins/-/phaser3-rex-plugins-1.1.47.tgz#89299369437a0032ad31c64e89a26d20fd8a5867" + integrity sha512-0xAoQ8aVayC3cu0+O7EIBWTgHNDIIA8ro+gbNre8eAqz+ljlf178ucT5FVuwPzsE6WpuU1VluaAN74WcDDtM1Q== dependencies: eventemitter3 "^3.1.2" - lokijs "^1.5.11" + lokijs "^1.5.12" papaparse "^5.3.0" webfontloader "^1.6.28" @@ -3770,6 +4063,13 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + portfinder@^1.0.26: version "1.0.28" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" @@ -3813,13 +4113,11 @@ postcss-modules-values@^4.0.0: icss-utils "^5.0.0" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" - integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== + version "6.0.6" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" + integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== dependencies: cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" util-deprecate "^1.0.2" postcss-value-parser@^4.1.0: @@ -3828,18 +4126,18 @@ postcss-value-parser@^4.1.0: integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== postcss@^8.2.10: - version "8.2.10" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.10.tgz#ca7a042aa8aff494b334d0ff3e9e77079f6f702b" - integrity sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw== + version "8.2.15" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65" + integrity sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q== dependencies: colorette "^1.2.2" - nanoid "^3.1.22" + nanoid "^3.1.23" source-map "^0.6.1" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== pretty-error@^2.1.1: version "2.1.2" @@ -3864,11 +4162,6 @@ progress@^2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -3894,14 +4187,6 @@ public-encrypt@^4.0.0: randombytes "^2.0.1" safe-buffer "^5.1.2" -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -3910,26 +4195,12 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== @@ -3939,7 +4210,7 @@ qs@6.7.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -querystring-es3@^0.2.0: +querystring-es3@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= @@ -3954,7 +4225,7 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== -queue-microtask@^1.2.3: +queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== @@ -4017,7 +4288,7 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.1, readable-stream@^2.0.2: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -4030,7 +4301,7 @@ raw-body@2.4.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0: +readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -4055,6 +4326,18 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +rechoir@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca" + integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q== + dependencies: + resolve "^1.9.0" + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -4071,12 +4354,7 @@ regexp.prototype.flags@^1.2.0: call-bind "^1.0.2" define-properties "^1.1.3" -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - -regexpp@^3.0.0: +regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== @@ -4117,6 +4395,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -4134,13 +4417,12 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" + resolve-from "^5.0.0" resolve-from@^3.0.0: version "3.0.0" @@ -4152,18 +4434,23 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== +resolve@^1.9.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" + is-core-module "^2.2.0" + path-parse "^1.0.6" ret@~0.1.10: version "0.1.15" @@ -4175,20 +4462,25 @@ retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.5.4, rimraf@^2.6.3: +rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -4197,19 +4489,14 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: - aproba "^1.1.1" + queue-microtask "^1.2.2" -rxjs@^6.6.0, rxjs@^6.6.3: +rxjs@^6.6.3: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -4238,24 +4525,30 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@10.1.1: - version "10.1.1" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d" - integrity sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw== +sass-loader@^11.1.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-11.1.1.tgz#0db441bbbe197b2af96125bebb7f4be6476b13a7" + integrity sha512-fOCp/zLmj1V1WHDZbUbPgrZhA7HKXHEqkslzB+05U5K9SbSbcmH91C7QLW31AsXikxUMaxXRhhcqWZAxUMLDyA== dependencies: klona "^2.0.4" - loader-utils "^2.0.0" neo-async "^2.6.2" - schema-utils "^3.0.0" - semver "^7.3.2" -sass@^1.32.8: - version "1.32.10" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.10.tgz#d40da4e20031b450359ee1c7e69bc8cc89569241" - integrity sha512-Nx0pcWoonAkn7CRp0aE/hket1UP97GiR1IFw3kcjV3pnenhWgZEWUf0ZcfPOV2fK52fnOcK3JdC/YYZ9E47DTQ== +sass@^1.32.12: + version "1.32.13" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.13.tgz#8d29c849e625a415bce71609c7cf95e15f74ed00" + integrity sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA== dependencies: chokidar ">=3.0.0 <4.0.0" +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -4280,23 +4573,23 @@ select-hose@^2.0.0: integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= selfsigned@^1.10.8: - version "1.10.8" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" - integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== + version "1.10.11" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.11.tgz#24929cd906fe0f44b6d01fb23999a739537acbe9" + integrity sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA== dependencies: node-forge "^0.10.0" -semver@^5.5.0, semver@^5.6.0: +semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.2, semver@^6.3.0: +semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2, semver@^7.3.5: +semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -4322,10 +4615,10 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== dependencies: randombytes "^2.1.0" @@ -4390,6 +4683,13 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -4397,12 +4697,24 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -signal-exit@^3.0.0, signal-exit@^3.0.2: +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.0, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -4420,14 +4732,19 @@ simple-peer@^9.6.2: randombytes "^2.1.0" readable-stream "^3.6.0" -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" snapdragon-node@^2.0.1: version "2.1.1" @@ -4506,7 +4823,7 @@ sockjs@^0.3.21: uuid "^3.4.0" websocket-driver "^0.7.4" -source-list-map@^2.0.0: +source-list-map@^2.0.0, source-list-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== @@ -4522,7 +4839,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.17, source-map-support@~0.5.12: +source-map-support@^0.5.17, source-map-support@~0.5.12, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -4545,7 +4862,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: +source-map@^0.7.3, source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -4585,12 +4902,14 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -ssri@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" - integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== +standardized-audio-context@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/standardized-audio-context/-/standardized-audio-context-25.2.4.tgz#d64dbdd70615171ec90d1b7151a0d945af94cf3d" + integrity sha512-uQKZXRnXrPVO1V6SwZ7PiV3RkQqRY3n7i6Q8nbTXYvoz8NftRNzfOIlwefpuC8LVLUUs9dhbKTpP+WOA82dkBw== dependencies: - figgy-pudding "^3.5.1" + "@babel/runtime" "^7.14.0" + automation-events "^4.0.1" + tslib "^2.2.0" static-extend@^0.1.1: version "0.1.2" @@ -4605,37 +4924,23 @@ static-extend@^0.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" + inherits "~2.0.4" + readable-stream "^3.5.0" -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== +stream-http@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" + integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A== dependencies: builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + inherits "^2.0.4" + readable-stream "^3.6.0" + xtend "^4.0.2" string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" @@ -4646,7 +4951,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0: +string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== @@ -4671,7 +4976,7 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@^1.0.0, string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -4706,12 +5011,29 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= -strip-json-comments@^3.0.1: +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -4730,44 +5052,82 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" +svelte-dev-helper@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz#7d187db5c6cdbbd64d75a32f91b8998bde3273c3" + integrity sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M= -tapable@^1.0.0, tapable@^1.1.3: +svelte-hmr@^0.12.3: + version "0.12.9" + resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.12.9.tgz#e323b71062a59bf4b73d2d5a7cc80dba256217e1" + integrity sha512-SGE7Odznj4dqZtUVIWcoPCvZ9gHImxVIIjrz+O3DDSi0j4OaSLim6MRF4UdhlBKeW3glSRc+tXNSKYvM5x+Dyw== + +svelte-loader@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/svelte-loader/-/svelte-loader-3.1.1.tgz#cb03541214b164edee58e4c98d4303f76df9013d" + integrity sha512-JU7qo1yBdDeJ3Aw23WoGKXrVaSn3KeWkM9KMl43HuMVFrsPNHIzZHTsf1ARJX/Abm3jeqNjigaO35RVbixhmWg== + dependencies: + loader-utils "^2.0.0" + svelte-dev-helper "^1.1.9" + svelte-hmr "^0.12.3" + +svelte-preprocess@^4.7.3: + version "4.7.3" + resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-4.7.3.tgz#454fa059c2400b15e7a3caeca18993cff9df0e96" + integrity sha512-Zx1/xLeGOIBlZMGPRCaXtlMe4ZA0faato5Dc3CosEqwu75MIEPuOstdkH6cy+RYTUYynoxzNaDxkPX4DbrPwRA== + dependencies: + "@types/pug" "^2.0.4" + "@types/sass" "^1.16.0" + detect-indent "^6.0.0" + strip-indent "^3.0.0" + +svelte@^3.38.2: + version "3.38.2" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5" + integrity sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg== + +table@^6.0.4: + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== + dependencies: + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + +tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== -terser@^4.1.2, terser@^4.6.3: +terser-webpack-plugin@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz#51d295eb7cc56785a67a372575fdc46e42d5c20c" + integrity sha512-6QhDaAiVHIQr5Ab3XUWZyDmrIPCHMiqJVljMF91YKyqwKkL5QHnYMkrMBy96v9Z7ev1hGhSEw1HQZc2p/s5Z8Q== + dependencies: + jest-worker "^26.6.2" + p-limit "^3.1.0" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + source-map "^0.6.1" + terser "^5.7.0" + +terser@^4.6.3: version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== @@ -4776,53 +5136,37 @@ terser@^4.1.2, terser@^4.6.3: source-map "~0.6.1" source-map-support "~0.5.12" +terser@^5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.0.tgz#a761eeec206bc87b605ab13029876ead938ae693" + integrity sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== -timers-browserify@^2.0.4: +timers-browserify@^2.0.12: version "2.0.12" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== dependencies: setimmediate "^1.0.4" -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - 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" @@ -4860,34 +5204,44 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -ts-loader@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.2.tgz#dffa3879b01a1a1e0a4b85e2b8421dc0dfff1c58" - integrity sha512-HDo5kXZCBml3EUPcc7RlZOV/JGlLHwppTLEHb3SHnr5V7NXD4klMEkrhJe5wgRbaWsSXi+Y1SIBN/K9B6zWGWQ== +ts-loader@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.1.2.tgz#ba9b9abb05a514e8ff825791a3f6fcf793272728" + integrity sha512-ryMgATvLLl+z8zQvdlm6Pep0slmwxFWIEnq/5VdiLVjqQXnFJgO+qNLGIIP+d2N2jsFZ9MibZCVDb2bSp7OmEA== dependencies: - chalk "^2.3.0" - enhanced-resolve "^4.0.0" - loader-utils "^1.0.2" + chalk "^4.1.0" + enhanced-resolve "^5.0.0" micromatch "^4.0.0" - semver "^6.0.0" + semver "^7.3.4" -ts-node@^8.10.2: - version "8.10.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" - integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== +ts-node@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== dependencies: arg "^4.1.0" + create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" source-map-support "^0.5.17" yn "3.1.1" +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3: +tslib@^2.0.3, tslib@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== @@ -4899,22 +5253,22 @@ tsutils@^3.17.1: dependencies: tslib "^1.8.1" -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= +tty-browserify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: - prelude-ls "~1.1.2" + prelude-ls "^1.2.1" -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.8.1: version "0.8.1" @@ -4929,15 +5283,10 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -typescript@^3.8.3: - version "3.9.9" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674" - integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w== +typescript@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" + integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== unbox-primitive@^1.0.0: version "1.0.1" @@ -4959,24 +5308,10 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" @@ -5034,21 +5369,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util.promisify@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - util@^0.10.3: version "0.10.4" resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" @@ -5056,12 +5376,17 @@ util@^0.10.3: dependencies: inherits "2.0.3" -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== +util@^0.12.0, util@^0.12.3: + version "0.12.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" + integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== dependencies: - inherits "2.0.3" + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" utila@~0.4: version "0.4.0" @@ -5078,7 +5403,7 @@ uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1: +v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== @@ -5088,28 +5413,18 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -vm-browserify@^1.0.1: +vm-browserify@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== +watchpack@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" + integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== dependencies: + glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" @@ -5123,22 +5438,24 @@ webfontloader@^1.6.28: resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae" integrity sha1-23hhKSU8tujq5UwvsF+HCvZnW64= -webpack-cli@^3.3.11: - version "3.3.12" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" - integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== +webpack-cli@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.7.0.tgz#3195a777f1f802ecda732f6c95d24c0004bc5a35" + integrity sha512-7bKr9182/sGfjFm+xdZSwgQuFjgEcy0iCTIBxRUeteJ2Kr8/Wz0qNJX+jw60LU36jApt4nmMkep6+W5AKhok6g== dependencies: - chalk "^2.4.2" - cross-spawn "^6.0.5" - enhanced-resolve "^4.1.1" - findup-sync "^3.0.0" - global-modules "^2.0.0" - import-local "^2.0.0" - interpret "^1.4.0" - loader-utils "^1.4.0" - supports-color "^6.1.0" - v8-compile-cache "^2.1.1" - yargs "^13.3.2" + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.0.3" + "@webpack-cli/info" "^1.2.4" + "@webpack-cli/serve" "^1.4.0" + colorette "^1.2.1" + commander "^7.0.0" + execa "^5.0.0" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + v8-compile-cache "^2.2.0" + webpack-merge "^5.7.3" webpack-dev-middleware@^3.7.2: version "3.7.3" @@ -5151,7 +5468,7 @@ webpack-dev-middleware@^3.7.2: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@^3.10.3: +webpack-dev-server@^3.11.2: version "3.11.2" resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#695ebced76a4929f0d5de7fd73fafe185fe33708" integrity sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ== @@ -5198,22 +5515,15 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-merge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" - integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== +webpack-merge@^5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.7.3.tgz#2a0754e1877a25a8bbab3d2475ca70a052708213" + integrity sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA== dependencies: - lodash "^4.17.15" + clone-deep "^4.0.1" + wildcard "^2.0.0" -webpack-require-http@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/webpack-require-http/-/webpack-require-http-0.4.3.tgz#5690d8cc57246a53a81f1ccffd20d0394d70261c" - integrity sha1-VpDYzFckalOoHxzP/SDQOU1wJhw= - dependencies: - md5 "^2.0.0" - url "^0.11.0" - -webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: +webpack-sources@^1.1.0: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -5221,34 +5531,42 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.42.1: - version "4.46.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" - integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== +webpack-sources@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" + integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack@^5, webpack@^5.37.0: + version "5.37.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.37.0.tgz#2ab00f613faf494504eb2beef278dab7493cc39d" + integrity sha512-yvdhgcI6QkQkDe1hINBAJ1UNevqNGTVaCkD2SSJcB8rcrNNl922RI8i2DXUAuNfANoxwsiXXEA4ZPZI9q2oGLA== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.47" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/wasm-edit" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + acorn "^8.2.1" + browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.5.0" - eslint-scope "^4.0.3" + enhanced-resolve "^5.8.0" + es-module-lexer "^0.4.0" + eslint-scope "^5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.4" json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.7.4" - webpack-sources "^1.4.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.0.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.1" + watchpack "^2.0.0" + webpack-sources "^2.1.1" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" @@ -5280,25 +5598,43 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.14, which@^1.2.9, which@^1.3.1: +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + +which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -word-wrap@~1.2.3: +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -5313,17 +5649,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - ws@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + version "6.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" @@ -5332,12 +5661,12 @@ ws@~7.4.2: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== -xmlhttprequest-ssl@~1.5.4: - version "1.5.5" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" - integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= +xmlhttprequest-ssl@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6" + integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q== -xtend@^4.0.0, xtend@~4.0.1: +xtend@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -5347,16 +5676,16 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^1.7.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -5390,3 +5719,8 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/maps/Tuto/scriptTuto.js b/maps/Tuto/scriptTuto.js index 869ec447..8821134b 100644 --- a/maps/Tuto/scriptTuto.js +++ b/maps/Tuto/scriptTuto.js @@ -5,6 +5,12 @@ var targetObjectTutoBubble ='Tutobubble'; var targetObjectTutoChat ='tutoChat'; var targetObjectTutoExplanation ='tutoExplanation'; var popUpExplanation = undefined; +var enterSoundUrl = "webrtc-in.mp3"; +var exitSoundUrl = "webrtc-out.mp3"; +var soundConfig = { + volume : 0.2, + loop : false +} function launchTuto (){ WA.openPopup(targetObjectTutoBubble, textFirstPopup, [ { @@ -26,6 +32,7 @@ function launchTuto (){ className : "success",callback:(popup2 => { popup2.close(); WA.restorePlayerControl(); + WA.loadSound(winSoundUrl).play(soundConfig); }) } ]) @@ -43,6 +50,7 @@ function launchTuto (){ WA.onEnterZone('popupZone', () => { WA.displayBubble(); + WA.loadSound(enterSoundUrl).play(soundConfig); if (!isFirstTimeTuto) { isFirstTimeTuto = true; launchTuto(); @@ -71,4 +79,5 @@ WA.onEnterZone('popupZone', () => { WA.onLeaveZone('popupZone', () => { if (popUpExplanation !== undefined) popUpExplanation.close(); WA.removeBubble(); + WA.loadSound(exitSoundUrl).play(soundConfig); }) diff --git a/maps/Tuto/webrtc-in.mp3 b/maps/Tuto/webrtc-in.mp3 new file mode 100644 index 00000000..34e22003 Binary files /dev/null and b/maps/Tuto/webrtc-in.mp3 differ diff --git a/maps/Tuto/webrtc-out.mp3 b/maps/Tuto/webrtc-out.mp3 new file mode 100644 index 00000000..dcf02928 Binary files /dev/null and b/maps/Tuto/webrtc-out.mp3 differ diff --git a/maps/Village/Village.json b/maps/Village/Village.json index e4ee93bf..30733388 100644 --- a/maps/Village/Village.json +++ b/maps/Village/Village.json @@ -33,7 +33,7 @@ "y":0 }, { - "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 1979, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 1979, 0, 1979, 1979, 0, 1979, 1979, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 1979, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 1979, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 1979, 1979, 0, 1979, 1979, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 1979, 1979, 0, 1979, 1979, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 1979, 1979, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 1979, 0, 1979, 0, 0, 1979, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 1979, 0, 0, 0, 0, 0, 0, 0, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "height":39, "id":7, "name":"collides", @@ -769,7 +769,20 @@ "tilecount":121, "tileheight":32, "tilewidth":32 - }, + }, + { + "columns":3, + "firstgid":4611, + "image":"su1 Student fmale 12.png", + "imageheight":128, + "imagewidth":96, + "margin":0, + "name":"su1 Student fmale 12", + "spacing":0, + "tilecount":12, + "tileheight":32, + "tilewidth":32 + }, { "columns":5, "firstgid":4611, diff --git a/maps/tests/Audience.mp3 b/maps/tests/Audience.mp3 new file mode 100644 index 00000000..81745d14 Binary files /dev/null and b/maps/tests/Audience.mp3 differ diff --git a/maps/tests/SoundScript.js b/maps/tests/SoundScript.js new file mode 100644 index 00000000..f90dfe0f --- /dev/null +++ b/maps/tests/SoundScript.js @@ -0,0 +1,44 @@ +var zonePlaySound = "PlaySound"; +var zonePlaySoundLoop = "playSoundLoop"; +var stopSound = "StopSound"; +var loopConfig ={ + volume : 0.5, + loop : true +} +var configBase = { + volume : 0.5, + loop : false +} +var enterSoundUrl = "webrtc-in.mp3"; +var exitSoundUrl = "webrtc-out.mp3"; +var winSoundUrl = "Win.ogg"; +var enterSound; +var exitSound; +var winSound; +loadAllSounds(); +winSound.play(configBase); +WA.onEnterZone(zonePlaySound, () => { +enterSound.play(configBase); +}) + +WA.onEnterZone(zonePlaySoundLoop, () => { +winSound.play(loopConfig); +}) + +WA.onLeaveZone(zonePlaySoundLoop, () => { + winSound.stop(); +}) + +WA.onEnterZone('popupZone', () => { + +}); + +WA.onLeaveZone('popupZone', () => { + +}) + + function loadAllSounds(){ + winSound = WA.loadSound(winSoundUrl); + enterSound = WA.loadSound(enterSoundUrl); + exitSound = WA.loadSound(exitSoundUrl); + } diff --git a/maps/tests/SoundTest.json b/maps/tests/SoundTest.json new file mode 100644 index 00000000..f1e38761 --- /dev/null +++ b/maps/tests/SoundTest.json @@ -0,0 +1,154 @@ +{ "compressionlevel":-1, + "height":20, + "infinite":false, + "layers":[ + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":20, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":20, + "x":0, + "y":0 + }, + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":20, + "id":4, + "name":"floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":20, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":20, + "id":3, + "name":"playSound", + "opacity":1, + "properties":[ + { + "name":"zone", + "type":"string", + "value":"PlaySound" + }], + "type":"tilelayer", + "visible":true, + "width":20, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":20, + "id":6, + "name":"playSoundLoop", + "opacity":1, + "properties":[ + { + "name":"zone", + "type":"string", + "value":"playSoundLoop" + }], + "type":"tilelayer", + "visible":true, + "width":20, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":5, + "name":"floorLayer", + "objects":[ + { + "height":19.296875, + "id":2, + "name":"", + "rotation":0, + "text": + { + "text":"Play Sound", + "wrap":true + }, + "type":"", + "visible":true, + "width":107.109375, + "x":258.4453125, + "y":197.018229166667 + }, + { + "height":19.296875, + "id":3, + "name":"", + "rotation":0, + "text": + { + "text":"Bonjour Monde", + "wrap":true + }, + "type":"", + "visible":true, + "width":107.109375, + "x":-348.221354166667, + "y":257.018229166667 + }, + { + "height":55.296875, + "id":4, + "name":"", + "rotation":0, + "text": + { + "text":"Play Sound Loop\nexit Zone Stop Sound \n", + "wrap":true + }, + "type":"", + "visible":true, + "width":176.442708333333, + "x":243.778645833333, + "y":368.3515625 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":8, + "nextobjectid":5, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"SoundScript.js" + }], + "renderorder":"right-down", + "tiledversion":"1.5.0", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.5, + "width":20 +} \ No newline at end of file diff --git a/maps/tests/Win.ogg b/maps/tests/Win.ogg new file mode 100644 index 00000000..43880a77 Binary files /dev/null and b/maps/tests/Win.ogg differ diff --git a/maps/tests/help_camera_setting.json b/maps/tests/help_camera_setting.json new file mode 100644 index 00000000..2dcdec3a --- /dev/null +++ b/maps/tests/help_camera_setting.json @@ -0,0 +1,164 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 49, 50, 50, 50, 50, 50, 50, 50, 50, 51], + "height":10, + "id":3, + "name":"bottom", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":1, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":2, + "name":"floorLayer", + "objects":[ + { + "height":254.57168784029, + "id":1, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":12, + "text":"Test 1 : \nBlock permission to camera and\/or microphone access.\n\nResult 1 :\nOrange popup show at the bottom of the screen.\nIf you click on it, the HelpCameraSetting popup open.\n\nTest 2 : \nReload the page and block permission to camera and\/or microphone access on the camera setting page.\n\nResult 2 : \nOrange popup show at the bottom of the screen.\nIf you click on it, the HelpCameraSetting popup open.\n", + "wrap":true + }, + "type":"", + "visible":true, + "width":295.278811252269, + "x":12.2517014519056, + "y":49.3021778584392 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":6, + "nextobjectid":2, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.4.3", + "tileheight":32, + "tilesets":[ + { + "columns":8, + "firstgid":1, + "image":"Validation\/tileset_dungeon.png", + "imageheight":256, + "imagewidth":256, + "margin":0, + "name":"dungeon", + "spacing":0, + "tilecount":64, + "tileheight":32, + "tiles":[ + { + "id":0, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":1, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":2, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":5, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":8, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":10, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":16, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":17, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":18, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.4, + "width":10 +} \ No newline at end of file diff --git a/maps/tests/index.html b/maps/tests/index.html index cd904056..af70151e 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -50,6 +50,14 @@ Testing scripting API with a script + + + Success Failure Pending + + + Testing scripting API loadSound() function + + Success Failure Pending @@ -82,6 +90,14 @@ Test energy consumption + + + Success Failure Pending + + + Test the HelpCameraSettingScene + +