From 5cd982f11405059d26304a403bffa076943cec03 Mon Sep 17 00:00:00 2001 From: Thomas Alberola Date: Tue, 26 Jan 2021 17:19:32 +0100 Subject: [PATCH 01/36] core(Readme) : Fix App url after installation --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index faafed98..51992251 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ docker-compose up The environment will start. -You should now be able to browse to http://workadventure.localhost/ and see the application. +You should now be able to browse to http://play.workadventure.localhost/ and see the application. +You can view the dashboard at http://workadventure.localhost:8080/ Note: on some OSes, you will need to add this line to your `/etc/hosts` file: From 05b829da7092009387cda7773973303a3922d8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Schl=C3=BCter?= Date: Sat, 13 Feb 2021 21:19:45 +0100 Subject: [PATCH 02/36] fix multi user videochat mode --- front/dist/resources/style/style.css | 23 +++++++++-------------- front/src/WebRtc/LayoutManager.ts | 2 +- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 4bf05455..e3291ad7 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -612,10 +612,9 @@ input[type=range]:focus::-ms-fill-upper { } .chat-mode { - display: flex; + display: grid; width: 100%; - flex-wrap: wrap; align-items: flex-start; padding: 1%; @@ -631,24 +630,20 @@ input[type=range]:focus::-ms-fill-upper { .chat-mode > div:hover { margin: 0%; } -.chat-mode.one-col > div { - flex-basis: 98%; +.chat-mode.one-col { + grid-template-columns: repeat(1, 1fr); } -.chat-mode.two-col > div { - flex-basis: 48%; +.chat-mode.two-col { + grid-template-columns: repeat(2, 1fr); } -.chat-mode.three-col > div { - flex-basis: 31.333333%; +.chat-mode.three-col { + grid-template-columns: repeat(3, 1fr); } -.chat-mode.four-col > div { - flex-basis: 23%; -} - -.chat-mode > div:last-child { - flex-grow: 5; +.chat-mode.four-col { + grid-template-columns: repeat(4, 1fr); } /*CONSOLE*/ diff --git a/front/src/WebRtc/LayoutManager.ts b/front/src/WebRtc/LayoutManager.ts index 4587f870..3c4c0ddd 100644 --- a/front/src/WebRtc/LayoutManager.ts +++ b/front/src/WebRtc/LayoutManager.ts @@ -188,7 +188,7 @@ class LayoutManager { } else { HtmlUtils.getElementByIdOrFail('sidebar').style.display = 'none'; HtmlUtils.getElementByIdOrFail('main-section').style.display = 'none'; - HtmlUtils.getElementByIdOrFail('chat-mode').style.display = 'flex'; + HtmlUtils.getElementByIdOrFail('chat-mode').style.display = 'grid'; } for (const div of this.importantDivs.values()) { From 9670f92a0804e761b030a99246d691b695ccda2d Mon Sep 17 00:00:00 2001 From: znerol Date: Sat, 30 Jan 2021 17:51:07 +0100 Subject: [PATCH 03/36] Allow URLs relative to map base in iframe / openWebsite --- front/src/Phaser/Game/GameScene.ts | 2 +- front/src/WebRtc/CoWebsiteManager.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index a5f29364..b4a74e89 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -653,7 +653,7 @@ export class GameScene extends ResizableScene implements CenterListener { coWebsiteManager.closeCoWebsite(); }else{ const openWebsiteFunction = () => { - coWebsiteManager.loadCoWebsite(newValue as string, allProps.get('openWebsitePolicy') as string | undefined); + coWebsiteManager.loadCoWebsite(newValue as string, this.MapUrlFile, allProps.get('openWebsitePolicy') as string | undefined); layoutManager.removeActionButton('openWebsite', this.userInputManager); }; diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index ef73ac1d..4e74c4a7 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -42,7 +42,7 @@ class CoWebsiteManager { this.opened = iframeStates.opened; } - public loadCoWebsite(url: string, allowPolicy?: string): void { + public loadCoWebsite(url: string, base: string, allowPolicy?: string): void { this.load(); this.cowebsiteDiv.innerHTML = ` diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 322c10eb..2576a72a 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -330,10 +330,7 @@ body { top: 0; width: 50%; height: 100vh; - cursor: ew-resize; - } - #cowebsite iframe { - margin-left: 30px; + display: flex; } #cowebsite.loading { transform: translateX(90%); @@ -342,6 +339,25 @@ body { transform: translateX(100%); } + #cowebsite main { + width: 100%; + } + + #cowebsite aside { + width: 30px; + background: gray; + align-items: center; + display: flex; + cursor: ew-resize; + } + + #cowebsite aside img { + cursor: ew-resize; + margin: 3px; + transform: rotate(90deg); + pointer-events: none; + } + #cowebsite .close-btn{ position: absolute; top: 10px; @@ -415,7 +431,7 @@ body { background-color: gray; } -#cowebsite > iframe { +#cowebsite main iframe { width: 100%; height: 100%; } diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 27e815b0..a9a0939f 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -1,6 +1,5 @@ import {HtmlUtils} from "./HtmlUtils"; - -export type CoWebsiteStateChangedCallback = () => void; +import {Subject} from "rxjs"; enum iframeStates { closed = 1, @@ -8,14 +7,18 @@ enum iframeStates { opened, } -const cowebsiteDivId = "cowebsite"; // the id of the parent div of the iframe. +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'; const animationTime = 500; //time used by the css transitions, in ms. class CoWebsiteManager { private opened: iframeStates = iframeStates.closed; - private observers = new Array(); + private _onStateChange: Subject = new Subject(); + public onStateChange = this._onStateChange.asObservable(); /** * Quickly going in and out of an iframe trigger can create conflicts between the iframe states. * So we use this promise to queue up every cowebsite state transition @@ -23,16 +26,38 @@ class CoWebsiteManager { private currentOperationPromise: Promise = Promise.resolve(); private cowebsiteDiv: HTMLDivElement; private resizing: boolean = false; + private currentWidth: number = 0; + private cowebsiteMainDom: HTMLDivElement; + private cowebsiteAsideDom: HTMLDivElement; + + get width(): number { + return this.cowebsiteDiv.clientWidth; + } + + set width(width: number) { + this.cowebsiteDiv.style.width = width+'px'; + } constructor() { this.cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); - - this.cowebsiteDiv.addEventListener('mousedown', (event) => { + this.cowebsiteMainDom = HtmlUtils.getElementByIdOrFail(cowebsiteMainDomId); + this.cowebsiteAsideDom = HtmlUtils.getElementByIdOrFail(cowebsiteAsideDomId); + + this.initResizeListeners(); + + HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId).addEventListener('click', () => { + this.closeCoWebsite(); + }); + } + + private initResizeListeners() { + this.cowebsiteAsideDom.addEventListener('mousedown', (event) => { this.resizing = true; this.getIframeDom().style.display = 'none'; - + document.onmousemove = (event) => { - this.cowebsiteDiv.style.width = (this.cowebsiteDiv.clientWidth - event.movementX) + 'px'; + this.width = this.width - event.movementX; + this.fire(); } }); @@ -43,7 +68,7 @@ class CoWebsiteManager { this.resizing = false; }); } - + private close(): void { this.cowebsiteDiv.classList.remove('loaded'); //edit the css class to trigger the transition this.cowebsiteDiv.classList.add('hidden'); @@ -57,8 +82,13 @@ class CoWebsiteManager { private open(): void { this.cowebsiteDiv.classList.remove('loading', 'hidden'); //edit the css class to trigger the transition this.opened = iframeStates.opened; + this.resetWidth(); } - + + private resetWidth() { + this.width = window.innerWidth / 2; + } + private getIframeDom(): HTMLIFrameElement { const iframe = HtmlUtils.getElementByIdOrFail(cowebsiteDivId).querySelector('iframe'); if (!iframe) throw new Error('Could not find iframe!'); @@ -67,14 +97,7 @@ class CoWebsiteManager { public loadCoWebsite(url: string, base: string, allowPolicy?: string): void { this.load(); - this.cowebsiteDiv.innerHTML = ``; - setTimeout(() => { - HtmlUtils.getElementByIdOrFail('cowebsite-close').addEventListener('click', () => { - this.closeCoWebsite(); - }); - }, 100); + this.cowebsiteMainDom.innerHTML = ``; const iframe = document.createElement('iframe'); iframe.id = 'cowebsite-iframe'; @@ -85,7 +108,7 @@ class CoWebsiteManager { const onloadPromise = new Promise((resolve) => { iframe.onload = () => resolve(); }); - this.cowebsiteDiv.appendChild(iframe); + this.cowebsiteMainDom.appendChild(iframe); const onTimeoutPromise = new Promise((resolve) => { setTimeout(() => resolve(), 2000); }); @@ -116,9 +139,7 @@ class CoWebsiteManager { this.close(); this.fire(); setTimeout(() => { - this.cowebsiteDiv.innerHTML = ``; + this.cowebsiteMainDom.innerHTML = ``; resolve(); }, animationTime) })); @@ -134,7 +155,7 @@ class CoWebsiteManager { } if (window.innerWidth >= window.innerHeight) { return { - width: window.innerWidth / 2, + width: window.innerWidth - this.width, height: window.innerHeight } } else { @@ -144,16 +165,9 @@ class CoWebsiteManager { } } } - - //todo: is it still useful to allow any kind of observers? - public onStateChange(observer: CoWebsiteStateChangedCallback) { - this.observers.push(observer); - } - + private fire(): void { - for (const callback of this.observers) { - callback(); - } + this._onStateChange.next(); } } diff --git a/front/src/index.ts b/front/src/index.ts index 1ef53d65..84c54d2a 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -118,7 +118,7 @@ window.addEventListener('resize', function (event) { } }); -coWebsiteManager.onStateChange(() => { +coWebsiteManager.onStateChange.subscribe(() => { const {width, height} = coWebsiteManager.getGameSize(); game.scale.resize(width / RESOLUTION, height / RESOLUTION); }); From f9c8b4131cba3963f90acc9beb8b84269b8db9a1 Mon Sep 17 00:00:00 2001 From: kharhamel Date: Wed, 17 Mar 2021 18:57:00 +0100 Subject: [PATCH 23/36] added fullscreen --- front/dist/index.tmpl.html | 6 +++++- front/dist/resources/style/style.css | 21 +++++++++++++++------ front/src/WebRtc/CoWebsiteManager.ts | 24 +++++++++++++++++++++++- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index c1adaa87..0bc3b99e 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -71,7 +71,11 @@
- + diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 2576a72a..bcbacfb6 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -358,7 +358,7 @@ body { pointer-events: none; } - #cowebsite .close-btn{ + #cowebsite .top-right-btn{ position: absolute; top: 10px; right: -100px; @@ -366,9 +366,9 @@ body { border: none; cursor: url('/resources/logos/cursor_pointer.png'), pointer; animation: right .2s ease; - } + } - #cowebsite .close-btn img { + #cowebsite .top-right-btn img { height: 20px; right: 15px; background-color: rgba(0,0.0,0,0.3); @@ -376,13 +376,22 @@ body { border-radius: 3px; } - #cowebsite .close-btn img:hover { + #cowebsite .top-right-btn img:hover { background-color: rgba(0,0,0,0.4); } - - #cowebsite:hover .close-btn{ + + #cowebsite #cowebsite-close { + right: -140px; + } + #cowebsite:hover #cowebsite-close{ right: 10px; } + #cowebsite #cowebsite-fullscreen { + right: -100px; + } + #cowebsite:hover #cowebsite-fullscreen{ + right: 40px; + } } @media (max-aspect-ratio: 1/1) { .game-overlay { diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index a9a0939f..a10fd111 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -5,12 +5,16 @@ enum iframeStates { closed = 1, loading, // loading an iframe can be slow, so we show some placeholder until it is ready opened, + fullscreen, } 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'; +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. class CoWebsiteManager { @@ -26,7 +30,6 @@ class CoWebsiteManager { private currentOperationPromise: Promise = Promise.resolve(); private cowebsiteDiv: HTMLDivElement; private resizing: boolean = false; - private currentWidth: number = 0; private cowebsiteMainDom: HTMLDivElement; private cowebsiteAsideDom: HTMLDivElement; @@ -48,6 +51,9 @@ class CoWebsiteManager { HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId).addEventListener('click', () => { this.closeCoWebsite(); }); + HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId).addEventListener('click', () => { + this.fullscreen(); + }); } private initResizeListeners() { @@ -169,6 +175,22 @@ class CoWebsiteManager { private fire(): void { this._onStateChange.next(); } + + private fullscreen(): void { + if (this.opened === iframeStates.fullscreen) { + this.opened = iframeStates.opened; + this.width = window.innerWidth / 2; + //we don't trigger a resize of the phaser game since it won't be visible anyway. + HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = 'inline'; + HtmlUtils.getElementByIdOrFail(cowebsiteCloseFullScreenImageId).style.display = 'none'; + } else { + this.opened = iframeStates.fullscreen; + this.width = window.innerWidth; + //we don't trigger a resize of the phaser game since it won't be visible anyway. + HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = 'none'; + HtmlUtils.getElementByIdOrFail(cowebsiteCloseFullScreenImageId).style.display = 'inline'; + } + } } export const coWebsiteManager = new CoWebsiteManager(); \ No newline at end of file From 0701e607fad442578998432ca15ddc97009e7a5c Mon Sep 17 00:00:00 2001 From: kharhamel Date: Thu, 18 Mar 2021 12:37:05 +0100 Subject: [PATCH 24/36] added sass loader to the project and put the cowebsite style in its own sass file --- front/dist/.gitignore | 1 + front/dist/index.tmpl.html | 1 + front/dist/resources/style/cowebsite.scss | 150 +++++++++++++++++ front/dist/resources/style/index.scss | 2 + front/dist/resources/style/style.css | 79 --------- front/package.json | 4 + front/src/index.ts | 2 + front/webpack.config.js | 6 + front/yarn.lock | 189 +++++++++++++++++++++- 9 files changed, 352 insertions(+), 82 deletions(-) create mode 100644 front/dist/resources/style/cowebsite.scss create mode 100644 front/dist/resources/style/index.scss diff --git a/front/dist/.gitignore b/front/dist/.gitignore index dd1fae3d..50a1ae0c 100644 --- a/front/dist/.gitignore +++ b/front/dist/.gitignore @@ -1,2 +1,3 @@ index.html index.tmpl.html.tmp +style.css \ No newline at end of file diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index 0bc3b99e..e4423fdd 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -30,6 +30,7 @@ + WorkAdventure diff --git a/front/dist/resources/style/cowebsite.scss b/front/dist/resources/style/cowebsite.scss new file mode 100644 index 00000000..8af87f42 --- /dev/null +++ b/front/dist/resources/style/cowebsite.scss @@ -0,0 +1,150 @@ +#cowebsite { + aside { + background: gray; + cursor: ew-resize; + + img { + cursor: ew-resize; + margin: 3px; + pointer-events: none; + height: 20px; + } + } + + .top-right-btn{ + position: absolute; + background: none; + border: none; + cursor: url('/resources/logos/cursor_pointer.png'), pointer; + + img { + height: 20px; + background-color: rgba(0,0.0,0,0.3); + padding: 5px; + border-radius: 3px; + } + + img:hover { + background-color: rgba(0,0,0,0.4); + } + } +} + +@media (min-aspect-ratio: 1/1) { + #cowebsite { + right: 0; + top: 0; + width: 50%; + height: 100vh; + display: flex; + + &.loading { + transform: translateX(90%); + } + &.hidden { + transform: translateX(100%); + } + + main { + width: 100%; + } + + + aside { + width: 30px; + align-items: center; + display: flex; + + img { + transform: rotate(90deg); + } + } + + .top-right-btn{ + top: 10px; + right: -100px; + animation: right .2s ease; + + img { + right: 15px; + } + } + + #cowebsite-close { + right: -140px; + } + #cowebsite-fullscreen { + right: -100px; + } + + } + + #cowebsite:hover { + #cowebsite-close{ + right: 10px; + } + + #cowebsite-fullscreen{ + right: 40px; + } + } +} +@media (max-aspect-ratio: 1/1) { + + + #cowebsite { + left: 0; + bottom: 0; + width: 100%; + height: 50%; + display: flex; + flex-direction: column; + + &.loading { + transform: translateY(90%); + } + &.hidden { + transform: translateY(100%); + } + + main { + height: 100%; + } + + + aside { + height: 30px; + align-items: center; + display: flex; + flex-direction: column; + } + + .top-right-btn{ + top: 10px; + right: -100px; + animation: right .2s ease; + + img { + right: 15px; + } + } + + #cowebsite-close { + right: -140px; + } + #cowebsite-fullscreen { + right: -100px; + } + + } + + #cowebsite:hover { + #cowebsite-close{ + right: 10px; + } + + #cowebsite-fullscreen{ + right: 40px; + } + } +} \ No newline at end of file diff --git a/front/dist/resources/style/index.scss b/front/dist/resources/style/index.scss new file mode 100644 index 00000000..bb4c167e --- /dev/null +++ b/front/dist/resources/style/index.scss @@ -0,0 +1,2 @@ +@import "cowebsite.scss"; +/*@import "style.css"*/ \ No newline at end of file diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index bcbacfb6..1ecbe889 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -325,73 +325,7 @@ body { max-height: 25%; } - #cowebsite { - right: 0; - top: 0; - width: 50%; - height: 100vh; - display: flex; - } - #cowebsite.loading { - transform: translateX(90%); - } - #cowebsite.hidden { - transform: translateX(100%); - } - - #cowebsite main { - width: 100%; - } - #cowebsite aside { - width: 30px; - background: gray; - align-items: center; - display: flex; - cursor: ew-resize; - } - - #cowebsite aside img { - cursor: ew-resize; - margin: 3px; - transform: rotate(90deg); - pointer-events: none; - } - - #cowebsite .top-right-btn{ - position: absolute; - top: 10px; - right: -100px; - background: none; - border: none; - cursor: url('/resources/logos/cursor_pointer.png'), pointer; - animation: right .2s ease; - } - - #cowebsite .top-right-btn img { - height: 20px; - right: 15px; - background-color: rgba(0,0.0,0,0.3); - padding: 5px; - border-radius: 3px; - } - - #cowebsite .top-right-btn img:hover { - background-color: rgba(0,0,0,0.4); - } - - #cowebsite #cowebsite-close { - right: -140px; - } - #cowebsite:hover #cowebsite-close{ - right: 10px; - } - #cowebsite #cowebsite-fullscreen { - right: -100px; - } - #cowebsite:hover #cowebsite-fullscreen{ - right: 40px; - } } @media (max-aspect-ratio: 1/1) { .game-overlay { @@ -410,19 +344,6 @@ body { .sidebar > div:hover { max-width: 25%; } - - #cowebsite { - left: 0; - bottom: 0; - width: 100%; - height: 50%; - } - #cowebsite.loading { - transform: translateY(90%); - } - #cowebsite.hidden { - transform: translateY(100%); - } } #game { diff --git a/front/package.json b/front/package.json index 0e50ba80..1ba7f3c2 100644 --- a/front/package.json +++ b/front/package.json @@ -9,9 +9,13 @@ "@types/quill": "^1.3.7", "@typescript-eslint/eslint-plugin": "^2.26.0", "@typescript-eslint/parser": "^2.26.0", + "css-loader": "^5.1.3", "eslint": "^6.8.0", "html-webpack-plugin": "^4.3.0", "jasmine": "^3.5.0", + "mini-css-extract-plugin": "^1.3.9", + "sass": "^1.32.8", + "sass-loader": "10.1.1", "ts-loader": "^6.2.2", "ts-node": "^8.10.2", "typescript": "^3.8.3", diff --git a/front/src/index.ts b/front/src/index.ts index 84c54d2a..3e0ae325 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -1,5 +1,7 @@ import 'phaser'; import GameConfig = Phaser.Types.Core.GameConfig; +import "../dist/resources/style/index.scss"; + import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable"; import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; diff --git a/front/webpack.config.js b/front/webpack.config.js index bb03c789..dda5ee4e 100644 --- a/front/webpack.config.js +++ b/front/webpack.config.js @@ -1,6 +1,7 @@ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry: './src/index.ts', @@ -23,6 +24,10 @@ module.exports = { use: 'ts-loader', exclude: /node_modules/, }, + { + test: /\.scss$/, + use: [MiniCssExtractPlugin.loader, 'css-loader?url=false', 'sass-loader'], + }, ], }, resolve: { @@ -37,6 +42,7 @@ module.exports = { require('webpack-require-http') ], plugins: [ + new MiniCssExtractPlugin({filename: 'style.css'}), new HtmlWebpackPlugin( { template: './dist/index.tmpl.html.tmp', diff --git a/front/yarn.lock b/front/yarn.lock index 0b85ad88..533c5420 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -878,6 +878,11 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.2.0: + version "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: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -905,6 +910,21 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +"chokidar@>=2.0.0 <4.0.0": + 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== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -1034,6 +1054,11 @@ 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: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -1222,6 +1247,24 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +css-loader@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.3.tgz#87f6fc96816b20debe3cf682f85c7e56a963d0d1" + integrity sha512-CoPZvyh8sLiGARK3gqczpfdedbM74klGWurF2CsNZ2lhNaXdLIUks+3Mfax3WBeRuHoglU+m7KG/+7gY6G4aag== + dependencies: + camelcase "^6.2.0" + cssesc "^3.0.0" + icss-utils "^5.1.0" + loader-utils "^2.0.0" + postcss "^8.2.8" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^3.0.0" + semver "^7.3.4" + css-select@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" @@ -1237,6 +1280,11 @@ css-what@2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +cssesc@^3.0.0: + version "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" @@ -2128,6 +2176,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2497,6 +2550,11 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "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: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" @@ -2543,6 +2601,11 @@ imurmurhash@^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" @@ -2942,6 +3005,11 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klona@^2.0.4: + version "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" @@ -3010,6 +3078,13 @@ lru-cache@^5.1.1: 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" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + 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" @@ -3156,6 +3231,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.3.9" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.9.tgz#47a32132b0fd97a119acd530e8421e8f6ab16d5e" + integrity sha512-Ac4s+xhVbqlyhXS5J/Vh/QXUz3ycXlCqoCPpg0vdfhsIBH9eg/It/9L1r1XhSCH737M1lqcWnMuWL13zcygn5A== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + webpack-sources "^1.1.0" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -3259,6 +3343,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== +nanoid@^3.1.20: + version "3.1.22" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" + integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -3286,7 +3375,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.5.0, neo-async@^2.6.1, 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== @@ -3721,6 +3810,58 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + 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== + 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: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss@^8.2.8: + version "8.2.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" + integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw== + dependencies: + colorette "^1.2.2" + nanoid "^3.1.20" + 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" @@ -3940,6 +4081,13 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + 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" @@ -4123,6 +4271,24 @@ 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== + 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.8" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.8.tgz#f16a9abd8dc530add8834e506878a2808c037bdc" + integrity sha512-Sl6mIeGpzjIUZqvKnKETfMf0iDAswD9TNlv13A7aAF3XZlRPMq4VvJWBC2N2DXbp94MQVdNSFG6LfF/iOXrPHQ== + dependencies: + chokidar ">=2.0.0 <4.0.0" + schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -4168,6 +4334,13 @@ semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +semver@^7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -4812,6 +4985,11 @@ 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" @@ -4877,7 +5055,7 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -5056,7 +5234,7 @@ webpack-require-http@^0.4.3: md5 "^2.0.0" url "^0.11.0" -webpack-sources@^1.4.0, webpack-sources@^1.4.1: +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -5193,6 +5371,11 @@ yallist@^3.0.2: 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== + yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" From ec691a75fe64e0c04323971db5ae1f2e09926506 Mon Sep 17 00:00:00 2001 From: githubIsNotOpenSourceLOL <76752051+githubIsNotOpenSourceLOL@users.noreply.github.com> Date: Thu, 18 Mar 2021 13:26:02 +0100 Subject: [PATCH 25/36] Update .env.template --- .env.template | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.env.template b/.env.template index a83bd171..a54df82e 100644 --- a/.env.template +++ b/.env.template @@ -10,3 +10,6 @@ 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= + +# The email address used by Let's encrypt to send renewal warnings (compulsory) +ACME_EMAIL= From 32fdfaab35186d37ff16c35e9f5efb2cce08f160 Mon Sep 17 00:00:00 2001 From: kharhamel Date: Thu, 18 Mar 2021 15:05:15 +0100 Subject: [PATCH 26/36] edited CoWebsiteManager to manage vertical resizing --- front/dist/.gitignore | 2 +- front/dist/index.tmpl.html | 2 - front/dist/resources/style/cowebsite.scss | 36 ++++++++++---- front/dist/resources/style/index.scss | 2 +- front/dist/resources/style/style.css | 15 ------ front/src/WebRtc/CoWebsiteManager.ts | 57 +++++++++++++++-------- front/src/index.ts | 3 +- front/webpack.config.js | 2 +- 8 files changed, 70 insertions(+), 49 deletions(-) diff --git a/front/dist/.gitignore b/front/dist/.gitignore index 50a1ae0c..05c474ec 100644 --- a/front/dist/.gitignore +++ b/front/dist/.gitignore @@ -1,3 +1,3 @@ index.html index.tmpl.html.tmp -style.css \ No newline at end of file +style.*.css \ No newline at end of file diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index e4423fdd..3ec6ad3b 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -29,8 +29,6 @@ - - WorkAdventure diff --git a/front/dist/resources/style/cowebsite.scss b/front/dist/resources/style/cowebsite.scss index 8af87f42..ba8fdb84 100644 --- a/front/dist/resources/style/cowebsite.scss +++ b/front/dist/resources/style/cowebsite.scss @@ -1,10 +1,27 @@ +/* A potentially shared website could appear in an iframe in the cowebsite space. */ + #cowebsite { + position: fixed; + transition: transform 0.5s; + background-color: white; + + &.loading { + background-color: gray; + } + + main { + iframe { + width: 100%; + height: 100%; + } + } + aside { background: gray; - cursor: ew-resize; + align-items: center; + display: flex; img { - cursor: ew-resize; margin: 3px; pointer-events: none; height: 20px; @@ -52,10 +69,10 @@ aside { width: 30px; - align-items: center; - display: flex; + cursor: ew-resize; img { + cursor: ew-resize; transform: rotate(90deg); } } @@ -85,7 +102,7 @@ } #cowebsite-fullscreen{ - right: 40px; + right: 45px; } } } @@ -114,9 +131,12 @@ aside { height: 30px; - align-items: center; - display: flex; + cursor: ns-resize; flex-direction: column; + + img { + cursor: ns-resize; + } } .top-right-btn{ @@ -144,7 +164,7 @@ } #cowebsite-fullscreen{ - right: 40px; + right: 45px; } } } \ No newline at end of file diff --git a/front/dist/resources/style/index.scss b/front/dist/resources/style/index.scss index bb4c167e..4d0355b7 100644 --- a/front/dist/resources/style/index.scss +++ b/front/dist/resources/style/index.scss @@ -1,2 +1,2 @@ @import "cowebsite.scss"; -/*@import "style.css"*/ \ No newline at end of file +@import "style.css"; \ No newline at end of file diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 1ecbe889..05ff5a03 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -351,21 +351,6 @@ body { position: relative; /* Position relative is needed for the game-overlay. */ } -/* A potentially shared website could appear in an iframe in the cowebsite space. */ -#cowebsite { - position: fixed; - transition: transform 0.5s; - background-color: white; -} -#cowebsite.loading { - background-color: gray; -} - -#cowebsite main iframe { - width: 100%; - height: 100%; -} - .audioplayer:first-child { display: grid; grid: 2rem / 4rem 10rem; diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index a10fd111..ada438ad 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -5,7 +5,6 @@ enum iframeStates { closed = 1, loading, // loading an iframe can be slow, so we show some placeholder until it is ready opened, - fullscreen, } const cowebsiteDivId = 'cowebsite'; // the id of the whole container. @@ -21,8 +20,8 @@ class CoWebsiteManager { private opened: iframeStates = iframeStates.closed; - private _onStateChange: Subject = new Subject(); - public onStateChange = this._onStateChange.asObservable(); + private _onResize: Subject = new Subject(); + public onResize = this._onResize.asObservable(); /** * Quickly going in and out of an iframe trigger can create conflicts between the iframe states. * So we use this promise to queue up every cowebsite state transition @@ -40,6 +39,22 @@ class CoWebsiteManager { set width(width: number) { this.cowebsiteDiv.style.width = width+'px'; } + + get height(): number { + return this.cowebsiteDiv.clientHeight; + } + + set height(height: number) { + this.cowebsiteDiv.style.height = height+'px'; + } + + get verticalMode(): boolean { + return window.innerWidth < window.innerHeight; + } + + get isFullScreen(): boolean { + return this.verticalMode ? this.height === this.cowebsiteDiv.clientHeight : this.width === this.cowebsiteDiv.clientWidth + } constructor() { this.cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); @@ -57,19 +72,21 @@ class CoWebsiteManager { } private initResizeListeners() { + const movecallback = (event:MouseEvent) => { + this.verticalMode ? this.height -= event.movementY : this.width -= event.movementX; + this.fire(); + } + this.cowebsiteAsideDom.addEventListener('mousedown', (event) => { this.resizing = true; this.getIframeDom().style.display = 'none'; - document.onmousemove = (event) => { - this.width = this.width - event.movementX; - this.fire(); - } + document.addEventListener('mousemove', movecallback); }); document.addEventListener('mouseup', (event) => { if (!this.resizing) return; - document.onmousemove = null; + document.removeEventListener('mousemove', movecallback); this.getIframeDom().style.display = 'block'; this.resizing = false; }); @@ -79,6 +96,7 @@ class CoWebsiteManager { this.cowebsiteDiv.classList.remove('loaded'); //edit the css class to trigger the transition this.cowebsiteDiv.classList.add('hidden'); this.opened = iframeStates.closed; + this.resetStyle(); } private load(): void { this.cowebsiteDiv.classList.remove('hidden'); //edit the css class to trigger the transition @@ -88,11 +106,12 @@ class CoWebsiteManager { private open(): void { this.cowebsiteDiv.classList.remove('loading', 'hidden'); //edit the css class to trigger the transition this.opened = iframeStates.opened; - this.resetWidth(); + this.resetStyle(); } - private resetWidth() { - this.width = window.innerWidth / 2; + public resetStyle() { + this.cowebsiteDiv.style.width = ''; + this.cowebsiteDiv.style.height = ''; } private getIframeDom(): HTMLIFrameElement { @@ -159,7 +178,7 @@ class CoWebsiteManager { height: window.innerHeight } } - if (window.innerWidth >= window.innerHeight) { + if (!this.verticalMode) { return { width: window.innerWidth - this.width, height: window.innerHeight @@ -167,25 +186,23 @@ class CoWebsiteManager { } else { return { width: window.innerWidth, - height: window.innerHeight / 2 + height: window.innerHeight - this.height, } } } private fire(): void { - this._onStateChange.next(); + this._onResize.next(); } - + private fullscreen(): void { - if (this.opened === iframeStates.fullscreen) { - this.opened = iframeStates.opened; - this.width = window.innerWidth / 2; + if (this.isFullScreen) { + this.resetStyle(); //we don't trigger a resize of the phaser game since it won't be visible anyway. HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = 'inline'; HtmlUtils.getElementByIdOrFail(cowebsiteCloseFullScreenImageId).style.display = 'none'; } else { - this.opened = iframeStates.fullscreen; - this.width = window.innerWidth; + this.verticalMode ? this.height = window.innerHeight : this.width = window.innerWidth; //we don't trigger a resize of the phaser game since it won't be visible anyway. HtmlUtils.getElementByIdOrFail(cowebsiteOpenFullScreenImageId).style.display = 'none'; HtmlUtils.getElementByIdOrFail(cowebsiteCloseFullScreenImageId).style.display = 'inline'; diff --git a/front/src/index.ts b/front/src/index.ts index 3e0ae325..8b0a83b6 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -109,6 +109,7 @@ const config: GameConfig = { const game = new Phaser.Game(config); window.addEventListener('resize', function (event) { + coWebsiteManager.resetStyle(); const {width, height} = coWebsiteManager.getGameSize(); game.scale.resize(width / RESOLUTION, height / RESOLUTION); @@ -120,7 +121,7 @@ window.addEventListener('resize', function (event) { } }); -coWebsiteManager.onStateChange.subscribe(() => { +coWebsiteManager.onResize.subscribe(() => { const {width, height} = coWebsiteManager.getGameSize(); game.scale.resize(width / RESOLUTION, height / RESOLUTION); }); diff --git a/front/webpack.config.js b/front/webpack.config.js index dda5ee4e..7e376da1 100644 --- a/front/webpack.config.js +++ b/front/webpack.config.js @@ -42,7 +42,7 @@ module.exports = { require('webpack-require-http') ], plugins: [ - new MiniCssExtractPlugin({filename: 'style.css'}), + new MiniCssExtractPlugin({filename: 'style.[contenthash].css'}), new HtmlWebpackPlugin( { template: './dist/index.tmpl.html.tmp', From d68406d5e140f003dcc7285f21ef31c1c990b450 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 2 Feb 2021 17:31:44 +0100 Subject: [PATCH 27/36] Create input need page --- .../resources/html/helpCameraSettings.html | 109 ++++++++++++++++++ .../help-setting-camera-permission.png | Bin 0 -> 66630 bytes front/src/Phaser/Login/EnableCameraScene.ts | 102 ++++++++++++++-- 3 files changed, 199 insertions(+), 12 deletions(-) create mode 100644 front/dist/resources/html/helpCameraSettings.html create mode 100644 front/dist/resources/objects/help-setting-camera-permission.png diff --git a/front/dist/resources/html/helpCameraSettings.html b/front/dist/resources/html/helpCameraSettings.html new file mode 100644 index 00000000..6ec6fbac --- /dev/null +++ b/front/dist/resources/html/helpCameraSettings.html @@ -0,0 +1,109 @@ + + + diff --git a/front/dist/resources/objects/help-setting-camera-permission.png b/front/dist/resources/objects/help-setting-camera-permission.png new file mode 100644 index 0000000000000000000000000000000000000000..833499cdb2688f76b60e523470d13f1a1a904245 GIT binary patch literal 66630 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 literal 0 HcmV?d00001 diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index 321eabed..dffe1042 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -1,8 +1,6 @@ import {gameManager} from "../Game/GameManager"; import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; -import {GameSceneInitInterface} from "../Game/GameScene"; -import {StartMapInterface} from "../../Connexion/ConnexionModels"; import {mediaManager} from "../../WebRtc/MediaManager"; import {RESOLUTION} from "../../Enum/EnvironmentVariable"; import {SoundMeter} from "../Components/SoundMeter"; @@ -18,6 +16,8 @@ enum LoginTextures { arrowUp = "arrow_up" } +const helpCameraSettings = 'helpCameraSettings'; + export class EnableCameraScene extends Phaser.Scene { private textField!: TextField; private pressReturnField!: TextField; @@ -35,6 +35,8 @@ export class EnableCameraScene extends Phaser.Scene { private soundMeterSprite!: SoundMeterSprite; private microphoneNameField!: TextField; private repositionCallback!: (this: Window, ev: UIEvent) => void; + private helpCameraSettingsElement!: Phaser.GameObjects.DOMElement; + private helpCameraSettingsOpened: boolean = false; constructor() { super({ @@ -50,6 +52,7 @@ export class EnableCameraScene extends Phaser.Scene { 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'); + this.load.html(helpCameraSettings, 'resources/html/helpCameraSettings.html'); } create() { @@ -109,6 +112,27 @@ export class EnableCameraScene extends Phaser.Scene { this.soundMeterSprite.setVisible(false); this.add.existing(this.soundMeterSprite); + const middleX = (window.innerWidth / 3) - (370*0.85); + 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) => { + event.preventDefault(); + if((event?.target as HTMLInputElement).id !== 'helpCameraSettingsFormRefresh') { + return; + } + const permission: Element = this.helpCameraSettingsElement.getChildByID('permissionError'); + permission.innerHTML = ''; + return mediaManager.getCamera().then(() => { + window.location.reload(); + }).catch((err) => { + permission.innerHTML = err.message; + }); + }); + if(this.helpCameraSettingsElement.parent){ + (this.helpCameraSettingsElement.parent as HTMLDivElement).style.overflow = 'scroll'; + } + this.repositionCallback = this.reposition.bind(this); window.addEventListener('resize', this.repositionCallback); } @@ -151,6 +175,9 @@ export class EnableCameraScene extends Phaser.Scene { * Function called each time a camera is changed */ private setupStream(stream: MediaStream): void { + if(this.helpCameraSettingsOpened){ + return; + } const img = HtmlUtils.getElementByIdOrFail('webRtcSetupNoVideo'); img.style.display = 'none'; @@ -257,19 +284,27 @@ export class EnableCameraScene extends Phaser.Scene { mediaManager.setLastUpdateScene(); } - private login(): void { - HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; - this.soundMeter.stop(); - window.removeEventListener('resize', this.repositionCallback); - - mediaManager.stopCamera(); - mediaManager.stopMicrophone(); - - this.scene.sleep(EnableCameraSceneName) - gameManager.goToStartingMap(this.scene); + private login(): Promise { + return mediaManager.getCamera() + .then((mediaStream: MediaStream) => { + HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; + this.soundMeter.stop(); + window.removeEventListener('resize', this.repositionCallback); + mediaManager.stopCamera(); + mediaManager.stopMicrophone(); + this.scene.sleep(EnableCameraSceneName) + gameManager.goToStartingMap(this.scene); + return mediaStream; + }).catch((err) => { + this.openHelpCameraSettingsOpened(); + throw err; + }); } private async getDevices() { + if(this.helpCameraSettingsOpened){ + return; + } const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices(); for (const mediaDeviceInfo of mediaDeviceInfos) { if (mediaDeviceInfo.kind === 'audioinput') { @@ -280,4 +315,47 @@ export class EnableCameraScene extends Phaser.Scene { } this.updateWebCamName(); } + + private openHelpCameraSettingsOpened(): void{ + this.reset(); + HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; + this.helpCameraSettingsOpened = true; + let middleY = (window.innerHeight / 3) - (495); + if(middleY < 0){ + middleY = 0; + } + let middleX = (window.innerWidth / 3) - (370*0.85); + if(middleX < 0){ + middleX = 0; + } + this.tweens.add({ + targets: this.helpCameraSettingsElement, + y: middleY, + x: middleX, + duration: 1000, + ease: 'Power3', + overflow: 'scroll' + }); + } + + 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); + } + + private reset(){ + this.textField.destroy(); + this.pressReturnField.destroy(); + this.cameraNameField.destroy(); + this.microphoneNameField.destroy(); + this.arrowRight.destroy(); + this.arrowLeft.destroy(); + this.arrowUp.destroy(); + this.arrowDown.destroy(); + this.soundMeterSprite.destroy(); + this.input.keyboard.removeAllKeys(); + } } From c654f722e31708db06d15110703b438a99801ceb Mon Sep 17 00:00:00 2001 From: GRL Date: Fri, 19 Mar 2021 11:50:53 +0100 Subject: [PATCH 28/36] Add Firefox image Check permissions in the game Button Continue to continue without allowing camera and microphone --- .../resources/html/helpCameraSettings.html | 6 +- ...help-setting-camera-permission-chrome.png} | Bin ...help-setting-camera-permission-firefox.png | Bin 0 -> 32958 bytes front/src/Phaser/Game/GameScene.ts | 145 ++++++++++++++---- front/src/Phaser/Login/EnableCameraScene.ts | 86 ++--------- 5 files changed, 125 insertions(+), 112 deletions(-) rename front/dist/resources/objects/{help-setting-camera-permission.png => help-setting-camera-permission-chrome.png} (100%) create mode 100644 front/dist/resources/objects/help-setting-camera-permission-firefox.png diff --git a/front/dist/resources/html/helpCameraSettings.html b/front/dist/resources/html/helpCameraSettings.html index 6ec6fbac..d1c0e04d 100644 --- a/front/dist/resources/html/helpCameraSettings.html +++ b/front/dist/resources/html/helpCameraSettings.html @@ -76,7 +76,7 @@ margin: 0px 20px; } #helpCameraSettings section p.err{ - color: red; + color: #ff0000; } #helpCameraSettings section ul{ margin: 6px; @@ -101,9 +101,11 @@
  • Please ensure that you have a camera AND microphone plugged into your computer.
  • Once you've followed these steps, please refresh this page.

    - +

    If you prefer to continue without allowing camera and microphone access, click on Continue

    +

    +
    diff --git a/front/dist/resources/objects/help-setting-camera-permission.png b/front/dist/resources/objects/help-setting-camera-permission-chrome.png similarity index 100% rename from front/dist/resources/objects/help-setting-camera-permission.png rename to front/dist/resources/objects/help-setting-camera-permission-chrome.png diff --git a/front/dist/resources/objects/help-setting-camera-permission-firefox.png b/front/dist/resources/objects/help-setting-camera-permission-firefox.png new file mode 100644 index 0000000000000000000000000000000000000000..7144f88b926f04f56a0bf30d81d5c63740746682 GIT binary patch literal 32958 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%0AQ=G_xAT{fz;*%*mIs3{jEn(h69?6G_OMI+CcH6n z_dD_Q1~z4>s_=YVXLIE2gsp6?|G~GKRXvyBfMnO?G~DaUwLeqZ8&7+*EOL~x(CpWS z#CB`iDga%daahZ+AZp?7@TVrYn^G`?6pNv zZH})n;9qeM`Tn{_n3UTk1sFO*HRl9Hq^9bD%y=Z8(;Ac6eW{|MCy}caV>KI-UAyGAE|JPCz3Hy;eo6X6-4%gy|g3bka65!{?_5G4Tqtk&*&waIh%*Vgr zQcbl2(I*#>K+sQ@)=0f#WSR<+=jOb&$Vt~a0F_lZpyY)qMS>y{^Q%mu%Mzz_Jet$L z)hjZ--pY*d&|zyz_vg;-uc>o2jAzH(crN|?NWPS`F7&BiZR9WccBAz8J%e%j_Hi~C zy>2fx23AlybDRCdgBj%s7=39m2w8%kEw?X%H zJk9lB-T?`wD}5s2mfBZ9!4D-Xqu6#(0Q0yPlzVIjO4)NR)a~x>9x!IM;lP(ESr^~g zoQYRa4X-GlI6 zb%E!=k3?iW6qLDZeU>;TYrHy{%Rlz<{>ttBP^CH)^lf$|c2^m%3mrfzj1v0-&w*`k zc;@ZL0$)kfT5-TnIzT`r+DNPj(gFuD6jx{Q9vT{&%j4=BM;20!JZFE2h6#P0in@hF zTIn~UyBSp_*(8k^GjFnkc0A=|R3xvH@@nIyVP`WJsJ}E*l4T3=h05p%k2^M21sc|5 zTZNKEkr>x*iU!S5lhe(v8JsC9iPj=zq2d3NuZiE&SvI6B?-_v`#DN4#mW+khUpWx5 ztt^?5~jMRkd# zDH*0jq}6EMNt1=LDk{>hbF-2GFMt=kq$R%NVQHqAIvl3kp3>YS~=_E4AaMkE;*w`Qz< zmlJ~)wxut8JGc6~nMV27*=f!)MH_KNTZC9u@gTBLHH)Spt-h0Q_=gTJu2{g#=CGYk z^46ky-5jx`?QoIoRI!mnHK*}!@r6V9xAOWf{gELV{)rx} zfMIjW>5%(0OlHMK2Zq0Fh{OpM1P)ad3RH(&%>37(4Yf>jK-bcb<+bvi7^F@d-#7JnNo!mD9%>8QtU8Sg%2V$UXksB~DA${IzCm~YEm4Pg60(2w&*2G6Z1g^h+~>xO^uyA!*up; zu~GDRI%t3cW;`B5?6B8SvY-1np-uZFgO7wzOt*U{M`A?p}j= zTvf`x4dn(&&qSyg6%3b_w)P|wlC@Iai!twKFmKv?O8U0(>x=ws-4wof#qvRhVIbbV zzRqP&bB~5j%!1%_X_=t*$RXfa?kaJ+3rIF;Jq>-4YEewA9W?08CA5t|y7y?wpCv3T z9~keco)oJbc3nHU8REKf#1(IUz2YM6A9LFSWz~Tb*&}v)G6I~Tk$S*z5NKG$EMBFd zzHf4E_nkqW?>OmCZAe<`m^S%b?A&;E%{Y!J;zUu;K6_lItnMsw6RX_=I%sWil6X%D zx)w9t43iQ|XD7Id`~M|+1*cq^9$Y(?QVEmI;BIo5ZuKA*ERs^?Kn+LW z97M1PNnyDXQkqg_^!D9)A1?boRbb5iObv06n26FeMNx+lUDC`+)3$Y?`<48I7Fa8l zRK{R178QoT7T^A#6aOp-6KF2VwrC8Lou~mOYxFpn`D2%2vF^5^hx3TL-5WZZ*|Yo! z*Dn95c?|}x41=3MWb{`t20RN z;#;|G^5yp~t=BI>%u;-!9#88M1d0x}1nTLrc_}xWZH>0sebaK2`?%us^O%7q*k3!n zBJxY>yf{;NbKGRx!7)u>qG&FvE_Qz}4-W1*8Yj^^w*bp&?A5L^mY-(KsK2>I=ipm8 z#$^HGho$sY2d;Rt9NSEPwxK>E;7*VqV)-+EXyQru4#l+pP7j#flJU(#F!=U!>WmD% zbI!FSe~uy)RsR*vu~BIhK8L7!R_;ePWE`W=@1sHNn2qbEq?8%#AN>}i=_Q0r;ZsL2 zIk=)XY-8n1(HlDH(UxMqc5g|gDv%PkhUH(4%b+F8>cE@JOLRaLFWHaE&;F;T zod87(m~9WNqd1<`0bw*NuFLHHRby*iY-XR=DyU;JUegE(LC&T$F&8$<6I_DA=9zk6 zELc$a%&%m3zVqVynRQ@5B99dgO;H5m!iKfrbu+soeQ)A^@aK;vU$#pJy-LXvc$GfN8%&}%NA`9Kw}vA578hAtlp+2Jw)<7; z${I?|HNI8wWlYDbA=R`UtAYx~w1!ijLL|AiH|Au#@2BAyz^l4nrnhd$>RApNHilvU z=t`iaaT2vx{rw;&oX~rd!)jC&^(^D8(ZB3fDl`#;g<|gl>j`THY33R< zS)y(-5{qAvb}wrNS3HyA_tzxFQ!;N7ZRfxKQXDcCBlGimPvFgROJsS$lc9-OuE(Yv zEsqw5d)eTEbgvMU9PV3Gu3L+$)4-+-HGIu1Pp2P81+FQM9`-r?rC|R{NR-)a@BCeE z?DM1e%`a|hWqLnY#>9i#W_@?C$nZJoq7=SZvA*ATr)KL+?->W?HkCqX+@N^io2NZMR))`9%LM*oRPWH{o*!Oqd3gkRWc z3p)NtcH!Bzlc1VODewP>kd{7%d2CP%C|Z>Cb)=E&c{-+>lm{MvvbW%!{slCF{GK}E z)}mBGBnDEj5eW{pwULFA0(qr^Xs8ocwizrIqsELG>#qavcG}kN(@do)k_C-Qc8q_j zmhvM*hZtG=CGJ}w?s&di(uO~PUUC?chpRZ-B4&YtN!LQj(^NX|l5weh=R8L0Az# zirT*uuk(?DHajSP-gtNs{nwUE3U8>gdB5G3glBos%OkA^W8rT_Re7yu!b|6`Tzfx< zB;-uEPcQUT=U;@v~fPZf+P*yFmqFTr2ZZdPdQ)tJU)_!q$x@wQHhIj?73)OTrQ)YjhK0 zr*uydbP%VAs(tlvq=B97L}7xFGHG$$Rhg&TR~+lVHdT3?oLt2O%MHswOc$sERPwAb z%PO{=>a9V50%r4q`~h!g1hjrZAB+rUzUt^d_NQ7n?^$c>*aG!Y;~}n)?jt z|JyIP`M>>w>-+r0d;n_PTFer5n3yH7>Rb5O&Hx4sp8zH$!sUKC2n-CDt>1hjGB4aj zx8=r6-WLI?(`xHRG(5S@WID&|X2phT6Ze!7HInVmvV(5()0K zkw!Ix(`Dc`M~28hKmRejlOMZ53~xAZG_A#-_|mA_?#`J9(KS@y@)X&9%uaE;?vb|U zwszM#+~i!etV**&e0{ZWs`FA-~MjTHqat8l$oQ5M?Ujw`}0vl1^~%`!^EWgNn?<_kxvNeTg>bI20*1n_=f< z;$2iIh;gnfy2DMTzQJy3tk~@>bOJwi?=+y@TEEP2vDzkIoi^_s?%uJYYIxXiO$WD} zLd!HIZat~wx!0@{$$@bE@R{sfv^9G1Eag@WmYHPO7cekTxS(>s<4L616*{kEmtLq@ zl^&SIrK%jFXIrA?n*ZRFr6%89UVU!ZV0_myyXtjNSybeHr%xILbv90PI`*h|rf6xa zrnKgb+fI&`S&C-EgovV8GgtnRF@V(hIZnU32beA5xStxsHPRt`kcrImUE31i{oh=G z#Ht|u!_t+y*n(8fMLP!h&Tq5m()MT5EhqW?l_Srz!8mGSVguWc0YcmO-Sirdzje>9 z&k}NRnP+AZ@4X|rus&(=w`lvd^`-xx(%w3#u5Ve_CTM^V+&#FvYtZ2C?(Xg`!GpU5 z4ek&$xVyW%yThIQ_TFd9Ip_Os)%}B7wQ7yY=&7Uo>9>29ElR_SNG7^Up6sABH0=X% zI!m}|g?TDql;ZPAEpRHSTCi8;p5m`;4{@F|I~+oly{j^-`Oz|iy-?T5U%4g|J=bG4 z*MM=mU-X3w*4~~`wbcp}9{h>p%wD!!n`qe7S)BE_J0_{4zEuV>A}jMsdvtM0^myc; zi1H^pZ`O=kv+AImW?$H^3ag$fvhd?oZhPf3-LpTF$6gCPU5WYdx8)c#)Y=(eM|W7| z_za&4yi{bs@JOmwRGV*Rtx2yQoVH6xb(hMaJ{E*H-+W9fF*j>X|7pk)hpvx5iQ5i7uY{KBBG{_I6t>F`vj0ItsDY!({s|Ym6a7>oOj8|iqG2%(W`gqm0s^7 zcM1~`X&^trpIWo2JitbM&}3FCEI)qT$#|8{h-3s>ttMhYKq1cX6&wOWs`z&}5L3M$ zvtPiKp+5;@AU!B9Z_s6}gJ?Y5m6+l$OeQM$m)EI5YS@dTPs4Qj=Tay}S3}%>+DZ~> z*D$egm`FgnCe2n~h=FWYAUR(2eT9yC3m@yGIkRP_`(CBh>7=T%6W72U90M))x}=av zzGS`1k>1zBz?^EWjTf>;@x`eZ*ZEL&R{nO>S~<1c`%m}a%9(7v^XQ*32i4{~`m>g+ z&+mUB@c6nm7ZRlnBSe&j+*aDdTu`%2CJ|%sX<0q9%P7OhdA~{3-QNAaZmrQR$4!Nw zl|pdsp@nUV!)QzAB_8o4F!@v8ua;L9)!nc4V65;h_kz&1vnx%}Lk^ol#YghD>$uTI zW3I#`H8mQncaqwlNeBtoBv%Ou37;>M+6|9p%Um>DjUGaiw>y&?Sd(fS$$xfs%AEsI zp+jS7ym_6?xth2ptxanjEdL%9UF`1$h>t|hMrcEO8Vn&{lWP11C*_N^R1%4Tdlvh} zWk_amqJBP{H)gO?x>Plv;_OrBm5&kkU`Yuz;&I|Cwp=4AWpWs{#Y)&Kvb%e_ro-V- z((H7{oUF>aIWi?DWeU4jCzxu1L>~%GQAHSC7-mc5M}iOUBG|&!hEsfxXJ?w27)YiZ zvb}lxO|*#Px2tG(dlp}m=g4RU-7OUKHED1lOn&fiS>?7wC@xh+vS22HcfDglR&3$b zyqwtw!$F=&Ob083LqPP;R-U~HUIOK?YO?5SvFgto747L&bBAYC|grT zwNQ>S1O(Bwb{3ygY7hUyF>SZFo~9nwYo!U^5B+!@7Ce?~e<@30L>63|&zth>68qF{ z!`10<1iy~e#V{^BLd3(AL2SXi5hhaD7g>|zzhX4vsKr}>M4g=5C08qr<-8}W2z51U zLr6v8ZyWS_dA!!M+DjPe7O;cZFxGmvI+V`g&_6a-SXmj<*4EZR0Q5nTk(B)8^Kzs^ zGW?NYf>&6yPZiqXv~a%&S?W81*#l+urSgVabK35Zj}|U!i|nE={J19`e+Y>KteY6# z=Oi@w#e6dB2lwjB8b}>A36=>zctX; z!}fEa7K##f=m#UyfT@xC**=LKGgJ|$We-CjjNsJx7xs$bBOs;qB;F`bhsnd>@^;2D z4vda;&DCdu1*q8Ix)xJVnRlV1h12kaKSyGXwL1Dc?9-PGlllCL3J%j!EaHv-$ipl1 zM%6r>8*f#SHwCq!jICB;KcF}#yF3qts9ehMp|}BEehvZ=c1o?CG7ZB60<{@_;ulkm z;|%4jNeR0)=LEiQFTb4QzO`YhoDLIe2oAAPVGNQ|&j&VNf9nXaU_CS{hOhuF;pTHw z_n&cq#%;7~4`(v54N_dE^baCPtu(g*2c(|Zw)H^IA;;kBPx<9k|CvOIrkRLL@H_*N<5Ir6!YdUc9kWO@8nW^t#t+nORB>%=xPqBkrfK{_ng8{S-U@K$_> zB0&TwqIH~eSXtpc&wd_{bH_UD=|cbNUU3Q7;YjnP;fSa~rK@gsUypMLhnEnio@J=} z5ocW38;iljWV(Sv8jI9frGhev;Tm~u2y0|SIb5mx`7Wov(GQcDPR76Ki?cGzT?nZP z1wL_0@3SE?)F5QJW@=ncfz{6 z)C9HHPP-~+Cs9V!QG3&n8ydXC_ozv~Cmgc+((P{7(P?Q^Q3u#PU$mJJ5@#wqJ2@O% zt*GC;+_ZNI<3pe1%N;)bjI|;rhqI)k&b_#VPFN2(FIgp$e){8K_^sE<42Qizt5KXv zOk7pC6jPenp$oV-RdjUl)7WhA&`d#q6ZhUhg@T+syP*MCzz~o#`yxfBa@%JaBTQk9 zg$cjh(LjoLKt6N|fH0xe_%~0EHj2^ z(b=Cx03J*UJYNrjdm2O2}Bt6q~M?X=Lh#_J3yfl;`QyVysRuehN&+XXa9TKpaMS) zTdFxPeKInawr;(>bD2uu##Q=Scm)4tOOkPTXi;1_BH$%Ih}pw)-+*aAMy2co^#xU2jXEAUvt-DT_WY>Ch!K zQ&#KckAH4Ymd@r0i1b93;Rb?z(fFaKm`V-ur1GUS!WO_!IqyQV*0rodYkCpnouaCI z_9Or3&u*C&Jr z>M+eX2Q8yutH<*zZDDro*$4mg;&6=nHwG=vG)L3rleoL{c353#U| zO3e(8COt17DT{cIzAAhPo%JaCr2xHNuv>(y;Y^CH(wQRyFFq}>t95s3i>KHSZ3N9K zpGIZpXih$%*Tt!5UBvALhr@Zeov`z;ZTDbDCHHvYIK%$SVjrktFbZ^wx}023p!3ar zBwm~~qVDLLSV?N88I@CTLt5&Uyqr5?SKP{j<&iU%Ey1c7&Xw&)CHGFzWtHx< zb~^UN>#2n`Az{DJ1U~NRx@=;nXk~H-6&VoMXv3*O(o8j6+E8Y4B){*dTvL;il6Zs* z=7K651fy0W-#@DJSxb7c79`uQY@)_trgD7w;Na^hlskZ=ph=3 zN9e0~p3l1^vv^;k%C~}G(JNJ{kz3-?81Jk*>>Y94l=il}QIO-K+&FAvOV-AwG^7b0 z1eVE_lx*%79X2{4fYwzC2Pue~^*TtBV#H}Ii_2Qz;|9`VWF*64!ZqNS41R&bdK=HS zO^e+3pq!o56nSL6xwUHk5senWV@w}KZ=#A?GMt3-_%g%u1c#P7*Zhn0r{turjmRre z?c=073Jo`B);MA;6uAbarZ59-9%Y$Oc3YISBeDXsAShj<^L{?c_A}& zFK_hdr=KFWW&6bB**!vs3#;G80l%Uk!DA?bg7}Dt?aQA8 z<&v~)sS{fDUi>Lt7(`F-3t;Q9XJF<&52Ww46Q1Wi6 ziItVe)8N;$KC7nSenMpA4zIfvQ5BW99@oWMvq?dK-!^mrCA0^prv)`N4&84pGyuZ4 z$O!%V6+kozr>E8}+H^^@1N(*V0bqe-3gi8qE2CFYJUtQVF!r~?DXC)X_pWuwT|ATE z|0~u0ACdJt9{ekYeUI;eK5zdE)h@x@n(P+~C%U#ScH9Qd9H`P$+P;TszhJY7?%iyqcZ{e_ndf-xysE^}NW+ue<(m1(5+- zqv;IFF6RfB#iO1ba{!QuZNb@yZ8|tt8PwgJ9?~Ga^Yu+4h)o`8J!3QvoWG*e_hsLgVe;eNk{O#(xbOxcWsSmSgXi6ou)W6U(L1DfE3IEMX*%wD8I8P;!+YF@yOrK zAJtcVlP^Nm@WwownYJrF^;-3Q!9)s)m>3pc5m{IQ+iC7)ovcr|xP5Y(o@pKP!^P~@ z$~_{vU(??0{-u!4=@fQ%wMomiSa-w~;ZMSnRefj9?sSOabk;snFDwXy%bnz8)5rNDN-IF}`6Xe%?g)AXtK=qW#MyzNl9$59J@~N#}&>QB|3NT}zxrNqLpM%982v_DF4d^{wQR`GpoerW0?lg6SC| zYXi6h%QZzIDrH6(dsin7E*V}AoU?Cmi;A67OMH1yLuhXr58E;4z)U0m(BdURkUebqi?wa4Kz zR)p4=4>>8n325n@322keY| zd1nlyUxH+FL0fT|6VAHyJc1*-15joyhQ&t+Ry_iT7nRYnEoq)CZHXi`-uI zOv7^@V_Y__d89tNa|I-AjYE`b8+R&QGQm)3e5hQ?>eGF|#VkQyXvjf>`%^JtUD0C= zRjF`xu5f$H$%uB=rM-hY?~Dxe-*W_U4#BtB)J8%!sS>pcYD6_0zL9^pCjHF8{3l8f zV#>pcvle z>|mAQbP^@HXFO_Uf^$2mjCo;+L3NWM^0q0;-CV0fb<;TVxpCI-@Wucqc^TQP>YP71 z;4#O+qowed3xq+HdT%XUZcjH@hr_^&*IVVpFNMPUv!pBn_W6FvEt=wr7lNl(jKQ$U z28xj}UB4amsqY>K`{$eRET5%0qa;0pagA)Qyrpni&d$D!phW!Cw4iDD{<}aIPqiuh zjo0aSrp-k>bc9H5i*O*qBtYJpWIq9gRM((bw*fnitLk5+rQu;~4Gi(GKtvsVix)_9{*FTGCD~TChN{@bM70SIZeaCj@OH}Lb8_5&3>SS3wig5VO z^dJ{4FDu&}8!m*`^B;~Cb!dY)sTYL?kR*Au7YGu`WIkSPQ_eV-_irF77 zc*E}~_^@O`uOjb$foyoY%6##J%Xq zvIhPd{JdvJ%A7ygaeStd>3?*gyttq1A1>bhTp@YW;aZ{TArdz-ws~ zjZfoBO9i8>E6==$+c%%CBzdB`w>v(Vb4*>Ej>8CZ%c#H+U2^bo%w{pbYj{bPVr=jy z8Cgg~Ixe@-$AZ0QwbWbYNDO1n;xO{4F%zd%+C+Q2xhnlb3ynV2H9tZ9+Dca^NDd}A z@Fx#bmA@E%#4kFrmA--zkxOM8!#NnR4sSjlZS+E|xz9GOb!h{h-nbkkO!M_eAN&t; zw0Jsqi`CWX8WKv4kpw;^gKEYaC(q+~-|H2~WYNOeaba4;w>3oF!VRFzN;m-Mz3%U5 z|C(zl>-z7+e#?90FJjLQu<`J}(|rKL4>Lpe(*r$$LkG5-@r+|L|LTDuY5q6f{debR z)A`4Yq<-gn$BqL4vLk%7J&2XuK@tc(TLiLZ;|GrKPMXJA>pOYaf@1{4l7S&12J{#o zVPX{&J`%QB06lv1nw!(g%V`A!1%K~l4lwu5 zjqdfp(DCtcF*)o12TBh*o+95rP{SbV5~4`n-PxJIx;v6A8T8pe+ZgW%eKZDH{x4vE z6ega(&yUG*CGhAIl_@Gv8q{&nM<_S^y$-?oV=E4Myj#PW*IT1Mm=2+J-;$B^tAB{W=vO;&(A&IN7?KHRtAH?jX5ghqK}IR#6kIF= zy1a)b+lJo<$5;;!dRH~`si!%%`Qle~tXtN(Qgh$WG-k!on=@M<1{{@2i3%sZrZi5B z6XIfsFIW3XX4M84lZp>g9rwmQ>8q4~@oGyb`p9$7dBL9ci+W`Qy5aLtv8TOLvzz-6 z$K&=0>0jXs#-ZFP>&4#4Js_m3=PUpQDuc26QS*nH@ z7i4$hSW7YT4BnseR^50jQdd%1Lbf%7&~g%%-IHQ}%)5+#6-Z`i?nRjy@jeWp9E;vM z6y=Be(tdKVMB&Y9sp!to1F*kli|Y@amqV578@TB0_jJI!fcNXyuPs1fKX5%Yt}p{V zkBX5%OwDo@&`10t8M=k|!?8Q%>(7mwLOK(gp*)m#OZoV{g@k>g1yo@)ZVR?3)CW!{ znzXM6w%g;D-@KL(Gc|lb=9FYoYU(3A9Y~HYi`hjnu4Z>*(a?nzRoP*9IWwF;Cnd<2 zX>U2SQJ|*vNbN|3(C_T&OO$8-ezeaDiE?v;f^>&H1pXvTmtSC@$?R2qexs97Cnl&J z5cj1*!@IH{T_YAmD388^u=FJk6iZ;T&UW}Ew9CeY07TTF})vao>my$WE(HN<3c zMho^ts<_h&>{c?mpk9q21_m7gP(6J_>6os}(6)vYTZ476Qb^OgC3aFvDPom-j;@f_ z9Pc3IfLJXTT7{XD$2uJj^_bS8t$2?-B01UA_E>+<4>@4R+8U=#l!P{^VRZl^`zio^ zV0j5N5V4steCMhxxhs2Uf#1lCVy66cn!{jKy?Syp^m`b8G`!4+^z4ZhDS|#B3soFS zNy^7ocozrMCl{s3Nw(WZXa+nWu}0^xJ50#Om(|(X*};#Dge2hY&C9^R@O)F@lK|ZQ zq^U8OXt4^^>t^evd!*L$bNmP4sO+G=vjaD5Wn`Sa;>iwt!lLhZL2O9nM?wS4*y4n{ zhU2hWs-&v}CVo+*(41ox05OUV#d~?@4fTz@@g$r^6pxpqnGmAfaw4xM6rc+dH2QV0 zvdc?DU#t)^(#4Fuc?s!S{(PA0UlAaLmPnBSTRKvlE{9CqqcQSUB?|a0@tu^E7G~^6 z#c*wkCW{IjO(?9ob%#a`4a!5DG!FNhI<^S9>ns z?`Hq*fv6py#0ZvFL}*sT<_LuPVpcmhrXT z5v6G-E8vbi@Hxq4{3y=i zXuO>Ok-5qThSc4hkrTYht@uD1693&5I$4fNB++u&$Ie5ziS5xo1=>O@r~FvE^*qHW_?r?F80*Tg(0`vp}_gk6NKCgBEcQN z^}>STdBQ}${?4)I49!G_Fp1v8I3hf=$gv+%=M4=r@)n1oSg5FKsR>>QYK8*s`?gc5 ztBmr+eobHZ`apac4%Sbu045|Q_oIrw>P;_KDb{UhT&OL-4;XMv81_$2^0Tvb>Bm7_ z9Aka}@fXd=UdM^^ozC$JttJg#Cv`;?Gf8lD;%-M~eA)86M^70VMger@-4`CfJD5_A4 zaz|D1irPWJsq^;sVvjjG%YSk%^Hx%ik#Rlt4&@7eZT5Q&=XGJ4F&a@+88vvJ!X8kc z@on8#Kbqy__W%aB6SxXw;77? z88Nx4YIP(T@eg{_N(2PO~+l>iAse0-%Tbz=Ez{=$Z_zJe7sw9S^O)SJn= zZ*COinuz5>$~`6axhV_-Vw0TvH-hz}FWKF(@s^!(CqIE5lTs5fcSp*AzDKSW#u8)Z$MKaY|{OH02?;X;_NsOF}BRdNKQP zGLU=b^$QcnTE+@n2=uNUyk(JftBs^Nz*fUA$~M;BJh&t$6Ln$n$;}c(Z;k>H?Gp zusqYQjzaq@(=4pAYE!+l`48JSO0r(F0R=(`Wp}a)g;9%iD5kvP`ApxhmpwgE6PTM> z;zLb4IkDL%CRt9m2;R}eDFJUZl#V>7hK}EB+OE^Yu4VRhP?us!QnMTu1hh)ELm(*SGj57tG^f!8=R<@`cq4|`ZL&4lA}`kK}H%rc3bDYcQ&VB^u1(gXG; zjyz(aab*Rz&X!Q#W3to#>Z;_^H|!^Mq-;;(h-u- z38LYN&fkDZtw0`IFrFSoq^l`{(*9XV7p^pPHP2Z|d~d6VnXsn%6$uo4b}z*4=LV$mOyjS;(l2sSQka(!HWz>;eL}NQ_CdRc(^3 z-W)4RD5#W%W#WWfJ{*o5W>cWPtebY99sM{+GPXl)-^B!5-FA*#Pw)*0Afq$!@~mWO zJ8BQaQ(_uGL-a^%_1iX~jo*@qg@!+p%AvBLT)Mom4jX$T!L$_H?vdJ*Qs$L+sw6Ac zj~q9PY_CI7>~NugXyjde#3cC-^-+87kt@K$snJq|2-=*)4Yfzpwh!;Ft`m{%(RB#h zApVn<3xpoQq;z#YY&h1rokKVVs>y$o0TDF=a5>@Wm-jd&PQ{fOnu4`dPH!J|-%`Ph zmdfxA`gcJ2dm+SqlvE015b$ObBtSlthXLinBFr%}`B`Au@cq3ZgYgt`1ub{)MEZ*o z6a+x2Y2e;5GC!a%0whm>7>E}#9D1Ye=`^>b#K%^b5{=o5_o;q)&@ z7niLY_ur0UXbaGU-7Z$Z6ahZbWK$vH&`kQBEq;z*Z~gDD0l|O=f}3~d{5Ki)yRH8M zpaDnW9b^AZEcK5eb0l{uh{&TVI$l8qW(I9`t3dE2ixTv7+AM=5^B23edkPo^`cx*f zSfGXdj5uIPEtptcec!+-1Ts~&^cQCLoua(<(#6V|c2%XR6J>`|k7C{GVX?Z{NN8bU zb^4N=5@Gi?F~FV#CYU2rm`tKL`a#IU1Pl#{Qwvsd%JhkOtsq!SHqb)Qh!VSnsB=_a zR&|XQ!W1`+qizML##*$xmN@Ll*P4ni^2#a^ZCaqq>P?}}+TS>T{g`vxEM*=vUo~0s zmvsY!VGt7oAcia;v|=tNa8R?gJy+_I2iW*6>k=9tKRt-I`=U~+ITeLZE{teJ?G2ky zL6y~<{-Mt+a@fp8OJ&ICr10BYZBqI4>pSQ1#hmq%-=oMLt&N&xuC1x*VDH>qU!MU3 zpWy!5MpZ*Iynr0TgxBvR1ymf_RS) zA~J&y4pJiLob1Zg8Jk;WFBMgJ7w)T!)CJ%0=)t%6d=eKg1=Csct1bN!z6Au&*+s;T z2;<{3p5{6UQ%WC*G5L8E3ub=Q=_p`-Ze+TV@T6DzJQ4f%cc+b14eW+haG z#zH(gUwx7?f)FD+(l7Y*e;!E1tE<_sBRS}q)GlTzltIS3Z%Nc%2ubGo^;bPwY|;n7 z()TQ12*bh$t2{m!|FB!9vAjunHjIW5swOD(O;@C1f*YS&?Z#dhi>jpilibhbHWrhL z(FLZ?RXBqe(b1JMja#kT|CMUtFw+q@(}+do&3TZ zvB!#`i|(--g$Ga8z7~X(rYQeq&{7}joV;_mmargHj^8LK?3E@;8^Rr_3WHI?C&yHS z1^pHTc22z5I%Bz8WI@vwy0yIwN$#vBHl0B~AS_^9i~opEb?~4hHgpXu+F!!->)53x z?5H65A}tG^w2j2q1{f`H&bF$DRo{w*9b*X0rlN+5%8JZsIK|MP?#Y<(X93t|{h9lI#$d z#C3WX-D5W4mh2wjgdm&mdryta2-*n{Z3aj)%kTW{(1LH}j4`m$t4>Cesxn{UmTD|6 zu+S?>_l(3NEA6}NS_1W}dkqv_syDL3_*_ZDfz*VZ=;%5F+Fz-vT?GNayN^w+iAdSA zpx}i=5Ia2Vv9$zC`#f`Z*IguxC`frD9d{S;!Z${cQ%YFXvvsL76F^_&SudjFsq|E`J|H#Sr zY_g$eD=W!WfI26zq0#K_H2w}uZ1@}q!M6&3C#OB5L)yF|d|tX%^{?qG- zM@$yFat>%w4~hnQrOO5A7kJ#CV1Ft$s^|1wG@F09YbdE#oC;r9G@6$bHGs{Vu~4rE zq?3iJL@9X8^l(k<63_djK9WM2)6ow;W0NpM8ZqGFJU>8whU4yA<&B$$sHd{q-TO;e)Lmh(Z7lKfpO5YIvprDBSMElPh|xsb07Vq)okn!D=v4%$o5@@aSZ1X z`^&>*^lr6+jmifu=QHZfHE|<9h(p{pp`(TG^sl}hCLs^1)X8}qZv>z z^e}#Q1tlp8K)Ec0H{?Q5=U|9gbFu%R{N@zKnhv#a9GYrY-iXB>Z} zRclxh&K)m8Ob0a?ol#=ub0_mYw?8(0HokiPahvlU!fM+N@sCUeK1TINH9;_xa>*O^ zv8uO(9_%jKNvu}fTpc?W-F-zwsj2g57rD6uq|DW4LY}VrtsV~vtl71-W+ZNb#X~@H zKz5>vUijlj)ekn6(hc3)DU>CT3WcMKxgqA=%X;~TW0|682_s<<$feamewu+VC7Lh2 zFs19*W}mIHdA4!Mx~&L-McIYfM~16-G3>BN|0+1Zb<1Hsk@d(vVxt!J3(b$~`ua)E z`d#gt` z_@$XBWTrmySk=Tg(^(TAO~E4_8|U(%XUDWNtuYwa<1g^<;`z-^nIR!5q?)t1f7Cxc z{gbkdMlV4AN1JitOSV2TrDpI1L8YW5b(DC(UDvY7wH>hE+DSJ;v(V`F912Oog9p5E zn;c2ZD{{*do9ACXP}e$f7;@JHpu&T1opH$Y9k@$-gv^!P$^_Xz!VrRdR6TtV$xZ>X z1qCVp$QF33OxHCxduYBT9Wb`9jjg@Cx_p8RNT+wnezb08_D9ekpV1T91yTmWawv;S zOBYWsTQH2cxVR11r?G%XN=o~1L`NFGzOMW1&6nkMyP))DVdi+Z>;Mn=NiO?7!|%#} zpn!%FxU$~e<~ZFifVh6xFj?$fR}q(b4d^RK>$=VCCO-;D(xcOAt^@5~UzC?VMi`hi zy#EM{mXG_}yBq)S9pk_I&VN&K{Cf%G-+kxCxPG6JOk!HiMuJF!ZMuXOw)e6H(u(+a z6fuywXgHNU18#7!60-yPFdo) z(eD=oVqP7KGVozA4vq;dleFRY$n0Udnbu|nVF<@C`z25RM6bC(=iBiBn|tBUN*Y^A zPk$&1Ho*UV-zT*^Gad8mdsR3fqy9<&Uu^$^mc6TC}ldpSsjbkMjV|-60 z@`m?7-+c)`!0u;I*CowHNcmneAG1xv7n%9<Z%UYt8s= z&rf~n_bX@Yw_+}DT@$cw#yy@t@tN&?LZkTC_KNUZhdS0?f?)sb`M9SAnDB0F#_*(N zEf4zeF6abDOE@e%C502k+fYUqa7&N(wHw(iMZ3~+1c^%HZO4Ug!7Ah?dbuNE*Yk?q z#k|PqH2V4XSjAg%_mhDn-!CM->E32(<4xiTc=mbg652S!3%8qYRO#?Yug12^zIeRd zZ;s!!_f(p&-|UyqHjmWjfsFSIHSWRBBlzcyEnYl#Pu}QH+qe5!&;Cw)?lJXz$2)a% zx0fOHrrx!unBMlkpNyLz!a4rvcx!Z8R3w})@p*O#4wR_Y*>a9YmXt>^sYH^$v=#&c zc)G^zY6yp$Nt`nUPv-L2nwRqyT@ycpKLZW;_G{~L*lwg+$2P~ioM;irYv&=+)bTEj zUgs1%)w-AaM1q$C=(E4vHZHF!R6Ftstf=Slok z1h{G92u8SXKcjDjLNE1CGmN_^6b8u-X_ov^0OVL z$lPzoF)?S{)0}yK+7+dd#I~Ya9edaervnmCbo+QDLqcORq)jKpWJ|uMe0G}#$r(6& zhpg=jRSvDy4Hp+=I{Kd7odGW|!ov&@`$rz3qSP-rX2ou-6Mi!H^Ask*jZy-+_rgdm zPabSKpm6PPJ`YENXxp|Q_r>cOs8pYW861h_m^W? zSBQGlnDe^<(NcZWxpr&EA+Dc5z$A>UM8iJIq3+e+VZjKGB&Cp7b|1K3*GH&4mikQYFLM^S z8R0@RM~KkV?z=>_himW&LkWhXQ=vUC8AikHDMOd8(ImZ-Nm{ey7Vn+j=y)A2j+i(n zy`RVVD+X@%U5}xr)@`z` z&bshDJiVFwyj>iEy=aT2Jdsp~a%&1N25J{1o!a_Def+oUqV+Q$;qknLLatm)VuTep zZ|Tpcqlp^n82zKoX}a()-;nv>toCfrZW`>@oU$hRpqzM6UR+6`bMDTk7I<1TE-`gR z(uNeS1laRNwyR*X;%?YD-sw;>ctNW7U^E`w##L(E3$kvz?IhZ_UBoL#TdmCdOM*nv zi0isvp1h|#;2$*ue@zMaP9z%3>`Zhoj9;?0yW8A%Zya3B#iH_MiLT;y39VPfzx*5& zH32P+)aidlASyQ}dou+~aKzL3l!%bvGWIPVv0(sSlKbYxTAa;G=wO^J4XLBThbO6Y zTlce$%NJW;zPB>$MDFe_ajF?G2kldzV?w+>(TTN356{tAuU?(3N3zVvth)=LGmbFc zyD6o)o{G4)@6;oqC8uLxaREIULcuBB83S5||s z^y60-(b2!kAJRj-n-Z_{7Pv3_En_F=E^|X(M+f-Q^`3L-bk;3l&&S(eLxXDYH-0Qc zBil5Y)0;2m4rjq-d0r!y*Rw7Ld%o;!XWc))y5co}fADgA1B*!POX2Yf%<5CT4KKp? zl3YtiiN${op;Y;!mgMeg{Ue{xb2C#*iD%ZDWya8DUqKCl8|~SyXI6!D<_239-0^Wu z`&C-|!N8*%ik4H>1;e=doy+TUfDb9%Y+p0JCk2!c@VG!87N%SAy1S})d&L>!xx=w| zdm-a~)n)VHyrRwIjUG$dU)5@LqnFlUF5E1vV3%3?lJT45~dC+4$!zbGM7@qSfXdRtI=Mrk{ zTI_B}T0Nrcy8U_vT(ye66o?vF8k^u)ao}+2vx+=s^@Z?|0gah^L;0fSZda2S1uu3T zx}Uet#T%)Gjkm3ado&~9A~|}xXTP0>kH$6Y>yuD>uUTPYB!3SI__e=`k61yv`M7>( z+Uia`dO~Z<&4rpYMKqCR84A^~q`xjA*gf>aw3X#%2%`0B;)xj@_JRfPdyb;;FpOsr1R|EINx-lf36{_Ijk>xCYxj zvS)YmiKZ@PN2$-*7{oVh7gU#0Sw?0o6+cb75bjg`x7VKhc3x7u;<0Q5CPjQd(1fo= zvgXoEA3*dWLHL39n}=fl;{${N41~ZK~f(^xSEfGY#oO-uu}9*6dgUvVs!TMIV;B+C5J#B4Ek$o0>QU zLBz#}K}ZrLN#Q;qF!4jsF|s6L0<-HwW|RAjK!W^zE(E5(rUX9zVckECd^ZcQ^&i#& z-(-KAM}QO=hL3p1eXXm3`LAvMYmaV?sk)CP4CI$I!L)k-i6RfTVSf ze)?-?|333S4S1jVUkv!KfA-hR|6%~J#rwhf=Z5bG>t8nfr{#Y?%Ku{dzijx=?f+xq ze{TODd;b45@xRRf?}q>P^ZL(;|GVMY=`SE<1sk>0j^y6Jd+b5Pgr$Yb1$6!Y4@yM6 AX8-^I literal 0 HcmV?d00001 diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 4dabf5cd..9580719d 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -63,6 +63,8 @@ import CanvasTexture = Phaser.Textures.CanvasTexture; import GameObject = Phaser.GameObjects.GameObject; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import {Subscription} from "rxjs"; +import {tryCatch} from "rxjs/internal-compatibility"; +import {HtmlUtils} from "../../WebRtc/HtmlUtils"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -100,6 +102,7 @@ interface DeleteGroupEventInterface { } const defaultStartLayerName = 'start'; +const helpCameraSettings = 'helpCameraSettings'; export class GameScene extends ResizableScene implements CenterListener { Terrains : Array; @@ -127,6 +130,8 @@ export class GameScene extends ResizableScene implements CenterListener { // 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 helpCameraSettingsElement!: Phaser.GameObjects.DOMElement; //#700 + private helpCameraSettingsOpened: boolean = false; //#700 MapUrlFile: string; RoomId: string; @@ -207,6 +212,7 @@ export class GameScene extends ResizableScene 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'); + this.load.html(helpCameraSettings, 'resources/html/helpCameraSettings.html'); //#700 addLoader(this); } @@ -544,6 +550,27 @@ export class GameScene extends ResizableScene implements CenterListener { //init user position and play trigger to check layers properties this.gameMap.setPosition(this.CurrentPlayer.x, this.CurrentPlayer.y); + + // #700 + const middleX = (window.innerWidth / 3) - (370*0.85); + 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) => { + event.preventDefault(); + if((event?.target as HTMLInputElement).id === 'helpCameraSettingsFormRefresh') { + window.location.reload(); + }else if((event?.target as HTMLInputElement).id === 'helpCameraSettingsFormContinue') { + this.closeHelpCameraSettingsOpened(); + } + }); + if(this.helpCameraSettingsElement.parent){ + (this.helpCameraSettingsElement.parent as HTMLDivElement).style.overflow = 'scroll'; + } + + if(!mediaManager.constraintsMedia.audio || !mediaManager.constraintsMedia.video){ + this.openHelpCameraSettingsOpened(); //#700 + } }); } @@ -967,43 +994,44 @@ export class GameScene extends ResizableScene implements CenterListener { */ update(time: number, delta: number) : void { mediaManager.setLastUpdateScene(); - this.currentTick = time; - this.CurrentPlayer.moveUser(delta); + if(!this.helpCameraSettingsOpened) { + this.currentTick = time; + this.CurrentPlayer.moveUser(delta); - // Let's handle all events - while (this.pendingEvents.length !== 0) { - const event = this.pendingEvents.dequeue(); - switch (event.type) { - case "InitUserPositionEvent": - this.doInitUsersPosition(event.event); - break; - case "AddPlayerEvent": - this.doAddPlayer(event.event); - break; - case "RemovePlayerEvent": - this.doRemovePlayer(event.userId); - break; - case "UserMovedEvent": - this.doUpdatePlayerPosition(event.event); - break; - case "GroupCreatedUpdatedEvent": - this.doShareGroupPosition(event.event); - break; - case "DeleteGroupEvent": - this.doDeleteGroup(event.groupId); - break; + // Let's handle all events + while (this.pendingEvents.length !== 0) { + const event = this.pendingEvents.dequeue(); + switch (event.type) { + case "InitUserPositionEvent": + this.doInitUsersPosition(event.event); + break; + case "AddPlayerEvent": + this.doAddPlayer(event.event); + break; + case "RemovePlayerEvent": + this.doRemovePlayer(event.userId); + break; + case "UserMovedEvent": + this.doUpdatePlayerPosition(event.event); + break; + case "GroupCreatedUpdatedEvent": + this.doShareGroupPosition(event.event); + break; + case "DeleteGroupEvent": + this.doDeleteGroup(event.groupId); + break; + } } + // Let's move all users + const updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time); + updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: number) => { + const player: RemotePlayer | undefined = this.MapPlayersByKey.get(userId); + if (player === undefined) { + throw new Error('Cannot find player with ID "' + userId + '"'); + } + player.updatePosition(moveEvent); + }); } - - // Let's move all users - const updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time); - updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: number) => { - const player : RemotePlayer | undefined = this.MapPlayersByKey.get(userId); - if (player === undefined) { - throw new Error('Cannot find player with ID "' + userId +'"'); - } - player.updatePosition(moveEvent); - }); } /** @@ -1248,4 +1276,53 @@ export class GameScene extends ResizableScene implements CenterListener { }); } } + + //#700 + private openHelpCameraSettingsOpened(): void{ + HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; + this.helpCameraSettingsOpened = true; + let middleY = (window.innerHeight / 3) - 275; + if(middleY < 0){ + middleY = 0; + } + let middleX = (window.innerWidth / 3) - 50; + if(middleX < 0){ + middleX = 0; + } + if(window.navigator.userAgent.indexOf('Firefox') != -1){ + HtmlUtils.getElementByIdOrFail('browserHelpSetting').innerHTML =''; + }else if(window.navigator.userAgent.indexOf('Chrome') != -1){ + HtmlUtils.getElementByIdOrFail('browserHelpSetting').innerHTML =''; + } + this.tweens.add({ + targets: this.helpCameraSettingsElement, + y: this.startY - (370/2), + x: this.startX - (400/2), + duration: 1000, + ease: 'Power3', + overflow: 'scroll' + }); + } + + private closeHelpCameraSettingsOpened(): void{ + const helpCameraSettingsInfo = this.helpCameraSettingsElement.getChildByID('helpCameraSettings') as HTMLParagraphElement; + helpCameraSettingsInfo.innerText = ''; + helpCameraSettingsInfo.style.display = 'none'; + this.helpCameraSettingsOpened = false; + this.tweens.add({ + targets: this.helpCameraSettingsElement, + y: -400, + duration: 1000, + ease: 'Power3', + overflow: 'scroll' + }); + } + + 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); + } } diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index dffe1042..3354d931 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -16,7 +16,6 @@ enum LoginTextures { arrowUp = "arrow_up" } -const helpCameraSettings = 'helpCameraSettings'; export class EnableCameraScene extends Phaser.Scene { private textField!: TextField; @@ -35,8 +34,6 @@ export class EnableCameraScene extends Phaser.Scene { private soundMeterSprite!: SoundMeterSprite; private microphoneNameField!: TextField; private repositionCallback!: (this: Window, ev: UIEvent) => void; - private helpCameraSettingsElement!: Phaser.GameObjects.DOMElement; - private helpCameraSettingsOpened: boolean = false; constructor() { super({ @@ -52,7 +49,6 @@ export class EnableCameraScene extends Phaser.Scene { 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'); - this.load.html(helpCameraSettings, 'resources/html/helpCameraSettings.html'); } create() { @@ -112,27 +108,6 @@ export class EnableCameraScene extends Phaser.Scene { this.soundMeterSprite.setVisible(false); this.add.existing(this.soundMeterSprite); - const middleX = (window.innerWidth / 3) - (370*0.85); - 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) => { - event.preventDefault(); - if((event?.target as HTMLInputElement).id !== 'helpCameraSettingsFormRefresh') { - return; - } - const permission: Element = this.helpCameraSettingsElement.getChildByID('permissionError'); - permission.innerHTML = ''; - return mediaManager.getCamera().then(() => { - window.location.reload(); - }).catch((err) => { - permission.innerHTML = err.message; - }); - }); - if(this.helpCameraSettingsElement.parent){ - (this.helpCameraSettingsElement.parent as HTMLDivElement).style.overflow = 'scroll'; - } - this.repositionCallback = this.reposition.bind(this); window.addEventListener('resize', this.repositionCallback); } @@ -175,9 +150,6 @@ export class EnableCameraScene extends Phaser.Scene { * Function called each time a camera is changed */ private setupStream(stream: MediaStream): void { - if(this.helpCameraSettingsOpened){ - return; - } const img = HtmlUtils.getElementByIdOrFail('webRtcSetupNoVideo'); img.style.display = 'none'; @@ -284,27 +256,19 @@ export class EnableCameraScene extends Phaser.Scene { mediaManager.setLastUpdateScene(); } - private login(): Promise { - return mediaManager.getCamera() - .then((mediaStream: MediaStream) => { - HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; - this.soundMeter.stop(); - window.removeEventListener('resize', this.repositionCallback); - mediaManager.stopCamera(); - mediaManager.stopMicrophone(); - this.scene.sleep(EnableCameraSceneName) - gameManager.goToStartingMap(this.scene); - return mediaStream; - }).catch((err) => { - this.openHelpCameraSettingsOpened(); - throw err; - }); + private login(): void { + HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; + this.soundMeter.stop(); + window.removeEventListener('resize', this.repositionCallback); + + mediaManager.stopCamera(); + mediaManager.stopMicrophone(); + + this.scene.sleep(EnableCameraSceneName) + gameManager.goToStartingMap(this.scene); } private async getDevices() { - if(this.helpCameraSettingsOpened){ - return; - } const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices(); for (const mediaDeviceInfo of mediaDeviceInfos) { if (mediaDeviceInfo.kind === 'audioinput') { @@ -316,36 +280,6 @@ export class EnableCameraScene extends Phaser.Scene { this.updateWebCamName(); } - private openHelpCameraSettingsOpened(): void{ - this.reset(); - HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; - this.helpCameraSettingsOpened = true; - let middleY = (window.innerHeight / 3) - (495); - if(middleY < 0){ - middleY = 0; - } - let middleX = (window.innerWidth / 3) - (370*0.85); - if(middleX < 0){ - middleX = 0; - } - this.tweens.add({ - targets: this.helpCameraSettingsElement, - y: middleY, - x: middleX, - duration: 1000, - ease: 'Power3', - overflow: 'scroll' - }); - } - - 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); - } - private reset(){ this.textField.destroy(); this.pressReturnField.destroy(); From 0b00055edab2f279f0375339d46da54e199a282b Mon Sep 17 00:00:00 2001 From: GRL Date: Fri, 19 Mar 2021 14:40:18 +0100 Subject: [PATCH 29/36] Use includes instead of indexOf --- front/src/Phaser/Game/GameScene.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9580719d..daa4d394 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1289,9 +1289,9 @@ export class GameScene extends ResizableScene implements CenterListener { if(middleX < 0){ middleX = 0; } - if(window.navigator.userAgent.indexOf('Firefox') != -1){ + if(window.navigator.userAgent.includes('Firefox')){ HtmlUtils.getElementByIdOrFail('browserHelpSetting').innerHTML =''; - }else if(window.navigator.userAgent.indexOf('Chrome') != -1){ + }else if(window.navigator.userAgent.includes('Chrome')){ HtmlUtils.getElementByIdOrFail('browserHelpSetting').innerHTML =''; } this.tweens.add({ From 1cfb22f6957365845c7462a1c85cd268f256e9bc Mon Sep 17 00:00:00 2001 From: GRL Date: Fri, 19 Mar 2021 15:40:07 +0100 Subject: [PATCH 30/36] Creation of the HelpCameraSettingsScene Update of GameManager and index to include the new scene Change title of helpCameraSettings Suppression function reset --- .../resources/html/helpCameraSettings.html | 2 +- front/src/Phaser/Game/GameManager.ts | 2 + front/src/Phaser/Game/GameScene.ts | 143 +++++------------- front/src/Phaser/Login/EnableCameraScene.ts | 13 -- .../Phaser/Menu/HelpCameraSettingsScene.ts | 96 ++++++++++++ front/src/index.ts | 3 +- 6 files changed, 135 insertions(+), 124 deletions(-) create mode 100644 front/src/Phaser/Menu/HelpCameraSettingsScene.ts diff --git a/front/dist/resources/html/helpCameraSettings.html b/front/dist/resources/html/helpCameraSettings.html index d1c0e04d..b1a6259f 100644 --- a/front/dist/resources/html/helpCameraSettings.html +++ b/front/dist/resources/html/helpCameraSettings.html @@ -93,7 +93,7 @@