From f8613398848ff7ff8a0c5ee753714e6fc8135aa1 Mon Sep 17 00:00:00 2001 From: Alec Date: Mon, 8 Nov 2021 18:26:07 -0500 Subject: [PATCH 01/60] beginning of large-scale redesign --- Procfile | 2 +- README.md | 6 +- _config.yml | 2 - assets/fonts/Diavlo_BLACK_II_37.otf | Bin 33964 -> 0 bytes assets/fonts/Diavlo_BOLD_II_37.otf | Bin 33460 -> 0 bytes assets/fonts/Diavlo_BOOK_II_37.otf | Bin 32704 -> 0 bytes assets/fonts/Diavlo_LIGHT_II_37.otf | Bin 33404 -> 0 bytes assets/fonts/Diavlo_MEDIUM_II_37.otf | Bin 34696 -> 0 bytes assets/fonts/REVOLUTION.ttf | Bin 18640 -> 0 bytes assets/fonts/manrope-light.otf | Bin 70796 -> 0 bytes assets/images/Wolf_Logo.gif | Bin 206508 -> 0 bytes assets/images/custom.svg | 107 - assets/images/delete.svg | 14 - assets/images/gallery.svg | 90 - assets/images/import.svg | 1 - assets/images/info.svg | 14 - assets/images/list.svg | 109 - assets/images/pause-button.svg | 8 - assets/images/pencil.svg | 14 - assets/images/pencil_green.svg | 14 - assets/images/play-button.svg | 7 - assets/images/question_mark.svg | 14 - assets/images/roles-small/DreamWolf.png | Bin 2286 -> 0 bytes assets/images/roles-small/Hunter.png | Bin 1117 -> 0 bytes assets/images/roles-small/Mason.png | Bin 753 -> 0 bytes assets/images/roles-small/Minion.png | Bin 1025 -> 0 bytes assets/images/roles-small/Seer.png | Bin 871 -> 0 bytes assets/images/roles-small/Shadow.png | Bin 824 -> 0 bytes assets/images/roles-small/Sorcerer.png | Bin 1102 -> 0 bytes assets/images/roles-small/Villager.png | Bin 1012 -> 0 bytes assets/images/roles-small/wolf_logo.png | Bin 3921 -> 0 bytes assets/images/roles/DreamWolf.png | Bin 8886 -> 0 bytes assets/images/roles/Hunter.png | Bin 1875 -> 0 bytes assets/images/roles/Mason.png | Bin 1364 -> 0 bytes assets/images/roles/Minion.png | Bin 1810 -> 0 bytes assets/images/roles/Seer.png | Bin 1401 -> 0 bytes assets/images/roles/Shadow.png | Bin 1178 -> 0 bytes assets/images/roles/Sorcerer.png | Bin 1903 -> 0 bytes assets/images/roles/Villager.png | Bin 1773 -> 0 bytes assets/images/screenshots/create.PNG | Bin 44673 -> 0 bytes assets/images/screenshots/game.PNG | Bin 35278 -> 0 bytes assets/images/screenshots/home.PNG | Bin 66356 -> 0 bytes assets/images/screenshots/hunter.PNG | Bin 34001 -> 0 bytes assets/images/screenshots/killed.PNG | Bin 31479 -> 0 bytes assets/images/screenshots/lobby.PNG | Bin 24781 -> 0 bytes assets/images/vanilla_js.png | Bin 218 -> 0 bytes {javascript => client/config}/cards.js | 27 +- .../android-chrome-192x192.png | Bin 0 -> 16342 bytes .../android-chrome-256x256.png | Bin 0 -> 20372 bytes client/favicon_package/apple-touch-icon.png | Bin 0 -> 15829 bytes client/favicon_package/browserconfig.xml | 9 + client/favicon_package/favicon-16x16.png | Bin 0 -> 1158 bytes client/favicon_package/favicon-32x32.png | Bin 0 -> 2093 bytes client/favicon_package/favicon.ico | Bin 0 -> 15086 bytes client/favicon_package/mstile-150x150.png | Bin 0 -> 12089 bytes client/favicon_package/safari-pinned-tab.svg | 54 + client/favicon_package/site.webmanifest | 19 + .../roles => client/images}/Werewolf.png | Bin .../images/Werewolf_Small.png | Bin client/scripts/create.js | 3 + .../scripts/game.js | 0 client/scripts/home.js | 3 + client/styles/GLOBAL.css | 17 + client/styles/create.css | 0 client/styles/home.css | 0 client/views/404.html | 24 + client/views/create.html | 38 + client/views/home.html | 33 + .../roles-small/Werewolf.png => favicon.ico | Bin javascript/game.js | 456 - javascript/index.js | 27 - javascript/join.js | 45 - javascript/modules/card-manager.js | 133 - javascript/setup.js | 585 -- javascript/util.js | 50 - lib/jasmine-3.5.0/boot.js | 136 - lib/jasmine-3.5.0/jasmine-html.js | 817 -- lib/jasmine-3.5.0/jasmine.css | 128 - lib/jasmine-3.5.0/jasmine.js | 8218 ----------------- lib/jasmine-3.5.0/jasmine_favicon.png | Bin 1486 -> 0 bytes manifest.json | 23 + package.json | 5 +- server-helper.js | 206 - server.js | 79 - server/main.js | 94 + .../logger.js => server/modules/Logger.js | 8 +- server/routes/favicon-router.js | 18 + server/routes/router.js | 13 + server/routes/static-router.js | 66 + server/routes/util.js | 9 + spec/support/jasmine.json | 11 - spec/unit/ServerSpec.js | 65 - stylesheets/styles.css | 1918 ---- views/create_game.html | 165 - views/faq.html | 30 - views/game.html | 27 - views/index.html | 57 - views/join_game.html | 41 - views/learn.html | 60 - 99 files changed, 436 insertions(+), 13683 deletions(-) delete mode 100644 _config.yml delete mode 100644 assets/fonts/Diavlo_BLACK_II_37.otf delete mode 100644 assets/fonts/Diavlo_BOLD_II_37.otf delete mode 100644 assets/fonts/Diavlo_BOOK_II_37.otf delete mode 100644 assets/fonts/Diavlo_LIGHT_II_37.otf delete mode 100644 assets/fonts/Diavlo_MEDIUM_II_37.otf delete mode 100644 assets/fonts/REVOLUTION.ttf delete mode 100644 assets/fonts/manrope-light.otf delete mode 100644 assets/images/Wolf_Logo.gif delete mode 100644 assets/images/custom.svg delete mode 100644 assets/images/delete.svg delete mode 100644 assets/images/gallery.svg delete mode 100644 assets/images/import.svg delete mode 100644 assets/images/info.svg delete mode 100644 assets/images/list.svg delete mode 100644 assets/images/pause-button.svg delete mode 100644 assets/images/pencil.svg delete mode 100644 assets/images/pencil_green.svg delete mode 100644 assets/images/play-button.svg delete mode 100644 assets/images/question_mark.svg delete mode 100644 assets/images/roles-small/DreamWolf.png delete mode 100644 assets/images/roles-small/Hunter.png delete mode 100644 assets/images/roles-small/Mason.png delete mode 100644 assets/images/roles-small/Minion.png delete mode 100644 assets/images/roles-small/Seer.png delete mode 100644 assets/images/roles-small/Shadow.png delete mode 100644 assets/images/roles-small/Sorcerer.png delete mode 100644 assets/images/roles-small/Villager.png delete mode 100644 assets/images/roles-small/wolf_logo.png delete mode 100644 assets/images/roles/DreamWolf.png delete mode 100644 assets/images/roles/Hunter.png delete mode 100644 assets/images/roles/Mason.png delete mode 100644 assets/images/roles/Minion.png delete mode 100644 assets/images/roles/Seer.png delete mode 100644 assets/images/roles/Shadow.png delete mode 100644 assets/images/roles/Sorcerer.png delete mode 100644 assets/images/roles/Villager.png delete mode 100644 assets/images/screenshots/create.PNG delete mode 100644 assets/images/screenshots/game.PNG delete mode 100644 assets/images/screenshots/home.PNG delete mode 100644 assets/images/screenshots/hunter.PNG delete mode 100644 assets/images/screenshots/killed.PNG delete mode 100644 assets/images/screenshots/lobby.PNG delete mode 100644 assets/images/vanilla_js.png rename {javascript => client/config}/cards.js (53%) create mode 100644 client/favicon_package/android-chrome-192x192.png create mode 100644 client/favicon_package/android-chrome-256x256.png create mode 100644 client/favicon_package/apple-touch-icon.png create mode 100644 client/favicon_package/browserconfig.xml create mode 100644 client/favicon_package/favicon-16x16.png create mode 100644 client/favicon_package/favicon-32x32.png create mode 100644 client/favicon_package/favicon.ico create mode 100644 client/favicon_package/mstile-150x150.png create mode 100644 client/favicon_package/safari-pinned-tab.svg create mode 100644 client/favicon_package/site.webmanifest rename {assets/images/roles => client/images}/Werewolf.png (100%) rename assets/images/favicon.ico => client/images/Werewolf_Small.png (100%) create mode 100644 client/scripts/create.js rename javascript/modules/heroku-client.js => client/scripts/game.js (100%) create mode 100644 client/scripts/home.js create mode 100644 client/styles/GLOBAL.css create mode 100644 client/styles/create.css create mode 100644 client/styles/home.css create mode 100644 client/views/404.html create mode 100644 client/views/create.html create mode 100644 client/views/home.html rename assets/images/roles-small/Werewolf.png => favicon.ico (100%) delete mode 100644 javascript/game.js delete mode 100644 javascript/index.js delete mode 100644 javascript/join.js delete mode 100644 javascript/modules/card-manager.js delete mode 100644 javascript/setup.js delete mode 100644 javascript/util.js delete mode 100644 lib/jasmine-3.5.0/boot.js delete mode 100644 lib/jasmine-3.5.0/jasmine-html.js delete mode 100644 lib/jasmine-3.5.0/jasmine.css delete mode 100644 lib/jasmine-3.5.0/jasmine.js delete mode 100644 lib/jasmine-3.5.0/jasmine_favicon.png create mode 100644 manifest.json delete mode 100644 server-helper.js delete mode 100644 server.js create mode 100644 server/main.js rename javascript/modules/logger.js => server/modules/Logger.js (76%) create mode 100644 server/routes/favicon-router.js create mode 100644 server/routes/router.js create mode 100644 server/routes/static-router.js create mode 100644 server/routes/util.js delete mode 100644 spec/support/jasmine.json delete mode 100644 spec/unit/ServerSpec.js delete mode 100644 stylesheets/styles.css delete mode 100644 views/create_game.html delete mode 100644 views/faq.html delete mode 100644 views/game.html delete mode 100644 views/index.html delete mode 100644 views/join_game.html delete mode 100644 views/learn.html diff --git a/Procfile b/Procfile index 489b270..8fbd1b9 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: node server.js +web: node main.js diff --git a/README.md b/README.md index 4ec0518..8e86ca0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This app is still in active development. The latest deployment can be found

To turn on logging at the debug level, add the `debug` argument like so: -`node server.js -- debug` +`node main.js -- debug` # Contributing diff --git a/_config.yml b/_config.yml deleted file mode 100644 index f3da9b6..0000000 --- a/_config.yml +++ /dev/null @@ -1,2 +0,0 @@ -theme: jekyll-theme-minimal -logo: /assets/images/roles-small/wolf_logo.png diff --git a/assets/fonts/Diavlo_BLACK_II_37.otf b/assets/fonts/Diavlo_BLACK_II_37.otf deleted file mode 100644 index eb42a9e284e4676870866350506ccea773750f9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33964 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 E1^fP@6951J diff --git a/assets/fonts/Diavlo_BOLD_II_37.otf b/assets/fonts/Diavlo_BOLD_II_37.otf deleted file mode 100644 index 31aeb803f55f39289b7499fd3e3c11c8bf20e36a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33460 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%0Akz^!Fb=+N|{Z-+fsI<-YmD zM>`dHfIQ0Of)~|6S5>$Q-he*HrafpZRrn**DDwvN$$hCFin1V|vW)zT51CcuM#h(E z{wMzrIfg7L^8As{+%4bzN4b!R$#7{ZFU*l|Nl&If8KfG3xoIZ(g1Juo?FN(TvICtW z#*&C}*+m}LvV)9ufs_4a%mm1;?D9bSJ~B}KkTg-ouRM0$FouGNuN@10;Y2va^;LM} zt7;5=FbCrl--?{0UzkPc(}mzg2k42lh;LNk)X)!#n+}LtLFFq`}ed5?qK z%i{#&QO@^=k8%_lR{B!b8FN6RESng=fA-nm`VM{bOwl12$LK2`^xbE&K7rps_EC0+ z%D(gc)35A974!>u;#=vr`14!tyZea$qi-wwj*)e$ERXn>bHXZa(n=9~~y zh3Ly)16D7;0ayd6RWSD*12iIDr{L(2nul-=I5FGbixiT6~(1Ch5X@mKKg$T&>H^0io9RzZzUL(*sojy?4^VUDp01@6?q?FZL3ybMYwADRK(O(V%&jQALSGQc2c0P z0(&WNfPynafp=s~#t~)KXJmRBDD*T@XlsJ__ZUw)z^16H2#`mwH%00(KnvR56!>!h zWv(^_{cA~6C0A3laR%1fRl!wvrA+J+p(ove^A0O;ndat5ohq-x5pId{&jZT5ZHf4$ zfF_jL5-pOU3utRe8Ys|Rfi1DN#0nm?wIp8U_W-?7drP!ehCT}PRiK|zLN6uUS1G@r z0{biR1C;PU^lVE=f(&P%r7f{O7Xk0cb)iL|4SdMLj-e%LY=u@x*gL3^>Y=p712i8) z*o3xtfaW4V7qrv^G|SLkf!-*`0~E^8M}fWy^i%MADdB!fcz^;2g2Nu5N`^939-yi^ z;2o6q1?qhXD93mzlHjRu&r{)^CnzaGyv#jMg%VGN22XHILX1p_Cn(ti=%YYi1^S^b zPsp1b-%knmSIQZnga<06`73aSQi4p0C-{S0;7<|Ot~!Lhz@^uK-U{plj(HJTp1mN? zGL+@niwscUK(xXOZ3I5bL0FMPZ3~MzKnh4!17W+|MIt_KgLlW_L(wO0~GuL3eG^({s`;U zmKcu%5q}TR6TBLTu^?eTS``%fE2;iUE`OyK|B6)5@EhbtUNbuaCkqf-3%r1q21KhF zC&sBfhS4)lzlQ_r5pIYwg8}Q7?*W`#9tAi9RACZ<<1kv95GEoZHWDigJ9%2bj!RBq zWVzbHQ$`7xrL5 z*nK53$;^Bvi#fuaV{)0B%yZ^fRdtn{sZh%GF7`(M^xujxvKjr z8|nkK3azk&f=BY4BhNVUd?U{`@?4`<<_meYkmrF4JbB)cXB~OYF#=bfWBwi9zbj9r z%mA|fSE1%dDD^!;vi6jJhCn-P1FmMKh2O(a@C;~4UUJB9eu@Xx?M2X`Ls zBHSgoD{w`q|2F($xTnDT4(I?hV44ek-b zFUuvSH}VZb*g>g5UJ>ko+X+_)9sfrSZ4j1ASFwuDwI9USfYz=JO}(T1JY?v!HvQUN*tuOB7f|E7Hh%kP2GN9dvd>M4h`mgm7q zn4kZPr~DZ5RQBJ0!=Lh(kwd?{L)1^-z2TIJmXn1y!_-ed9$&GJ%t z(QBBwDxW_oEdr0^SN8MdcB3rWJ;LlP(*?T@c=+$o>jOe=dH*k zr#$*IUvNd}|DH2w*)i-ulm~P4M-N)_XSs6pUwH=`EqGCZj@{4y_>{i~zD)K1T0W7t zQa=AG-{o(yg8at^wLX&T{og%UYs%|AoYDV}Pj%3T{;QCJ57=n;OK2-RfBJtLDHXl` zfxDI8@5^5!ul&CLBj!V?N@;(La@73aKNUR0s-9Gg+zPrsQ2X~1v7WDx*gs|T?->92 z`>(R}pK(78)8aYW$lk4)1K{?sNsg`oO<%E5U0jD|* z8F!`~PIEdky_rGGFziVjl=DMp<#ezKtPriRi)L^xq(-e3byl1vno+Y0EFw)|wUAE> zE9%Ft7W>bNlfnOV9*A?dzfhWjRl0n7wiFi6<)C^UP6pR2r%@YmI+lqusVwDWu;N@W z8~dQWI1}8D^Snbi8N|6DIfj$Elh_@dCTGZ5a*lkBn;93#McCA@kgK?3QwT2H!rtjN zDTcl99=T6S$OG~aoOw*1lJCe1@&k5NKa!uw&#>{ohOO_Hzi>=G{}XW{=c@3mIZouh zU_2QwrcEXPd~rJ0mFb34v7X>%U!05$Upj zZ~x+C9#^Y$NKkNP|L;$2^s*yfQVBn#%jfg|`N7#v^uITZU|l{obrc( zszkh6Xq{wGm4de>bj~u+vm9>?=$tfAvJTpYgTBcG4V!Tjfrlk%8+up1jbOlCg#Do5 zAYKRPoujDn7+xJT&k4Yjcjd3Z1Uhfyb$|{l2E2#28uVBR;6uFCpv@kE zw~z7aq1B#(!td}_fp&WV_ygW5&~maxBevMZ{e!*K!)_=sP z+=O7|S%a{+o#04nK@;kT7P_z=a9kNz;s~AC5UK8rJF&-oh2}VO@?bm&4;}dhpeN%= zoS`edh!gZ>8)9I58DE6EFz8=s&91or){W^#9H2eBlTV;Qdjj@mdV_#|40;*56#WZ* zI*^#4QwM>n!OUQk>CgD1gkkc`g?k9Y(Q0{YaFDj>f3de|ks@mkHKY}{X95EvVu@=| zL|7>LyJ3-oH4KfkdsB|S41G%awZDS=)Mx}qKGX74be;59JID3?V(;e(YU2spwovDNJ zI+JcpUB&Ogh-gn|#thhv`4sRg#sb)*lC~_Aup18dLn~ua@wHuvVJl;j@l`>c7NJ$( zJVVz0z7H@1C2@+zv_l`OFwVdQ2IpT&Xr7WP*Ctaq5v5fF_a`X6LB_-lOF7N}3?r;f zjhQA0RYRyL+N4hX=*$DP_=gT z#7}nD?tIaS%#WJ!;>bxpnbyxLB^<4E1dRnWlqSmX8YKz)c-9p_~-AUa|-Ag?{JzO249-|(o zPEb!(r>N(tm#EjMH{#0BKJ_v6Id!i3hWftxnfjIbS39+xmt9Z0!ETXp5fL5xbQl#K z93LE@zz$KNQIWx80xGjI5c2fm`_}E)VP4?*sy?zps; z0xG;tiVy@TuoKu85F4aGpNeGM+m(6gqohNo5bpd@MWG*s!zxg}`Y3A5N8#=YHzO3- z{iFDZkHX!R3L+HP^P~94kHS5b)JO&TRa6;O;q_6<#_>LVN9b6F;5)~J%B!%FXwi1X5ORVrQwj-5Luzasu2}Oog7WDsi z-TN>1x-0K#%U8Y0Rawneg-&mbwVyX`hK|CHbw5@_m$2`uj@yrZnC(-TMa&B37;}~> zfLa``8ly^4Syf9^YgC(5hty8$n(6?kuS0gcT|+xxyWVzIyVZ6(>98ZFg;7?FenWcDmN8J*oX#`;GRd_OAAgwv4rB zUDzh$CBYd_t7 zp8X2@E%rz3&)Z+OFSh^Q{ule-9qb$&9O^l^Ieg~ur9)?j-VOsCMmdajh;T@DSm==E zu-;*_!w!eN4#yoXI9zkM;ZW>Q;_#YdI0w#=({nXBBPVk8xCUHPt~uA9>&f-u26Mx? zP%e^-<0f&_xD;+7w}RWqWpVqtBitG8BA3S%az)%-u7vx6d(FM)%6OJ{;%oAC`9{1O z--2(&d-7lMo%!DU5I%?>%a7;d`N@0|KZ~EwFX314Yxwp2MniDuy{T5?46f603#aQD zIHT6|w6$~GINmUQ*^K4l@d`VF(EZ8Y10*ppt(xw4L|b!fbI zi!s@kMc!cD7@V~%bzy3%NWH1IX6@S4v^B=G;0 zHGD!6r@zud&|hc`-%rrH^Ph4$^Tf?@1>+xh(pAo#X+-_Y^XJ>QkS-YZ)7mG_TaELM z_mOJvH}95sjWkcouNL&LtGNmKR~el+{qvE-IsI$PaZdkg;i;Kl^^F{6ku=>86qpmo&4`IN`aT^^HMC(~!7Hk-yQGO2uH~C?K93{c zp;f!WGX@V1j}H?&v!iz=o;w)x^v|NLPk*6o87tMH1|M3VFUjwI)WY%8 zQg~H50NyYZqdqEb6Jp=U_+1d zp3*AkajoKfCXKhZrp{Sgyy5B14Ptt(hNl*~us+ALgNvJUyi0~q6i){<6nJCjgQIhL z2t1uE-IPp}H|A{I{QS+C5MPnE4KP@jtj{*mDs(Zcznfz%v6}TSt+CUGh|(7AU~3zz zS!&M?nH9fV*fD>nNcCE(IpObV?#edr8(v4!G}!S}lv*~YeMeLEGwup?G=Im^nhnlL z4Mp8RuB*WBw=FWPmjG-DO=h*Z$u0Jwv`U`uO*Y;}pVbd(eh{wEi>hI#ru>mwE}(BszxXXcu#3 zJ$@6l-7q${PT4kojkqywW!6^XO7{G~eI0stj~kY}IBv}Z@l5LRm4{3R*T+YUnVdM% zJSkZlk}-G1JaNaIwD@piM10(^AwyQ3P8KIkm=%|79C9_`@}1NB&x^WM3s){)DTc&_ z2Mj#s_u|>HoYM#5c7>Q1Pg^)`s*(3tWvCa?Luw^`9&!1tNSCs2w_m28(^lJi)DwBy z=(ccT8CRxZdD@!3GthQPUrUy|QWN_jn~yINdOvofS<>elY2)ld8gSvU{YfDxdB}Lt zLu%5^LlTqh>EZ<(-!Cjaz%(JDwQy+d8Iiwma@aXj&cZVXW|$MFPD_a~@e3DO7n!6b zquA)#@l#Uf&$ODUw|3*=b*oe7F14Dw&^)bm>%2kYSZz)3B6lyu)4O!H9gT8NEqgFXW^X>?(o&#>NWN%wFy=cc%DT zKQDn_w|?om46*-&Cpl)`FgUFgaMW_M&M;!u zsF;~za_XwtZrm<*>&(n>ag3H{3pO4|6*i9Y5&0iqWEW)2*f?P`53%{Yqc-BwyoKW1 z1?+C?ajQ9mcGGU})ts9&RcatLo#k#%TOT&G(54^Qe|p<4dp?!Blzp9NU-@q38nqnV znIGL)+L15yH}ZqiI-(s}LYV_}j~6xj3sP^b^yo&OaGiI}_gf<5=e!bmsX(e@kjRUm zXCgmj^Pxq8|BXI$ge0D|r`&_fRAZKsXbr2tj}y9P8M+I+$oXflWjGxcxt929fb|ZwJJTEqt97p%tEiB)7 z&B)XG)O?b5HPd94c9qOf!lJZ}x2;6sw3RK{FoNS9il z*wSrt4eNy88fg7X)(G?=OPeR`vD}gB+w+(mFxl@FAl#v&&kV2{ZDVbbn(W;J&RC87 zEUv|t7Q7*1W>oyt1=Gh+ixV=|r>rya?WSD?UB&{w zZ=$f5yIOGd&<-=-MCx&k^wXJpLU@<}R((uV! zOlwyzL>zm{YYkL%eN3k#X>HMyF45eKNqFjInwX3%YsY8!WVPZ?3L&zx>RWFVkLg~+ zA6y+zC+6GQB=9-C_q1ryJH$`q57HVUox;)@yniuDblSgr^o?G-xnjR!{t}lpE|;HY z75S_O83k)M*Rm!roamL%ynT%5DJ{|P+gR$<;E*JU(kTAK;dT2>Yq!pszQN3oSbc)y zrxn<$7ofOpV~hEmVwzdZU*VeEn@G)=S@P+vB7yEH#41al5h~EhA)L-+`RpLarwMd= z5q~R#<7v92xnSFPV*N3u;Hs_os+#}Xs!k`pSP@?ul)xt>bNrroTS5YF6|FoSs5>gK zd;>qpk&maN5_novqUN0niuw4B@$}IZIxGKcfo7HPtbc-L*o6H(t;Ta3G&&O)i4hmH zSb#_0^V%Ud<}MTISG=Kb_`$FalP`+xmUWW9>8ptVm06 zq7zH_qPDaTpF{1vct0p)+Fw`dXrRlrC#`wtY(v38XbX$ws=#0Jo|hu>Q->5yu^Nxl z5gOijVc+&c=QfP?68RGK1A#B33lkXHuvmSZ_q67h8XgNg8_MyIIEK&PkFhb#z|%n8 zig+5HXX}x`@KTmEHuKkNnG8>d&7>Ba;WEQFwmrl`Mo!G}x?JjlnBpk{zg5U7=2yhq zmL~A0I5n>=kX2ac327h@a)@Fb8|3h(sLv^0();psu#RD7%O)~nrzS9jIe6eNZN%FG z%M>hj_)8`J1qbsP{*i{iMB<-luu+jDg1>d)zj81?!p;Wk75<-wKhMFCg+H(1Rv`@G zu#Ll>Mb;1)z+t(A*^umDaLk7Pg2C)9{|{;}f!&+zQ^D>{4yj-*hv^%(ZgQM~tsACq zSj))*0?RfztHM4GrgYfVVag_#81fB)Wg7-cq)>%ZJs7uPJR`W2 zb(i2TcsTciZ4K6Ja$f~&HvSkW|Cf5M9;buMItI2>`7he*)%XJ;fi-iJ8a8&A!I>=% zFr+hk)g*_ICj@^qWezep3WcGLOlM&2$Dj3ZE{e;o%wYz96~({x@TVr2`S8a)oDSgc zfh1o3OE&&lN+vL53_}75gO;u)%rO-XADNRZ{%}afGWfe7b3p^srfiG6`Xv)4Z)ELo z%*4TrKNQESO;JG(u3oyS3REvtKd63Gm13h>OZ};On0mB2TAiex0lV8W^;-2_b&mS7 z`l#A*xP3lbTBJEL@u#T*e^=19osq9^Q zH~S&>3+#8>U$pxGFQM%&l^zN|9r2$HtCr9QWv|>jHFBs@AINQuTB-mufAlMOG{Og#D!R zC()mze6sSBO`qI!@^sv&UaiKpy3{&X>vFA| zweHn=QR|h_-e@s4Fm^LeFyA5k zdzgoqgUr$93Fg`6rxrU)W6Nh2FUvqnkR`#g!m`71)KXMOTc>Ir=Q`#(P3pMSXg=s^tIqfMgXO5YL3PL1{kC53dPiL>F2`I>d^+mWv#$QGNv^rB#jfAif6<_N zgO&}tHAri4uA!yj$c76V{?h1aBih)|xLM=QjmI~h+jwu|1C38KzTNm?W2y0PO*%L6 zYvSJ|ph;wtMNM`$Ind-Q=LWP za`t5R=A0iZIj49Gmg*U$I`87BIzg1uSv@877k#feya#LI%v?Z)mG;zL$ZT zKC_iZ2(}?*P7y-crkEr_w`|%Cy82r>=Z4eG8&=xLT4bey{`*|&ZIIMIwO=C8hB>sh zhuFNqm^jY=e8KHKNAE2cw-3@zSl033bW#7#I&s@kW~On`v!qwsR!GBnb_CB(*u3?YyPew~1oA`0%d2MxAX@JRP0D z6c?(CZI4C@y1T;32Wo1fsRo+-Egkk?vA8ZiVd(^utEUtyt&&#Vl|o-B%Xf3D#hvId zI?ISGv=j1lLLO;WCuvx#S(WRuS9fVBT}4-U(ook42`Lle%@1aMD-Dy9jXJ5HEg}BV z$+VM?ARo@QFvFZ>bC)hLZp_@iIYaEcEOYcW)7sUkD^{38mrOV~^Pv6oO_@_OOovxx zZN!Rs-cs|FY12~@O$pJ9mc^TQXKvlT#h5;A_0(lz;xf&`8S_(8jIra##3YD$Gots5 zHBFq7oRnnVo01l|EWmzw%(#^arV&%e#iq>1YhJx_`O&>I1@E8+UPFXcW z)O{u|Bxp#Xnubtf7+jcU6>0r>oGwZj%}F#P{z(E;nBIodNgJie_+MiZm~_dh5^VinsBQbbg%QO9r6uZc+>;Wzu0;Qm zdbJY{dI%%13afU5`RSh8<~x>i=MENgx!1VdwhFmewlj|&svm)5dep9^pu?6T-u6vJ zq|K=fH(M&l?xGL(8FekZhn5qYeX>CFVcN-;+$uu40Ycx5_)@1EPA?zcqONGwE4rlA z32W!!K4!@%MJG#jjS1|4Tg|!Sto}I_=)}3SjWJW+Z$bnmy~OxQ_Qhhsl^xH;Wo7w> zZ5+KMIiJeEipBfG+ylaQ2ghNnQS`o8FC~>uqEii0Ly15wG;X)$Jk`>uQR}`D>#@To zhFHai%a`3*XQa)71yTQ7k}nq`NE%u#jD7+^bU#76i*x`>&61WfQZG?zsFiw48i|q2 zX6Yd-IbL)7`P$Y^dzOePiHUP2nmqeme`BUO?AuG<(a%NNP)l1~YF5uIwPAJ31*V>$ zTMC2NqI^0_UM!cWi%%N-?_)nxRZCreJ_F0oHa0)_>%N=CQ=4|4y=Gh|XlZy^HN*R3 z{$I56AB{z*q@#J*NLP3d|+GM^_Lx7i=x|c|u_sb)+zGPm`Q8Vl|U|r|^ z)ZIf|!-+cC9A~>&B)|-(ra#t*6Mhqq38j1@XS=A?C0ciIOmT_Y){Opa=qSL%b7Fq( zBC}LVw`+CHpodFea7BWewuTu37NBh`EI`e$A?wD`QX0s@GNgMCZ4{eNvu`k~ICV!( zN2?&)(*pX6!%`eQI-Lbvp#P#^PZR{uk1atos{7BvwiR}Y}RPB&0k|p84>`M>sVQo zu0%bA)4e~*$!%!C>1Lw|3o-?|;5+u2aGZLZ*Riy-WH=~qcsprnCtm~A+=-BCoD<$l z6K+tKYqtCOOhE~4a)Y)gQ4bU(!wpZ`YPvXrmF%gi7yabfciH#W&)P68Q|#y&Jkv(O{)^_jBne@eW5KeTsD*3#r=i>5}$kbo=fn+rom}EbG%p$C-kffrS6QlkTR@{u z-6(wx)9$>N{S*e2IF06XSgZ5C}~Q`0XO(qr~ztzfD3wVG`@ zZ8vGI^jgxTNo(zOB|`BXb@5ok;5*H^AGOx`mj{VMN?nd)6L%T&rH@u8Wzsd$FvH3r zH!`h8DeG4s&9vj~V@`7=0_E?hsefrN1JJD29w)`)zqt(24w~P?YDP(StS61SAltxr z#ujDsHdyy=%rY*UlbW0=N;r6!xOQ^tq$ znO0Hvd4|B`6ssF?y8ahAIxPosI)&3uo+Rid3o|+WyE>fS){oPj5$N75bwLbwP0&pc zaK7=bJiE*G2^W+NJJn=y(u~Ys zofDY+Vl{1eTTqT_@@EPcc7Jor+@>_ZRrnN^k5##}cLGgIxXr11aJrPt(x?O`w^-d3 zs$m<79&~s_UNQ%ZPxadR6Qx5H`=W}aq}dVoPy2^XNEG*s>$0}5Nq@JGf9xRBgrIfS ziUWcUto7K&>rZw>`ijzdm=IX@$g%9}MThaNTQ4MY`Xie;eNjtJm&P$01@)(bE}Vl3 zeC2&X5?YN3+0j8PZf&sc+IaKL)=+Oz^0~n6;f@W>m8}Cf_n@wZ26ubCppLuG?G~oT zb~Z|8w&(TvOU2Sz?DRN!7SZ1g3|rR=3oY;7u4Z~e8gA&_C9rFcVLNY%)Q7#kHT%uY zU9sqoivo;G!?)BNh~uKJf)1w@IE{La)2Qn>je72fsk!a6%N#W(Fa^80EVVbMn<(d) zS*$+-6K-OCLDytqyln#pw16m_3JP<@5?dM8Ek>yMyt3WXJdg8 zA35vxofW;Ww;t^(BewUGyXOoi^;^XK>aCnJD#ax*-$73$p-8LW#+ydEk$u&Ax3{-= zc3qI4S(?H6reSh~W8SiG!rujzQNqXSh#BZJS?GMvj!zfO= zz2YuMRr?olwkeB+cUfH95%IQ#1!v_myq7sv%1SIX>1}lSWW(T`=G=K!|AuOpy}0Q8 z-o8hl&@WR&n~v4LD@Z;vx|i|WH*GFjR@g6L^|q6K&AI-_JF`dc#ztu<>z3D>s*=S< zw4;HxWpmTBZ!Z$ds< zlhl$!i>pFeJR362-zvu5zZFhvnr=~c=c7$2S(CS!Te$>yyEWLpTUdMy>#*6>1Os^w zoRSqB+I#2qk@0>DMO_vqVf(!A@@yPLBo!=dP9GRbxMmHsC0sYHtlO#mk8Jj`1Dli0 zQmyZP)K&ytv_DYwW!md_;Uw-@$IDW$my+5Z-D1;&V``;ae)qPMPx>mqbStGP1*H%2 zoxZs=;_!$o^zs#Wm-GcUajw|vStxa;?Q=o7NUxMNk%DNJamD;q3s#F!QHjF?jru3j ze49OWmO2|dP3tRtDmisOa%-YEb^Pquc%yAVX`hM*2i52x%q${>2bd;27 zoHlRT+$rL=Eo=Agr>n~BC6RV97OXr^Kc$~s9^P)9xH5AwSP3ovQwc+xml79&PMDta z%l6d2TfWQyD7$e<;aHID{G^I=ZpF$LB7(UgObUpJFrV|@)Y!ER?|WCIo&mS zt&p+JEmYg)?lo8sC9PjCE?&GSb)}KcV>iuSo3v6~o-}dscvD%J);fJ;dV)A>N$Q*x zCR@jO8l4B-a)ADHfH`&i(&_7GuAETQ&uz|zai^Pe57_-PH$<-(Z9jSOiaD!HD>4>a z%`>tS_l2k1?_8F?BindoaC$${TYG(!VSC2R;IP=Kk>>bkTH0G`Xy_6?OsXO|j5(So zQjX2rahy8PDm#aC5v4bh(a`etKs?UO%cV^Xrw8q9^W~uMfnpuD->JykcTR0RC-&ZG2%VD9+b?N- zws?u%wPHigh1GFkn5(zv(RSEkquP18;zCSOepZ2dY3m1~S9wlWLWe(~|N@(9PN;4V!m*5BKrtHU9-*I^M9+UnACYm&9R%Yq5yh4oMZwt;8T+LM5hQl6y>zmDI z*QKnVkYVoMVuBxz!L^cp?mbS{I!zvZ^)h4g!qg3-b@rq&Mk!>baCR#u|57hDd-gi4 zO((c=sV6m? z7LQ#y(KK4B8qZ0)=nuzfUJj>!@V>OiT&$zN!d#@L-=3m}PB8`F(!LO?2DXO=h$VJ? z+5yAciBcoB-I?LoL~70+xg~a7VHh-dte4lMZKp(9ojtW?TXAvP*g>KqqVy1sK=o(J z8cBn26#8mI{M+MkX>{=M=mgp)jk@Jv=X6znsl81fDCqU1tc$HBNN`H}O3=HI{Ia>z zu#EX))~N?+w8g1u$LXg^7Tch!`WmEyS_1)UmkLyUMv_M@N&h0<)7PD0;lH%grnoc5Pku@ApxeTdPz^|Ham|44xu8*glp zQfXb|diG4a=G+h#TknYrq&CKA7Um^CiXHP4s;A5q!R*t0jGH*amhUPYJVv|c<_gP& zt%CkRJ)9Dk*cMxagBK8+jo7O zuDfG(W6c{NpKzUj!R0y8wHrF*b}+kf45{f4|LTLvLGks#1oi|A*EylBH(Yq&S-xXK-~pVZDQ zrLj^A%6F&6eZ&`N;PXhFN* zrh|0v@6hgdas}*Cb27M9oIbY&vP+ZcEsOB}DU!0~q~kZ`q>D(ZCf%Xuqq+A*NCLB= zqR-OmhTWqNT(TPLy}fl?lbzjTpVjF4Y1QWM_(R=;A@TU{q%g> z-Te6pPAKrmjeJ9R;GrO^@zfI=u)><=X&AjyWMqBk7 z%KeU({_A#(7wH?Dhi2iV`4cA?m6kuq6>e}~oZRp)<%Z|meynKtgKVVyP$H9AgcM7e zv+cEu5M4~WKe@xnJNb^-$#;}@@~3D!c_-gaGRQ~Q&2DHH!tCHHZNmdl^IFNVqr{74YrF(qyQ$Wfxxy^rRy-Y)fNk1_ zB<#~Nldw@6m4uyIW>N?S4}rZU&LXs%Ykipw+^g5(HpE>6a>f}TZ)^|bfc8M%*dECC zq`)>xKCvhAkv$n9w3}>wiT<9Y46Za`I|kSO4xHZk4BcisioumIy?{0?pq~|7;&SQK z-Q2_RoAtkZN1qt>anCl5_Mb1#VMByE&x4*{zP9CXrZ{ZpmGSpYqcGULL*1H(?#dGj zQ-n?f6T`yIyTS*g`6$)~L@hF!OPuj4dVg#lQ@}g@OH)U;H+w9aYt8$~DXF^M1M*_hmfScB=JNt}t2Q zR0Hk?y=D>G&auAAM%2@icH9D5h%wc|O6TSZj|56GxB^^Zn*+(1N9{4B>LVpvPSKSd zz=%SE#hX@f5z;t1D?#4C<|zH$wAWn<3~o*=h|I^k28+^!Cznx-dovUuvc5Tu>tmf*&XO(w!C| zd>taQ5n0*e7|5Rr7|5A)bu<^g7>VfE&#uSQHifi3rnk_CnC8EQNp=ZLvMy5l@m%=y zTwCqjWeHm_WR+`?n`dpBzFy48z}<+Ehpfgy12HzYg!W(4p@55DIAQTbF(w*A{=f*U z@$_j;*zOC7_YTWfQ_Q$Pd6kniokP>ICWYm-qoVxE;cPJA=PKDentJ#gvrl^agge2 zXJ~*e`8bBb86R-!%pvYz5l8!0UXO6HHNOl)L=$e!fHc3|5w%*59pO9VOF?hj%N0I9 zad=Tgs%@z5OMyMzHQ)vsJRfBs5wNI?9s5b)k8&4A%viH}LzYpwAtg#jXxqmeZ02MsXV$%c zL5F^uElg0FmCyupl}+u!(JDXCp@rE(g`J0PLz8iTEO!X*$x_E(tGjU0iz4n<+?!Z` zS&(X!=r7UM2MxC0`wPFdU?-#|t)3z3^Lr1E9y}@vL|i(w^U$uHqOOQ5eUjjmh8!jg zbJ{4|U~92hC>zPfubQ?d1v&hN#tg5_vGw5AU80vwa>ek6k!X%$Xg1NIH?jrTmr92x zP)5EM;gm0z)DSm06+6>qEH(FGsi}7lD$R1?ZV0f`V|}qlE1M1H(;M1j>6&BdqcD38 zh||#i{RL^;Az^x^Y&{+0VMkQLG7!94eSdj7$$&+|XOyYOA^;jUu z`XI|zD}a+c=mtz-R3*>Q0kA7w!?`DP!Df1<6-Osn^XP6YfaE1iEbQo`umbYJK?Ls8 zeP)&-SgGlqFKCApag)~Ou!NHwq_+m#jJ!&#nQ0$v;E$v5Sb!7TO4O z0Db7f9os4+$)BxgUR^GD16SJilCUxlM^Ryz4i0Hn%v}yceW`)A#MN3DykElLeHRAr zmmdt?QhfNfoqr>_NiME7RSZ8tciyH&uOM?@f&|?kmw4wbEmFVw%hV}CC z)aCP2O~Xfx3mt6izkft-4?%x#=;X~?-~61ObI^QZ*N!sj{c4z}a&PoYq@bF^xFi4xu0Rft<6;|klkCm0KeY(Qt!g@(fbu^rE+Td%=H zy;uEuy5SuCSwqd*6Dx5q6IU2vJ=YYhII;J>>HC9m1I@GN&7N)CvTX&9$4~fet|vJ( z@N=Cc*88ol#&3E=i~gnw>(X(>?##(=ZmooL3>OX`;q)zWvf=5&>CezE(m2D)PK(1A z)DaGOa4txeFPp)f=?QaY%hvF5x&91oSyuZawEF^GUh3n*!CzL|qAoYZc3JK)CtClx zpx;CHmpY)c#-XqF9O9(!OQAy(nA@eqRgmV`E@HpIJjRMhO_!FocNbQH=i_j3D+)?A z-^Ls=)cGo~*MKPQ>g6-L4`Yu!$aWn&m-d%zFR;Q&!UD@OLXaG6(Be4IcvV`h6=%zQ zdU9;MZNgbjzuT5U3;PKA;bR5c*JW!Af!sqRPet;!_zl?D1I3yIoCx52l{8k+o01ef zef^|B4j&J(jXNvYUgHuSjXW!q_CY|R@xlolI8f!16_Z#XZe$ING z8$#`={kh}CqVApb38S1DPxQemMFu)s6I^p>d|?_cI?TI3Ef<{rxa#0uNZr5DUwcqG zhW0e9p0&a%retiJwbk_G!p_TP>d3AT8nKCsXIMr3;Rj3OBNj)QB7 za;9gSr#a{StzGXso&Vl{e74=%>-IeBS-;=+My7yucAq5j&|zG$zl0P`&$f z>jDjtbyL2L$|XJ(1D}c|K2=FHZJ_BB7fjkZi82c%tz{Nj#dS)mKDy^RNoV^$-Y#d! za2=FKqC&rt{1`wUo-=A}ecJK7<#7kO2U?CE{Y75el6g?|3;mBd=g}Ic6`ww(_1xAD zKr~}k zsEBXxFy5m4?S{8%VoE0bo$wc(FI$I)CdP+q(sEMsa#bykM;s%<6B8oD@93ysXbhdE zj*r)Z{sE3dlyDp^luXHS@!EJbo%RcfX{Mtz=@H?(Bh-$LZnjaWk!jJIjL^{C;c9E= zWh*I9Cd?+LZ{MDNU9JH-b{OB70?Znm4pmIIvDjF$xh_EyQ=Cv*qPl&tk#MP^;((yn zWb$e0$!Th0ejgUL;lyY)VJW+AowI#{CQcil5+@b~@66t*UhlZ|TMZpWw#JWD1;+=4 z`-&8hO`dArjwZGu;Q_4c`IpqVCUZn#`_#hDCD=Jm%qT8SD^=gQu!?@5q2An>`4j0> zk@75^w(Qk!8e+kb51N0tQ(C+$x>)=^&ZWdoZLw?$h~*V-$n!-2QQfGzNk&3R&H`$; z=mEbgH!(Lyb-1>%?w~j}_xN}9>WZ>Gg@u~$a(u5wx68thoCr9iZZAE0I4>zXDNhp+ z92&Vp?YSj4*F$se*qJlmtExgu1MQcVJs}TG0P^> zX@;?wI$s(KZy1XUhOvk*7LUid?1izgwUmqt$sE=6j3z4#qip+D8f9Ql5U?k;1_tG$ zQ*fZY`TKKDtCeOx&@UtkN-Jtd6 zN1e-v*oe50!~jnkF(NrSIVMI0Vvxa8NYD;3q4rQxwVI6UX#O!ey6QWfi0l_;XMc)Xfb}gQ<;~xw@{Pi&4Ab`K2GHkw!=|O+?A53){#&?S(}`! ziit^x5|fg(N$SZ($+wvN+_XILSujJNwehcmnf%1Oh!|DUoKFgwu(ZhBJeA@hP=2!t z5OBrHhx!RSQ7zi>ZQ)|I+0>JWqnTt3-&t4N)pgBhRR{6_uD9nDB1IrgX*pl8ze+>j zoEBm@yZI7D%X%S?xPAK3I)iST094ay5AsR{X_kJbBdNIvB>3y3!@PnWw4U1|?cnWc zA(~sez+(aIpdKk7blvs&P7^I?5$PeB^x{#0J|S+`gd5j=?L<0MuvP#;>j=uKm}sb!8qpe z<|`OFl%rl$X4XmvFQ=YF#*m@h*h>rvhTw--57=%bCXvybR+iy{x#-^-I#x#e`811{3dPp;{~R-g8amjMFN<$QGY&0O`}#0ktf3>r+sgcHg{z6GVRDl8RFKm`}SdJ=){|GWZoj&l+{YtdBs?*o(r<)yqEBC?0~1!MZc( zEb#rAK+oud^f&m6!YYRyjzQo~ta4vbWaB1uNDmjtH6lT-k&T*b&$nH@)#%C7svb zz@M60$#{NT*9xK(l@Y~){vT&JgRg7mQC25>vX(QLy&i94mu2ouO%Su=oKI6DwPYQV z^b*zHND%~;r;Y9vK!i`O&p}1{!Hz;u1$=W4zLB7p3Fx7o8I5|N)94qiNvQGcdC-iw z?Hn*J!9qs~F)sx~<4e$`M&3xMN%BKZ95`LG!HKJw2H4^&A#a0l4WwzJa*{Q4DZly* z_`UfCq4O`H3gC~km%<^o(0c>6nK_Tx+_{Gg`jyU7)DB?8Vn8nw@0tlSHwevO`DhZ9 zGwk7a|21Vc2n~|r#T2%m0o$M7|L6XJI|Z?mtK@+@06)%$A3Kpj&@}wjWYIKO&R~UH z7N|4qBOmtRM5e48unGA9E=-cx33|mNTe~JA@L$U6QCWOGbfHdwikj81;;Wj`1^RofRp_`-|4K5smBu4p_6>LH0^$r}0kQ8aB zLW|){?9jI)7!Ze0V)N*pUi~gJ4uSqq}3ie1O+E7Rt`4+stE1_L;I{=-99gc0C0OLe=(7K<%yuXdTA~B%4MLt`q>(M zr)-7hc<^NCj!h;*d;AkX16{9xb|UhCM=)dhDXE{v2`+{?APEnr_6ZLs0uD13 zaF~hD;j6!J7=!RodH#$49+uhgfEgf}z8gCmZtVGA{~R;m##*C#P&U^~%4XNqsJ>6x zeBYpKzW@JMHrIo)*$tG;*ygb`A&=9cZhW>heaMi?;{`)LAl}3D5BMX>L02WijT~Zm zrbBLo5160I4Ecutf;{xEAuY($$loZz=r`{NUY? zdj1+Ba)ujgjMo_l8J8MYL;THcu;ILd&>L)GYm#SDW>Rf(%;bBMW_aPr7@o0UtRPKd z4U@!VF!@Xk7<4W&9n5|9Lv{rFF>4McooVbGb_?sqMzROl2FR1R$39`-uss~hVa}8r z&wa(s;}&st+y-t37s~DCa=9{YKX;Hj#hv3?xi;?a+ym}6?mse4_90|Tn90V;X2`5$ z3uHDjJ23iehKva>*-lxEY?mxcmM7aQD}$_w8c3UHkX?YpiOaHf*?rkF*=yO)vL4wV zkUOD}E9E2PqvR988uX=np4?WxM(!wgmb=S+%lVAB5#8X zikpx?@m&5|{!jTI$OIuY6b(lL8jJpB$e@^tW`hFW2Caq^3TNbwcJ${^#G*u$g3`yJ zfSrDRBDO(YTu8;axWm4JO%1YgK%2qIc$!%8oi%mOL~Myb8Ck}5w@($^Js8ENf{|&l zsd2F?f6O29Vk-PgeEn6iNpZVk#gheXjh5|}?ISFgST1Q>doq0FF7Op(sme!$aqujT7M0o}VrDsHl+iHAI{?K^n(v|gxR*Fe1=E*l8 z+RVyI%gRz!xqxIr(sVMx!7-s>s?hAPBH&f#LYhQOc6MTxy0ZKj`0oPak@ES+NcmzF z5O(;7lY7soOG^`@D>UQ}7hxf|{^pYz{HMqO+4ombm~`pq@k{AU>OY5mp^DK)r^bln zDOmOCc{c9>@gnS5GA*+_b$@1sjNa@Mykc_gy%V33Y`)Z*dH|bV$CfO?iv|{W5)5M0 zD#0MiuiO#NSD(+ndRc?1E4dBgpRvGvx{e*Y7U6mbZ79@{Or25ptrJ2g`I--t(hS5y zoNjy5;)CMl>Z*q8ssr4Q!jA~-x}M-4S$Ek>TTTqxj~$j0%}Lf{jW z=Plp`idSw7Sg#KBpCQzjf}C^fQt$}7Z0(BDs+vzfy1uj+1^L0Od9?(-yaiuAzcp1* zd5hPn9KPP=Eu+5U4N?0u|OufePTq){uX5kpcu?m;mi8g;jjV zeLmk~+}L$TpNkEUb8%FHk6#n(jqcV(HG z#2uSK&9&JKimrWTt^)4-vJdaZpY(lrDRnn8@Nbczg_-n+-HV*ML#T| zSi;C0=p>Pr>21k$>Z+Qx+|D1-Aw>{CVqk=w5eCj_>Lh32oN#?3%0v1pK2q=!rtg7~ zhlGy9NF@ZoaA8KfD{SmzYx&>I{9Q|=tdGu6vBfy|K?X2ieAiJfn_GyROXEG+q`lbA(vp%ad z#rv~MD^wTOS3<&pew9J{o}z!sN-+(8PT;^@Zv#UT!$>GyCaqa_E$v)&Vowt7jA_kA`W|zP?O?^Z2Br_Ok#;8=sjI9aN;dcK90cspiC5iGFFL# z0)W1BHJSVbav;ozd11icezdxH+U75qNj_2}{= z!jV`V#7BG7DNbyM{hh~Z@}Xoi12%sUrS@@F*=NfsXX_`?JL}q z1$U{0y#0mzL)GiRF4s=iYRhxZ0`CFp_kN(^y3pHoR8RoN`uG?uE!u6qR%5+tyCjA8 z?K3r3Iwa`;L>ho|3c!Eh9L#`okZly41?l% zIrbbP=X!1FS7e3iY3Bhlh%mp0jE0>N=GkUCTtjzobol3+AQOi!h70Shf+USjFs{q8 z1j-sb8H7!RHybQs8NxfPA9~jtQAvf~*N@@vK|R}_V}W@epW`3}0?ed`socNi1SB9p znVSGbE@lsARF(oV4R_r%1mW)XM>^OixAfBXXA+PvV$)U#B?v6RMSF9KD^yo8Z;o)M zBxZK%f8@2nVT*7N5EY}}uubzqC1awwL^}v9u0>upg-ak|AQ+fK3hxrsV=|rAnL=n-wiC zUChR0L4ygDEj0tj*uQmf?1Mic9j->O4Q1d3fxAfz#Yyi3~q`uj@NLc4*X3x5Ml?kfMUD_a_J90aD8?gcjMF9K%YG% zpVsM)k|~&2ahPu43=s{z&E#SegceviZeP0C`otws5!vogFQqMj@Q{rAwhg~T-nf(e zp@Y3@@dCfi7&w`mIx_!OPM|3kCrGH8gcj=XqZ0zw-qf~1HiW6t3B%i}Pr88NnMi<1 zX50z06SLq(!{A1HKPnI#6jP@77DxawjcS1r?Z(7T0S9o=k!GC{hW-}Nw87oRc;WJK z7}}2p!%?@!ZSu|15EUk;;VHyH@d07paN>7heu3=eCB6(1I}qB@h9E2D9R_72ByBXF LgmH40;;;V)H^7I` diff --git a/assets/fonts/Diavlo_LIGHT_II_37.otf b/assets/fonts/Diavlo_LIGHT_II_37.otf deleted file mode 100644 index 18528eda37c4a35c88977ab405eb97f0c69416a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33404 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 Msr29Khg|gk2Pzv9i~s-t diff --git a/assets/fonts/Diavlo_MEDIUM_II_37.otf b/assets/fonts/Diavlo_MEDIUM_II_37.otf deleted file mode 100644 index 1cfea27f762865ca1e288f466c9d39b230c9dd38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34696 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

`&} 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 b`$qaOXYrD=3^>Vs;F7j6y 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 F{{WN<;v4_~ diff --git a/assets/fonts/manrope-light.otf b/assets/fonts/manrope-light.otf deleted file mode 100644 index 2b319153061ba63951d431fc7129cd96f262d739..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70796 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 z 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 DC*d*Z diff --git a/assets/images/Wolf_Logo.gif b/assets/images/Wolf_Logo.gif deleted file mode 100644 index fe14c7409dc2b1686997ec5e7f145667d8cf18d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206508 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 diff --git a/assets/images/custom.svg b/assets/images/custom.svg deleted file mode 100644 index 8eb5fe6..0000000 --- a/assets/images/custom.svg +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/assets/images/delete.svg b/assets/images/delete.svg deleted file mode 100644 index 8938fe0..0000000 --- a/assets/images/delete.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - background - - - - - - - Layer 1 - - - diff --git a/assets/images/gallery.svg b/assets/images/gallery.svg deleted file mode 100644 index 56a2e0f..0000000 --- a/assets/images/gallery.svg +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - diff --git a/assets/images/import.svg b/assets/images/import.svg deleted file mode 100644 index 4507e6c..0000000 --- a/assets/images/import.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/images/info.svg b/assets/images/info.svg deleted file mode 100644 index 8a71b01..0000000 --- a/assets/images/info.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - background - - - - - - - Layer 1 - - - diff --git a/assets/images/list.svg b/assets/images/list.svg deleted file mode 100644 index d3d8453..0000000 --- a/assets/images/list.svg +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - diff --git a/assets/images/pause-button.svg b/assets/images/pause-button.svg deleted file mode 100644 index 8ac6ece..0000000 --- a/assets/images/pause-button.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Layer 1 - - - - - diff --git a/assets/images/pencil.svg b/assets/images/pencil.svg deleted file mode 100644 index 584c54e..0000000 --- a/assets/images/pencil.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - background - - - - - - - Layer 1 - - - diff --git a/assets/images/pencil_green.svg b/assets/images/pencil_green.svg deleted file mode 100644 index 53c2bd5..0000000 --- a/assets/images/pencil_green.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - background - - - - - - - Layer 1 - - - diff --git a/assets/images/play-button.svg b/assets/images/play-button.svg deleted file mode 100644 index 96e17dc..0000000 --- a/assets/images/play-button.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - Layer 1 - - - - diff --git a/assets/images/question_mark.svg b/assets/images/question_mark.svg deleted file mode 100644 index a90b9f9..0000000 --- a/assets/images/question_mark.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - background - - - - - - - Layer 1 - - - diff --git a/assets/images/roles-small/DreamWolf.png b/assets/images/roles-small/DreamWolf.png deleted file mode 100644 index 67b7a20976a5369f96de952e9efa910ae5fbb82f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2286 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 II;Vst0H-O#?EnA( diff --git a/assets/images/roles-small/Mason.png b/assets/images/roles-small/Mason.png deleted file mode 100644 index 7182d35674f6dd0d80910feaa978a971b6ffe47b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 753 zcmeAS@N?(olHy`uVBq!ia0vp^DIm14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>I{@#?djqel5y|twS#$w z6$D%Z3lb+a%>DL%|F#1R$JG49ZInW1?OriSr^bXWSXzB7%nT(QuUFh$+c5I&U1II0?F1P>YJLrU5e5Ts8H?dHt*omiV(vH18 z`&E88Z*srj(*L0R1Eb;>HZ?0vmlnB}yzQ%G-ihb*RrdZpz`YMJ0^u!7AI9)u0DTKv2Kcq$ikAE(#)2Nd&)1q7Fe(Q z_VnE?cFh83-N9i#Y29yD*Jjlsk+6nvU`(o(xJHzuB$lLFB^RXvDF!10LrYx)OI<_L w5Can{LlY|lV_hJ_z+n2qw>2mla`RI%(<*Umc=jjs08j&ir>mdKI;Vst0FbsMRR910 diff --git a/assets/images/roles-small/Minion.png b/assets/images/roles-small/Minion.png deleted file mode 100644 index ead2d61387c755c52310e7f2a79372cdcc98239b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1025 zcmeAS@N?(olHy`uVBq!ia0vp^DIm14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>p1!W^ zk2(2S_>_%ixpp%!FfI0UaSX}0_x5^b@ht-ZwuF;R*ESvCypj3)e|=_nduh7SwS(

    n$!^hMWr8X1O&&tx5 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 diff --git a/assets/images/screenshots/hunter.PNG b/assets/images/screenshots/hunter.PNG deleted file mode 100644 index abbb19b6292c1f9452c60978756f49d41f3f309a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34001 zcmeFZcT`ht(=UvQBA^se5T!{Gk*3tpMWjhnP>`-SO==7fkgkZ*JAyy}k={#y&{3oZ zLa(7{=m{aT(979)KhGoH=l#|?-&yNh-#Kf&e;5~G?|scR^P8F9%r%?9N1DnPDXvkF zkdRzdRe7LILUIa4LUQKT`E$TGO6DZT|DAHuR=!VCh+hluCd0z_lUfFObV9$Opt>emF8Ph=Q{ADy!IUgv-aIS=8H>B$X%dn*cWAgtNba5f z@6Z3+HITrM%A%6JN<#9vaAk+@vH|QEaQFUy`MlWplOs&m>d-MH;m)Fvs4dC}`aM)U zM=_Wo&jJ~19m}Wx#i4q~QgjPzJ7A%cnQNNtlq|V6GF=CicLU6DkJ+iMm=;?LVaTTcHsEf2L>gM{7;757SoZn7Nr!&7eNH#L8_E?LrW6aVvg|5SZn^9 zT6NM6KX>P>XpiOl-Ud~sjWcrE=mJv;T=glLJja-UOH2_x-g78BA|Rl6CRuH2zhY*e zQlJm**!;1-ZXHf~{A78ha0zk2T*k>A>sP&8bQ@yr*(Ou_`ENj{NNg^+C3N?p!Ft0c z;!{#`8tKhEYts16-pL((lH)yI6}IXc+OqCAL*cpJZ@%#2`-a(`L-nF1VW02U?dSUy zJ-%(L)vnvV?N>h122VHB-E^N9mkfY^xLW>K+87ZfWiy;dotyLhz#{kV<}!bkWO9`} zGd7-(#x;Z%GD=@YGubPb@pq$K=Pt^rhDzIpT!Ri-+f>RhkHzk2&7nR%5-ZX+^CJ57 ze8F8y4XfIggib|YA4J)|w{WR1d0fx{E=d5{_*um}SN?3dK1dWFUr3r4S_|pjJw!dM zL~eJCs3a{5$7-fj>EY;&U%F$jjs@JP)oWuy=b>^OsK2pMeZXLc&|3#b3`OIQij){R2d; zYw`|HadBF@9C(wDX@WuN2?R*s>w{(ISqZ`zZiA>WCCzFbq*G}#WLIv!s7$xqH z$c=^sLz_B&GE6=+q|>BfkDag|o4$j)ZS*1Aq*e8Oto4trtcPAIN=JkDbtTh+DQNM8tcFuJ+WjIHfJ(3mLA!+aRmS1`8Nc zo+u5F7eQL~iZv&$T?&a_|8~oGw7&PdV;ydJ&}MoLzvEEDBsDeiMNGE(nTAY_k)Ekb zHZLUMn_{9~&T9RRz<2N?9MOtpo7qO5N*1HSUl16_Rb#GAi4i%I5LJ7xW>OxOBXqyZ zn{P^HWBf1Z7lb<7@QlJ=xOXzhFqR&4@x^j-T7jB!&>LuhZ3D% zJ^FbKziXI>DAmw&v8MF!y9R${Twfk(Hh@izGJxd~-@wHueB z{+1(AK~U5&xM_R*wkX;@_1I4v7Tcda%HX&f9khO4)W+KTf%(RnA6ISNd7K6fCU!$N z-C||D{H}q|rM8I`)ISZC`zCSIy+$u@c&g*$=+4YV#*bO+J8h6EmyhoI@kA{2fWs`! zpkY@Y+L|AHdbylAHB3Tb85s)tK76J(3P&-MKMSF5A=-P&txL>@_peFp`k4!67M_Jr z^gZP*VqNdDeL!OsG(yetX#u6>{N3k<`Tat0#kf1O66y&xIA{iLB{e^d zD+=|tG~~K;{8Jyr9eq2?UsZTV)2LQf&(w)!tmlD1e1yl;T+erZkNLPX-6kGHPiF$% zN?M)#J(%^~@v=7^ey^hT=%-CPt`9O>xy_-=X516iQ-jN&ZD`PBiMHkJpJ7tU9?f$y zE^J0jJUf&AMb~_m-ZqSmVeUJ0`k`ota6?IaCQC)l&)L*dTgF*86X?x0(ri1+nr-km z`|FqwuEv{cz8q{C1vK@1<|1}f+!06N^I8RYgD@83U%fZ?^a~)=R*H#K!zPQ$lmQzm z1~fMWtrGOCdV8Ck)FoT={FgDo*;pg6)1BdGuF`k95+*N0&Mo;=ktsA43>IL2<_v8- zdC6twS{eIsTxyh4tx*VuvD8)UQ~AmRmV zU?%;tmnE(yTsDd@r=t3s+jrlFl3>)|`?7U}T~%kh!;y*|7B_UMd%={Y2}J$`5Kb40 zTO%7B8^jI778nkq_KA&{M0Da!)P3GDx%;5Qk{=b9y58hwqgK$=6o`WuJF{~j=K=Hb znR?~Hs9fLDbDu>^pp!RolJ1W33E&6wLn>PFVxR9ejqXx6XJ4VQqG%dM-=0vSQHU~t{ySC53{KG>;0N>)#}$T_J#wygLxb| z{v~&}Gj~ITOJ4;)$qU!E@2FN%lB--xnJp~nb^C~UH_5G4cYng9)wq0Zvd6&F^`-4w z5E|A$5H;^;s%w2CmO13o?c(iS%ksop!`Ht8kMJ_D^p?|nCKUT~7Lhv|-IDz{>vDK8 zQBJcU2NQB9{JJx^wd5Ri)v(XB{Y&-$_%&75j$Wpy0=0AQ1>L6oI;q8u5qnct*E zRhH*Bx&)OA0@mje!Hr%hZeeKV&{!lyA@byT>!)wG6Lod#>qi@L{LSv4HuJP{ z4F$9Ji{H>QSV@0M-*$<9h{$VN*S@M4f5m3?ExEr*wYL4;I0~^sZMyKyI9oN8EWwk% zz)VYeRH}igoE?{187`0*9@ zz_P|r6Q-OUxK%|B%3gazio0fIIE9G2s2SuTVc?T)Gg7SyJA8g99fXR81`mF^wll6J zO%&lTfVaSKNv2^2H1l`4Vd-(vu5Ptt=6HKmE~+Tx>a@dntQXCLG$HKRpbhdm`g@8!Q}{j2J2nzp$RggYbC=gw zE*?ea^_d;h8@1HviZL5`m>KGNUYyk7Oe8=U&SUI!1hieHtypGt-`^4`d&cf4I5Ckc zCg4g+AN*yE*hOzrZMp$`rmIDvk);W+{7d^m+~Y5wTGHWk+qeb>66)%sIVTbiGi+Q%#vaU>zWr z7RyPn0JttWjiP7ha>gvbtFU*(=ZAqs`nokHFDG6WT0e#;wKhp#v7Si7LS+6bb+j#A z`ScZUbI@4w(NKJcn?vh#>R5CQ$t2grW$qQ~;E@`q0!hrCRB%b;<2{;gGF zZ{1_trTAu>e5oJzlSOT02vB5;=cn>w5M^4(`V+4l*MV#L`d7*KcWt^2ADN?zydK$$ zG=>|9>cHJKHqxzE!~KoHc2B~mMC6&xm?^8SEDkA)(;K*Qb#sV4@zAw@03U`XBpf_D zWG*Mz73``smq}o^$G`UpCg16mno=IaFcFb=-5Ga=#T))5Ta3p`H(~2s5XVehL)C{e zTmc?SB;sPES3WO@8iN&2-Q<^AFH4D~&l)_q9Wyz+Xy$j#NkJZ)AOuz(T^;X8xcWZ+ zwz8J4YXKr$ZDU7rVxK*i-g-jR%-BpXA=dJNvn;)FRCb#?RQaLS`zSKFX z{XDEnFYi4igWJ5Q5MZjDn?aSqcN3nO4V#B=&M_j?An*e*2u+mW} z`?65IkXR}PCSz6Tedi$cJ6xQd`Iy_&WQFf$IJCFtwRo=Eo~oL9QnBj0%HVO#-swm_ zw|o-w`amD;?7usg)qY&sQ8MsruQTwipt?{0`aNi=Ep5=Ak|FmxxRz&i-PSNM9&Tlu zlk(aIuUc1dT!f0YSzFDWI~?fy-hW&cAr|*34OZwYB}tE0zcn9P^C`qKKj zIMrzQ`kgIDHcgT@*IJ|>zubK)T(^!s?L>(j;4R!hbzl3nD5J;E*Zcx*`WwBiDGU7l z*V>3rX)E&gN_pZukmkS2B(f06JtpWL9yw5IfgF%9Yjs1GaXK1?)P6QdmstK{H|e7N ztK8#ef{q8;pa%79)bZko@8hqgL$NXa-5aq;q2pCi5z1@rbusMX zZoc8ttF3wh>4sIY7B{oZ&1Q#^(!-BY+wnA`TY{?4}fgo{Qo+x{m$GlE;JLYRne&XE1{ms z{otZ_ZX&qqDRS*YwgnI7a$twOKvweIhs>U&rRv9pCW#8k|BKIj2`Vy@S1T50)6Zh; zu6S~LUsa$wMRKbp?#j4p3EU>aYxNjH+RNl41o zAz?d?R2^?YbZ>8+`do60KKln!wpbn}KcjhTN|lVHM}@5i%Z4f~%jBazL-O*4r+&Y4 zgq0hO75R(C4M9qhN2c3rUC-Bg5Js``Fhw>Ek~Wrlm@r$nHjUMVKGHW!7^Kv7QUel* z%|3S!_0gJyK=K_`enW461Gr_V`{t{a4#ONBmo#G4=O?rp(JMXawd3Q@{YYA%|p4psVMuNP9e;^XMD zxmnlQQ0hPB$Q@@i>9r*D=-ldvBcC1o9y5eNcw z4}o`d2}MInq}L}ZZqnglo$h(U)0oZ!cZ!d5r^u-IbI>x3Ufg^n_r@yqXD#Dr46~)- zli1D*Q0N^|S!<E|x7)D*B{VEaYc-*> zn`)C!J+&Xzhv__O?BfpQ(v-+KcR{u|-FwS?BdM5##CqEDW1>YASi+4y7~W%%$dL8C ziRbm&708fG25WAMiq4%rzWy<$2(bOK+3h9qA!M{28p7Eh_zsnf%pY9nFMVkcKV%=<7Ew+$$}mDRN4Z z2r&oUD*c-~mI%qNa?R+#>cK#eDC6av-wBRi`NIox80HM?_r zM}txg*Ba1=KH1`vds^5cxr6S>9TMV)f1%VMX|o(fF;Ol*ED=21drPDkq$bzIBnz3- z42bG2(-gNqIzwYsx6<^%M{nLVYrxO&&_6c2NS(`}mT(jtx79_KEk2&_2v=X@&#w^^ z)a36rinYW`T5UF2r^hp7IoRt~)?2&wZ%RFX)b2hW-Rs(TA8ppeGhV)Vv=*D=N1N4i zdG++bwZFGDhcD!(&6>b=2J5WM@MH3&9?Crkjm-QJq@zhio7Jxo48lx9mAUg^<~*$UXmNr`4)6`Vl3a$Yn`M?U&~w-wplMmOb`%} zJzglYdC_Hfu37&jlwC7bzFB|wRLfQCSjNZKgZr!MN41(HNFYO$`d6~{r}WN!{oAuR z=ha;c=&jnJC?f(BWfgY@H>;a4jvGC$g?JgB;ZAGv9_6OuI0E)p_LB1#HH{US6p@(zQ^TnPfe^s>GPoi zp21LU22Gh&@DV~>V6>tEGRWSFPXgZa-i zTLTep%hA#RR-HI>C2M2e77`>vsdeE#jHqZB2sfDc*fF*)P`BgrvLtBSyjtE|)V|#( z97*oL00{tPI3UVz8|PZq=bK_9p$~D74nNvQvYvC1aX|Jab6aGDP1UcZ5M9&`EYY(^ zn~pu3zcgbCBV*U39=((mc@tyz+Ady2FSW|@Y7n1UEJNT36gg#Sq813(mSLB!vAFeD z={$Px_7P(K>HQ(=*!A+87b~PE94)J#Yl#QUSeTRx*uV5T2092KaNIYjI?1xL$ z*!t4qBadTsFk&u{LD*3rnqpN&d3GW!I@dx4P>*I>{iMFOC4;s6$G#t9oyH0jAbbxt zmm?3vk$bSgyGTE_iK=>!yLh2x>1_UEu{r~Uw>H?C^vRn7LDw%e=`+Ja$~b2wGwZs2 z_xU+&ctZoqk$ZCr_+Qa;u!(2qmP85jCX03NT891{w&u|AalGTU(o}*ZsVmC>{jtw}0DYH-J#A)G?GJGBY0?)oe+pYR zJFrWKg?twENwgrXfl;rso!Rmd&|AH74(JR2X*fg6)Bg!&C8H1K{?Q|c; zBQwS9Z)kd$WZ>`anQyjw$E3vTQqh*iI1Ss!GW=Ut@iW&k7tS1OD;;p2k-o@6O;bke zj8QO44ri?m`dLnXh>Pet=_rfGy>L7KS`4yN9Bm`_P|J@Oe6K_q)p2BzAnn@|6K&Dy z`Az*lPyM$A(#xBz(vTCE=dc!@o3Cp9bH%SX*u+SMc>uc@ zTo#sYTLRG72O)D1ulgO|^IhX-ted#lqmGcyw8d;kt~C;8a}51B*C*Trv~{uBPlg7# zadCK>yGURIdy+!eX>%oXOGETEBk*wt8~ogjVz~7#|FLiX1_~XJB+s2#ktE{OF+eUr zKD+}APGU~-Z^&x`36k+wEIr28f8Pc~f&XJwVr9I#GjE#BU^?r(3xPf7E*|yFF6TWf5+}8vLZHwwm8i868XB^ixb#w@KKOlarrVKEV9LzUvfXCEFywxG9$|Xh3>AtLQHE_zdZdh1 z*e8B)K>tn($Htm%a$ESu-P~`#x#;xVsKs*oXO;&NiN9J==DC3vR!Q>iXbuEndg$s# zECeKdEJMI`0`+(X&pqA|M8XJ@$G|8gelDPY?mdIaMy&tFiNFCpi{bl$_Wp)wIF0H2 zMRI=y`~Y~N%59Wv9a$djchmrrSNfp+^@9nIqBA0O?)03Tj*I1bKA~+nQ}tvY?|eSG zf!&C1)teol6AX}83(%>Bc|Uw2)?MjT7{X&P@2yZbvSp~ZwYUy7Zz=1xgDU&B)K z)W!^)2W}v(QI{}y2&0x>dE<^NK!_-_df7=Ij9Lx5#l5fi1Gqr+yWf9u>+ktd2b+w! z%3Ty>tgmaw@;rPr!ezFCks}+6GPeZ4PEDy>0#)#$F&{yj?Gu3Rm~gpX|0QPe!}(WVcjk^FX3?-O8a1!I4r%^ z#$hvDXw22~9{tBg8|kw`r9T^;+dof6CD~$_$;AG|P~{x$;MsZ8kLNwy3}KtjQ;w-4 z#F6dIyi3YVl0vd36I&z0hBtFSS?ck*^a4iNqs1>2#&@NE`q51bi8(S4=y8`(ok(Vd z_h9ec?L7CMk~hWwqBno12`l3gm#%*7=NsQe4z{FjQSGE-2GXoNmWmb;-QN^12~B)S z(@a_TsK?b=>I-bL8eH{g311c|p!*K&W84wwwDS2oW_oYbD#b(YOd=d5X^2Od`gAn~ zMi11wt&6M~k5ohREH@TRb7j4DV~cGD>#+S=a)bM`f|_dji{=!)GCu)F<2xQezJbb=&`(UHKmQT<`8gfgf~Nt z=6cYbZ%ajwKvd0bHLB=`ux`1dTKDxXaZxFkrIjG3@k+`k2nP?F;~5p=cWSwphnt87 zx8bwxC*vES<}oIx8GS|HyCVgikF!j#4eL-v_CzYfaw55xMxx*)!@W$9-HLC`g>a^N zGfdwYrcA%VyG{=#=7{effP3yRPeSD#|GBx?@2=`bG*)6Ky)T$w)9uY(@pnyLc@wTV zx{jB&8~OYw84mYnOTAEVXn@o9Ppg+o7p^~biCMDd4i7JBw$?%*(ZmtsZjQolcdj+4p9fWG30%0%?&$2YxT8)o`seOA&RCbM2-G>B#^j%1hE@u1KzV;I zZg(E24QQ+?PN3l8TYl8rGas5ui&KhqG+|;^NfIS0x6ehQChN|{^<=rO9;C@iRFBN! zp(DhxetSGWUd5aMn+=jZnLQFG6Cc$DO)^H_-uQ#t$?j=964a_}1++Uu7_-h|0Bn8h>HpmY^))so>CSfF?lbBTA1iO$fd3s`6EHvU}i zK4ae1Q2)9FaRGDT1eX;f4S)lO1FR>)F#xi&G~C-KktiPkqLx@7EqZAO2|C}kA?u;X zV8f*;wX=V9YX8v^E(xg8>g4LZTyq#8lXArD?V%VCZmKY@T|vQ+t=J~Q>EX|a{r~v29c1j7Rm1|Equ%SB``IU9j>90{$C|KAmK)~o5_YL$$gz=9JLDGR zX0x|r565um#>3Uf#nzTfb$b~3k_SgshwR{;qm3P%NvOq@KR}6UEENmrZ>Z<{)n@$< zD)pZYorCNS$0?o&#tM%BcLvx4H~m9^$QB=co3u#uei>m9Tn zt(b})?eEAnSx7ET-bfZ0LHesB?&5&bdi3%KW)}lbJ{!DC3=L!~KN;%D*L#J5Duioy zUfgH4fR)Ayn5ClqUyEH(BzDj?xas*VZq$u;N=-jKo4(3miZwdK8zi{t#~Tg=k9rL= zc}6h6I3k6m&wEEq+0I&UQEh9rXyw9};Gl}`SC0NJ*jxd;DA>p&2uN$lELK)QDJv8c zp_&9z!3mJaA^rfkq@~Z5bj4_7>(+O}KxOve5Kh2F!fyRJL|PR}_X3ma&ntd05qA z0Q~B2sOclpxll)ovx~V-trMbk9?qMw%etWabw2e{sJ{JaQq{68DFrsQwgrf0-}j~{8E1` z0HlF)0%7ca_n-pc4)Uo)VR*QjpALD`sIx%*_Fsc_9KId@ZGW5a%t$<2u&p*}Wb$Ss_1wr=Btq7g*rLp=4P0KU;FXZKV08==}oFVDF z@MY9%OA`_yrVKl(_X^uxU)K@!(U%)Eym?tC{72%HQRg8Av!rv-LcRp1M`zlMbJ(w& zQAV*pZ~jn}7iC*mb#p9JOGIveMZpN@@2fX>A3}3=_FNJUBng>eqj~SS6wCW3e<4Dwfygm#gvo34$p1#-*ZQeiUS0Gn|ZqxGeG6vbhS#jNl|O zwOGDDB;k5sf0-=xxY^e0U9LDIjB!Qp32pyKngS;TruC60zi;l>lF17LEjMWf zXbp}cYL7DT@FmoO0Sybr?qqRL_jBb}-sC0eiJ(KVYyIV@!O5H{tx9_qe_M~Duax)xxo4W&Xu zikW-i?<_~^J#rMNBpwI@p$;q)8gRlDc*tlNQ%$Pm0He!hSmF0LW>RbtVVu^uNp+q(n2{tFYGu zM3A#Jns8GI&%FsrOib4L1ykr9%fkw1QDR%;%h-n6>y;FG(k6V(TIy@=QjDqYpnaVw;*F|sJ~9cY~#0} zgApkG^grc_rJHxi^{PQ7$Vj^CaI@4GSz_YJ+?p>p`b>&R6=q!`$U46WDN4?KzNxrE z0PnptiTlJs#mvZ>mX4|2=u;akX=O2NWbs&{E#T%(r;{qvG2_s$8h<=zkjamlPP5pn z2sdZwEjKQdy^#ZI*v3$nTOjGXeBFk#WjM$w9TPi>UwSo09&Cjnd?st%Cf%={J*mqw z7|1o8w2xmaZh>F~VwL`!rKf&lKLv~m!{zvaN9VLh7~F)y!9I$x_d-By{OQliGB`ga z{#_zz|J|GAMPp-_zd?a|VmeT`mD@Zu(n?YJkx6u0Nq#>x57hE_U$f@%0;{c#L^s?O+hG%!9V1G=P46NcadRMv5jZm4p9eZ(hgZ7~IlUF-F+{cHE zTKeHd#@A(*BR}7}IstkGR8yoWyEAiXX}&9YJT*yDJSrWD0n*&w*0Ofv7Z&Ehi;?+8 zy3b~{utwX`nHq_iVs;~+BZoiipaOySBrtZWz(+o>fy=81jrHbH`cHViW`tIAj7KC? z0#X6idrj*s(V}>%)G+459{Z_XgII*~7WL^;!nE{6wX4(4+U(>y8xN4g|IXxB0)!Ds z8QYsVSDWJV=pb{i{?$RZG6p(?{>+Z0*POq;>hj)WN*^o7!LoEN$5uR>sWy-rEO1hW zZmymYiA(Vc&@-|A>%OeAL!#5j9wtiy0~@Q0u68Iwadl!V67~GbZ=$s14xStU)%Ku~ zlxrAvG__7C`Gv=`tqD=(izCWOZgcSoXDR7l<8rJ{ViS-+4Cn~mWMPWFPBX(0LXLmC zx3u_CFX59t@+W$84QLngb_Bfo#!};<_{ z%j$__d)tPw;!2m+J_@?B&hr>k^p(oP{^%d^(qx5#(ZQ?u8cN{w<@DdjZPj03pw_S| z?IdUmXO}g%>C6>Bpm5pDev*h;DQt4UqjTWllQqq&1$mdJDiAtJQqb_xtnRmWzZdwk zjKG}7-LNy$-NQ?Rb*7%4+*=D@EQw3$Rk?XS8+O`|P5z+_py>kh znzJyGADL-d49dQHr?oX>{0Cbii^noUTtgOtrR01OzY|(j8$wk^svlNEs7LROBF^RM z6@?9>kv3i(cpl4YY5sPEUEPVw6h{G^5IT7ReHWhN`smjgWOXCGRf@@(!JZqfFT0BBkYZQn0lXhX6^+t8vvv5Enk{kLE>J*XYe( zqTgD$8dnl|3=V%~$I`7)wh6c+{_*w!!H00LWO$|tcyH(U=*r6YMR31m+gA>{!1W(z z!o%Yx8gM3%;DtUDuO|rMxR=#Oj#bw@n{^TtZc0BIa1=74eR0}lD_fMfWz}8O(NG}8 z)56EQ4YX*)&Re2MYeI?!xkp-W<$$=MJZ0Bd7tw=;K7D(j@Z}{|Qs<+CEe|;YP$bdc z`*kXG-1Y~i(b<0ICu-S|43^hFXuD>0ik>jL5b_smwoYLO=vu8cr8kX=F5B5bHn9?S z5?t!%R?==}dkK20z^AyvIWJiOTzw$DpX|wOknnEHCyTQ+CUBYiPEBq~afNpecB!83 z;dSD5YVfpiDMJ~rjD>SRtq=N`N6Rm}ZsbdQ8oxv?>nBpn?cbUF0E`{!>^VCWEi&Nu zA{=UL!>(1PBuKlJW=ar9HGa(|9Up9KHt8dzcNfwi=eHK>T{#&WIK4(N<7NZ}Ge+X} zEVJSlA-@2470|8mEkk@yc8J?j)hI?fi|-k}ZP(@P2Rg}n5w{z@0jggAi<}`)(-%q&2`hNf!W*OFb4~-EOj0Un51f&W~3q zB~<`7`Pga5#3RRhkAR_SnOG( ziA|@%*M72hTK#xs$=zN)mM+WKLah@?czm57HWlD7y zSnn3whS8qc(s7#W>@90KvtamZWb0o>Uk7*}z~R7ekd#qgZw0sLb~})t>2&}|zYGJ3 z+mFp0>|-P6lRX3`i&y|=_AIh62Owjn(SOm8q1@tLG*Xld**uMV^{p;_P_1v4{-o_SobtwtAq!ItnqJ3 zdAL!IZHAJb{I}fl{CS)p1TQnjQH(UU&trP6?-Cp*s)Fn)9B$DwSpg6tRKbP}>=z*9 zX%!s$b?;arij38hhA=&&Ca8O+dO0(4euP~U=nv9bb+J+R zh^3l4z2sDyZx*qtoE_wH4I$TW@fHmep+b;-~3@fcWIcZZn(0Ix6g zEy{nj77=as9|Qg{Yyd18vNU4CJe-=88O^2Xc6MaE%1PAJbM^h!y6uyhZ|5zi?3XL2 zslYo1Cy&|yQK%6H@nXQKCDCN!$(~EbQbQszC?34~4DtCHhUw0Z`07SIx9Rn`xY$qN zx;{rZuKaJWgPX(wPPaP9y*(s&Y}9(&c-=Wvl2ly^;{f%E{+{JMShJ7_o90XM6O<{} zZle0Bs~fph5>%IIpw)FugMWFngp^UXbe-f^M$BYqvWnT^KzQ?PwG1>3~*V zzx|TCT_%f5Zrt4=G4-jmXgwI+P4!2>`A&}~aZqovn;J{@J6^`$7N2m|VHyxitJyV- zbtSG*pT0IxqQHlu@}TC8iU?0OTjskI1sr&FC0p^Y$ASd_qE%n|ab@svpnP0WXk6Ww z*5p-4S@TY>GOL@M@E>CUxooJue_#ENMe`zUPNnao^h)T zW;y_Lo${nY0))N(JPykFD|o$0vnB`a?%dP1Fj~W*THU6xn;FujXzUt}lZUnc*@esu z;z!+`pcod_d*HiK&WD_6l|Wc!RRYBJ!k?W)c;s7q2^muoUv2fXz@Y_L*TpoA7CkGSl|km9qIzKL>g16Zut(v7 zJrJ2ug`_}AjEoXLacK0iC($31C-{%XD`t_Q0F=#V7uMwY z?>hI6(*`;nved*CS1PwwhIDw-@bXNab9Kj9s5S*lO zqfof9Ve+q~wf}PeWy{lLj5MkPe1=rCq2STjCWHyYl%vL}f2y`W{CJ*XHUSus_`?`B z8Za_-@B6O9x?i|ztdKtqi-N&bnQ#6yz()f*EBb%v{=a}AfG*Ibd9Eb1?VKy&GYcMF z?k5E_Y}c#tx{VUOLYe;sv_~*y5&%IPD{JwB0q^$z0}ua`hn4=-Od#feG{5I*&fD^5W&&-|eXxMguO4~HGM zt3y$h(51QY!WwIO;zBmdRF*Bdo-$^s*IT?Rxz0bkFpIBegQ@P>MOf+0)`4;ph&l70 ze%SRqxPNY=@V%~O)gB1EXmZO5ZvxPkq8lZb$1s6%#Pk1H_8x5UPi5yb9%rLLxRsVh zoPaTgL@oolj7_aK7FFBCrf0f1m^PMsxC9_OON(4LnbNDnYE}Ccs_nUDPE&cnT-1JE z#ZL$xUI4libcA_QV&HEt%2$mQG-U~fOO)+3A9scv+#X~fI_hV7)fBGY-38_l>QcxC z`$6KN! z?yER8$!Dgh>rt%rZom6J>W^W4`1@*fBvI^DH+Fs|>iy?%{rLIAe4hQMNOB`ZZ6|#LynmNT25@(hNB%%g2WTNAx*Z1L^!SnU7&u!#Bj6dzrm-To9e?^hjdt4Hf8M_;^m7r~P| z5oeCqvm=2xOl?*qNLqyL&f(-*`j00`vbj_C3#wo5U%~o9bOz|Oq;<+J#QepoOpZ?k zR~sM9iEDWvGvDv&9~}_n@(`2mdR|8d)QYpoUYlp3t!q!Q$MbHz8}46SmkhC<0`2=yS~YN*~f@?22BVnqZZ7=yi|y!v?l*W*ut57MuBJZKhkg6p82i z8V!n{r@C*(6RQV_6NsriDZTML+%>07F6=Z-M2;+{+I>KHx+)tTYk^X5+dC ziOC))Qc*%e;vj>m=aslfdz0!J0b*Zp;=oKh+*yJy`FO0=eI{~h0Uxb13xy)p5y zY}jlan1apa3f7~?BZYgt6NVD&>RetYPR}l<@tB2uT*2mnW0v~;YaUJw2)5eqeq0a2 z#tfy~r3~;u6Boe^bvkjtkQNwYzrXNZYampdPd830DGep%`XxbR_4a{8cS6-YuZ>>r z)skrYL=nh`NfsUiHrbz8A`Gp0BBdlI8(+7#Oc)$l=U&8%^v}Re%3i%+Hn}K~wPeQX zD0}_3eT`8~p>wS|bLoMQ@tm3jkDw2-WdCrFKy)cE+`8YpskXH`7iZePXNgsH6cD*` z3{l}UepI>b@FLsJVityGq8!J;Owa1GPsV?D2P3z;aE*vZOfBAtcvby#R}oub7>9df zn+xLV;Zl*^{=s2fk}Q?gFCx!u*q<$OPbkZ--(RzpG0r@|E|aCP z!HN*=CUIuw*6&8M!x#qMt}EU$l`Ddf1)EMW|!4{1yXL_-l5`f_U$J#;tS5Z zJRE4B!4TX48jK%!!Ed)a!7KU9b?G&?iDtyw*7$lvHLh@P`XFlBYcxF`8x@?jI1TQQ z-S4ph{)mzB?$xT+vhmd|<{EJQj_!o>bl;`B*YAf#hT5g6%%+70A)XHLro4~8xyOZx zVY*8N-d)zX4|@dBYrN!QuO}t$~qM>T1802E45i?00w_ zycCet&R{}%tB4i9Qqd*!Zjz!32j0R`SyDO_5OtYmJ9$}96Z>V;^SE(>j)t2z>LbU18*3?$E!Q1_)IJBRibArS{!>y~P2~sZiGr9*KEncm%z)$n@zmo7V zziyG}vh3NgupVVkE|HaRTQha6LQm4v-gCZiv=HcRmp}D7a4qK>&o<&Mgd!V1)1p$0ZYXJr9h&uMJ87&MYFHb(xw+ejUY-;iZcI;n zJl9?^HvGeA+}7H+D%0BgjSAASHOv)S`S1tQrES+j%G|@8aqE-rr7Xhw7C!FA%+)Gn z8-(UXzu1dAGT`xSt}ig z{byqznt2LH8Ov@IoT39}9V1PTN@0A0#7bm*X({Xitfc0WJtv>n+dYfJggB|sjTdiD z$&T>T3G@4z%2g|==~oOVJFrUa9o0vO$cd!y^*dW9#ukv46iLCa^@U2fqC#zua|OGg zj&jKyg~JBx{yfDq<>|G1z@P<4AuLkH_zt@IaOdK#kz;|FMZ@0jCNt5+qlxa=6U_%@DQK5@dM2EH;LoHEdCj_7XK7vrM+D;j;}( z=|Z_q=?N;HE+M05RR8gia((h?_yX%W%bIj3JAd|;PXn(2>uq1h*23|U^D;`C(!!?a zB;to+mcrHJrf;;WprWtvE)S-J=H!h@%%Wo-&O1ffgDD|mL?acT2BL0?hd_cnXNFg2 zke0C*+#}xJ(Jl&T3}K6j3=+Cy)y$lSDRm58Zlui2CgQ|fIL`YZAi>LpjtI4gWexq5 ziKr-RM~&tE^_wl2eTCee zjXfI~ZD%G5p1F_J!a;DW_QpwDRP!FLKBB?Y5zxi-x<~9)~y>6 zY^X>Pwy5+fRX~c;R6vRZ6a_+yg(4teLX)l(sY>qw6zMHW2@;wh2qX|Bp-9(AKmvjS z0YbeC-RJxEy<^-l?mgq2f5#vE5!SobdgnXKGv|CJ5=zIsCDKv0qxiEjT&%DBT~{%w zm%M_d2nE^9dcU0zd1+`%=lh{?>V&(fRBK_juk?|3twoxL51cwAz>p$`+|3Lv>KB!| z3jY%Lvn8Cm!4$pn(|_1&Ho^ab6H}V={hI|u_0A27MT(@%T+)-IFUhWVYRmYB-kWOd zBpsK(+~&Qz-8^{L9&=s`#kPgSM>w7xS)Njk*b;i*gE+w-VQ!mkJ*ZOwz%YYS{Pjd$ zMq;qM^^84su`0#VH?Q7IDE}I9VdLHW+ft4l{*F#-9dNt_k^;%Dc`7`n?)bwZFR1Xz z!Z_Q}fDU|xLEL@m@!n}3bCsN)02F993sM5Fm0|C-4nxQfrHT1MEi(tsK>DSB>xhr- z;?04Si*Z(kCHJ;|qWcx@g_B|xveJD~=nTm}ilatD;Tlket?(vPO#kGk|&1QB|w)A4r z(SS9vYBfiH^(6skpU9Q9$=xqmZenK8iQVBbcWlj{{W?^EUQ%526R;M#al?L?Ydgj7 zuspfUg}Cmo6>0{p*J)hB%}vmsAu0cXPxllEw~2qf=!rw+D$(ls2pfzD%<fme?UBFGH4h5ZDX%;{8|T9UCnwhU{=_Sy36d=@b>D#Wnido$TY50 zPPoFt%bdTuco!`;Z8tsen{t~ypt#A`Vb{YB+?dalhkIU2&4a$IUvJ|Vrhw7BI_^2N z{kF{x9=N+{y(jf#WTFVc*izB$dF-puUt4poqz)%)(y;G%^lRVd?bS=PF*i7IpLEBq z7qMsL{Z408jz8mqkLATS)4N^r!Zemk=+xMtEmOC``P6`;GuG}^8^90x2K*=W!zr6L z1k-wFdX&7sZ!68T%)^*HhU%>`BlL5f>gd2N@$IVygwrYniPy_lU-W?vBL~lK$D(OI zLOCJbsmr#5-AvTivFed?MW)bI_t}K{{;J-xAsQZ|TX%a+wXC+U-(+EH0`sGM-Q7*< zx9A?=oPqBwnR$MoMSB<O-x;pYn@ z46j^Yy@BgG|IX>7C$;a|P}}BDkZCTlbb}SR)_c}fOjel1%@qr|yAv)B1Rxp4cyMq=814>p{>FFDB_ThW#DA{mqDP_7}bq zXF(H7>sURNb-QlsVhMb3dDZd~ZL*RV35{q2m7mvSNVgCf zz1Lnu&c8-4#4X=0h%~2M%brh_6zxFvWH`{yZ$298@Xb&`XOhTCE-63ado2eg<`QYR zz>ND%KFiJ<4Hsr=lM=s)YE;ZG*IxjT&r{{o<|raDj`M@}TM3K12D(Yb;t#Bb!^ zzCS3lS!LU$fA*1A9lCi(r&iVKZK&jZj5cm(g=8eWDZbouK1sG|gPOT=UTGP80avAl z&R&{$UKp9A1-;oMKKetzQ}tJyJT^>t^g%(;>M+-a{E77R-P4iDmY8DlnV>$(?YZMB zaLQkuEYE)iQ4`*nm!uu!;It5*-j+9#^b6>+v{o2KE#!2frJ|8AAvF23=3q6@yoQ^VS zoi9Z>(oL42^)j-G zJ;98=Pe6ks&tS93kGHS<^5(&uS3qy6NxH6K=&H6&0ao*J!G3|Y2mWZTC=z3OfZ8YR zim(23ikaR1kDc>XM$KZUqk=;tj8Mt8-w&9&x5Hdv@{eXg7sJq;{7|L;=b_tSPG2QF zGhN~bJ1Xs(iPllQs#P`z3nX{@O?odFpzKd4Jgj9&ml>}l21u2^20_&gDf2hR@DGX2 z_`^5e)V{T`{-uJlsnPZ&NyE*s=7=8)CVg$K}UDh#x~rm$uZ$w|)Pl{O^p;Sq9DN(kdC zG{yAzVMCbCmHsS;T4l;E-nH3sJYZTg5at166O4USjHY^nJuWd__GL;&|4MKhmB{6OOd3ej}MHvguZt_ zfYR%0+Ql~?!mDcVx)M4THm(qTgX<{>^sys1O)Cs>n50Y3Dzmkj%@qFJsJF^>iRIbC z9FnKBwXc~~VLyL|@syK{;9TlloSc|W3RhoOV9?5qU$Oj~k3W-`=>Ai$I0vXe#_jx+ znzt;vHkg)Sc%RRlOFnR_CI6d!t z*u;DY;}>8+tScMpV;+Wt<{rIw{SFuh^DVb3;c_LW*wB1wx}`<0fIYpe`#nz;x^h=eS+AfA)9G_JRt^*2 zdrjx~CN(AgO~EDkDgR_UJI3-eRYvMN3O3=@=5~xV)e#go_oH_Gl_>WDltCSmpPbY@ zzHEsx!e-f7P7R)1>wtn1_PtN~jWW~WpL-TOJfAVszsqdwt$x6%u(#bTfc4_X#C=hz zqjk?}eQ73k?c$g3P0omP?v8JcU1<~V^gnD`hY`|U505h0N%7mB_q&nrXJi3Ko*A<{ zwxE`T^uvK_`R{iw>^oP^a;i+A19rvQf@Gw%tk|%EtZEm(HR@V?X=**)G~p7~EUx!M zQ>@7g^cm+B#-V|!!V}`OWsL|@d03NuJ~`$e*E$=E?&@|Ddh`J@bk)-hShei zQR|ebtTmV-fJ(f69N-)JaL4o{7rf3Gyb%0${nC8`YEr<^$KTdj-{g;;wnBZnmOUz>@(l@4E;BA{M;ZtHjryJAi-jjTyD#wgW4;1IE8Q2$Jm+lNY=7yFTIg8xAh)3vOt?FO@Kb*r%>QS=@IUQn zI+S!;>P-2dS4;C>+R^DVIkb9d%3sV&LFv;H%(VS~uu7BmfW=pg^$mDq8!Od4kJC?< z6u6zdn3b-$WMklSxoZ21$MVSeNk6FN>9wr*)lGeg#-JLWM{82}Ker=)8<@5{>70w# z8ds^A7f46SMWB<>)g+F>GfIx__))8=MIE-ndN)2s0X`FT?4G42J59SgaI^W6WX{gTpk@Q><~#8=&_Q`e^3fW*<-vFD z=jP1!Fu{5GIhv}tpq<(H@}oYCE?voHvZ5jbktHpNK9T4PR7!>!YO!h4Z&4AC1uOc2 z*1_#G4<&q)K^9kR^OYe7Oi$U`fZ3#RDqLO32Hmi@{K1-+tGqWU;CoBQz*)Rm@)@q- z-b`03ZuF(RQd4Xy{P9L>qt1xXHpVjyFo#`@nXo9XZ5~%jz;JEC64D-$?iUjT~#C zZl5nNbjS`=&lVy6<+merww3LpoZ$gfr=J9`-^223k*6a2WL`-;m3bxl>fAj!yh3fe z_*aR_jq#6C7th$K?ID>5U&n`(U1gv>o8u}5?VLAL_inhx*0$MpHxJc+o?e+w1s-9) z#?CL6=UZ?p7C4ZGP6+)7{I57(wo02i&U3UbMLm^tjD1hlMq0!6AmMRiX5d;>%KVG7 zi*26Vw!2zH%vjzWvL!fdqvPq;Ux!W>CUM;WvF9~kORc<0wVTbuys%hZJ)3DMyn%|! z3#2OLT?ValMsky%GF6r0h(x(H+NEQE3{rj}6cs298|NjC|#7A6fE2JzjCBL|=Cxj6C=JiA0rapIP*J##O*q%mql$pm>9y3z?6@&ft ztlf8a-lyO;2JvnB@koU=uj6nR%i$_lOKG4B6TB=#mSCL@_g;imKT4H^!EP?hp_}{W zl4zkxe&3#-)h31I%_O@`YE@gl=2b3$URF0!`B=$PsV3mYini8S-KNhd74>9B`$4Fn zV`wj1ae$u~my*TU7@=u#S^(mB1Bdnz7rYDVMwDg;pIuLNg~63dL$jr7Qr>+Of4VW@ z$NT+dlB+svZt;+Hc*~(H)1#XXsjSx@DQTl(HFj+E#0=Dk+TscF9o^LxV@L|7Oa0lx zq#m#st6d)AqUCS$4Hk*k<&_OmH%un(SOgE?x>WdPK7_=ZkNfBmB!)lgZ`uzz4?1G~ zwMU4-&5IM)Je*;9j;ZKte-bIDLLDi(vie^PhSEZ_LNgc(x|J$D>Pc1)<1YaArAl+m zyqOaSW~k{7gL9@)jPoW&YGz(%4_peBC^w^Ks%>tFM3TIZ&O7^6x(JBJhsVO?xr$=d z&$)^-6I+u!16TTZmvVZZJAHU&#jFqs(9&mRSFtzq{mchx9U9Zvw{||Y4bcGD6&+#; z*yLZYFcVcl+YTEej#Ro-mca^FygJpT)0&N~WnH5d=<8qh?>bnp{H+Sp!g(#q>&bze zqg7JatJxw>Lu|>T2P^| z-H~AeboU-=^%%uc&fTqW)K9IoTIM*fXP`eF7PX9j&^&eVwwx?{Uag$ZyhAjo=%0HuGqiut2^J+EwI9XQuG~3t*pWR?pc!~v zMWr(nMTK~pWO@bqn|8T^BkPD~nlnmHOuH4ULY5rjmK#mdLakmnUmwGF&yo6tkNu}2 zMmgV87$3DYhP*Tx0FUL|raJ`H=FX=cea|*qW{z+h+cwveUOg}2U1pbQ3rUoe$%(hB zU$J(kC0EtQ9ux4PH{>^{jmJ$jI7KPjouVJj_q3^qF{-FJ{zv_X#D7ZWjJotmrxmKB zIPz60)-%z#_L>~78M%F3$o{~w+w6rgSCU5$jqOtm4lNzqKr9=E?NgS^U1>SULmJNk zvKNFevZxEVvXi_mTh4ZvrXLcb6?#(#m*Mz|%<*+=qwfMX+GO?i4rWMVyq9uiJOY3A z4Es3{s^P@$Eeq=4a0>J>f`;3@hKA}LTc1#(RJZIe)C@wYF~qWc8j=r*lxC~sX+ z+x4E={dv!qi4O5y{Zg%9Vp6dPG#xJe7n7qn?g2#@RuJQPZnts5z)#?u5 z5gP4E-E*x47{@RCd$zjc=1>&UYEYy3vcdILlN7{ae)U^G_l5V>_MhIl0YmHRS=`fk zpnlz~c)qG``zp|^-HFcrNVLy3ElH%nT4 zbqB=8JCFg!*u?zybPp}r>Z5<2z*rQe3N@-I#MKzo?MO%&`W#1Zm%$%c{&~p%W0Lj% z&u2={RW_@zg8llz1%apwDG6!#FXR4Ia|Zr%s0{WRB%pRn&Ox3abzOA>*Qb;J)eJ1J zjR@TO)!{Tya(lx-6VDDQ;s2eZ2I*!Alm9K<+}S15d?tTXy8;G*Bwa;bSrRB#_*b6| zA*uZ^gA)K(mtD$Lm_*x8zCE()Tj2K^!&EVBD|#@}I@zO^hfkmWs5D2;to z@;k%a;4WG+$0d2Qd3U=)Ku>*ho|%eucDo(4Jr_&;7Q6eh0GeJt5w!au7klom%wp!^ z(pvLw8Q|ctrfsijJf0DyfQAV(gVeG7kJ%b}hS{nHZkwwvZr3)?Ukg~K@zPjA{*|>) z4d3`j!;C#=JQ-wiO;W8?4b|$rvNpbkYs}69@T_X2K}BghQZze~;uyUMQdQy7GIl>M zN`RIffo_RH>(_vt$a8T^s8byf3tV*qKqyD#XU@W;curcZ#xob*ey)ENOhm|6`7d=9 zT5t;()!R9){{$)PP=mtKA)5e`-K<;w(vDG8R?bAgC32Sg9~36(FrL5%HQ3pY`F57( z0;F&rf2;AwAIQ3@8rnH-m;IY6Mr1%3E(L4&ozGubO0Jj$DSD`P9nK#ZZKq_@mk&c6 ze;nDXd!SvE0nvi@X`_tC$-lAQzY)&9Q5s0J7vW`cN{8GRRM`6)oLxdLaouAzrtwEq+H-AX60Ty$}a`YQSQ}n%f)(4NKr#r)QNK`o_cFF zhs-SIz5b7V5R+U#A;}!CbnzZt3CR|ct8xPEdw85>((001Lz6Z0+_-lT{MN+B+UGd| zt6|yGM{Z&+huT|otg5~$CMV4`EH=8(Dto^^bb`^gQ7`L*S9V zXi2z}614dv3){X5;Hu)Fo#mW~%3!zCl-<1?sb!59>@r3h3)ik#Xefvx&7&J*_(u85 z9Zy}KB7}_m=9c}#kYUf}n5;KfW?@ziYQ~@Go2R5YC$t7LF%Rq^%Wf(m=m~u}5WHMa zxQ#+?;y|SE`oSr06+q!9@=rnQ{PGfMof$kEY`y37EC%iRt@JFf^S^q;7*s~5dtdm95xNq}3xf3e*`hj-AURGew>s9?yh*Gx8KFYwh zZycl~Jh{sHrHDzRS_@5cPuUPSIh|}un~Rpc4h5%BY^F1SLoVIor&|tfcl&w*NU0n^ zK03_!9URtnlE?qc`3u`PAK(5r)?EGnV8D`Jj^MD>0xcxjV~dXgcwZB$#aUn_j5x9o zfD*7d1zGtPTUT+%4Alzi6F#xP;%;Enos|CW#)qc|a@sAGveGThcEPps{|B!qz{tW)#s;3Ws zVL~YWqc5KQJ^op$&EOhh36u2kABE2UeGGx4ZlIlv31ND2CN5a{H9Ouq)lY5%Y7Ti) zz4sLW0!UDCby5>0B?)S9R{+KK{y${SzbQC5IcG9Qx7_Lgq4(b$&%b+F3{V!?Jcv~^ zAK`-YAV*frkd@M;ADID?7dDlUWs+d&nsN%EKS6IRmS5t#hB_!nIFP&Yfm9u=oGR7) z`g>?Yz%Vi(0Vku1b=k)!B@`qSE)*-2I*&>i1H_@l%I@WcZNr*Lfsa)ZqL$y6kQ*Mi z=H;@pL0%5VgI_4Sg^E{TE#zu*Y&(^8LI-tlz0Kzd9Mwt|HfR(D1##v4J{0_UDT3I{ zZ>pRKWm+SO((VHWe|54$oOrs#yCmQ*+Q~6X1kS=KezN2N1eV+{=&X%8$P`WD8|Ov- zcz;%wagX~N&}BC*z)m+SYiC4|gh}M?uBuAFDjIr(oMg8(2YB#}goe&V$p=a31NMcj zm}L%xB11l}&5P-1lbuyG;B;4yw*p{ZkRV}6p-4H5$nr^s5W$8xPK&C7d)(_c3 ztR3kzfUGb_d5&dC}f28BMfm*jVcOH3|eGn^q=p@? z8&-sp6j{O62DPV8Vbd22$E{DZdJl10&s9&&dW@zV>W>$LXx^URlC9+EzF>GQLj_PG zyHE%1gB)Ef#;b3m-LSQ}=H%{A+hD&^lFkjOd|MdNFNZd(jt@G#)jAu`07()~ zJ>~V{ue(l;(`d)(yxF;2Ps_|O*Ae|8C4+1IfQsGO1<*p~u7s5RrsTEAMz$u+Rd`xt zOif-%aaUPBv}fUkUedr4tPHGpBDv&%JW9RlG`n+Ie>Mdu9bqfHG+q!Gzc z!_(3P9Tyi+eiX#U(FPl{o^$rhaL)1#bM;@PB%Lf`*~CL2eCw6DPoxZ5 zY>mEV*k8^Nwyw?|v>$KT%bpN^!l zYlJUG?}H4t^@N|vSGlLt!+rfgvab8CmHjO5u8(61Mo|!uJ||!lnLrcfl)CC)KE4*7H|N-JG7N3F@!9+95QRXrus`iO(KXRW^_qRml zFLmc=out6deahtoXMMHKXcU?v4Y6f_A4uZo%$G1g%a&Fv)F@oF$V_gV#eeK7%l;8qIL-RXSCP#84B0+pN> zsoW^f9ZO_U`FK$r$%rGl23*|xvjX!X#U>dBi0x^(>C{gO$gjSYWiId5C&sipP6i4j zfeY%L_$YXl%(2Cb5lOMn-qJHU*NfW|uOnfJwR|_ZCL>uOUp}PrtIRiuC)@el#qNLV zPvk(7SEd}aOf51Q$T3}69IJXaC5_BpGWnz&xz%ZKOAPGRFLysgs69~%BX+DpHjFa? z^q!pN-VM+GAlU0)>vH)e2I!S@ZER9(#Vx2$Dd1piU#4}Bi#nH`LCTltrd_eg5SiN| zO3Dx<^-MZnDl_ed^WM*r1;Z7R=-SgKi>~Dc2QYAb&HfI7=>4SVhsuPe3=9;ha8Tvty%Szz3$oER`+Ro$7Z z5C%%ye&!r)Pl_-6g7RmIP)?t+Pm#FJFAC-{bUPW4;(QR#x=f1r zjOVzEp8F9wzh!@cKn&Yc&+k|*b=}k*#C2CFgvCUg=t}o4F2)?_9A zDn{%W)7>LYV-imk-1!K9?M9M2E)EO$i{2NXW+dcvLm*ONSuzt_;vWg<6L*gs0{!%o z0^Z1VXaPY_5?OX>w}p``qM zj(}Flt}9EnDY4iwEpe#TJ9s}N^b4NDZB^k|9*n0AmYl)~^!JZoSwPdr{#0w<%U@!h zYn#40fXnF~LvTCre_>84>Ip%~X$|fz|a$Mhd z&!=`Q+c7-(C@^GbnSI4(UG9JddmAidJ@7^fj<`{i(Xpm04JmRNTYUS`F<@HAB7?bU z>&f6j#Nc1T@E6~i-7l{QTOfb(Q-6i9m>Mx?eh`@a)y~Q_#XGj3-!Ezmf1x=CW+F1K z;rZZkuUKofTUP>FATQt6d46x9z7IK$u{mk46{Sf6!yg$4T$)WBOBBrv$gs|oqm5M- z`vV^z-zp{xpk()d7Hy874(pS-KFM_$QrZ44_arhe*~K|?BMnJw!VFIy zzP0^s?D_sgtm^WR`j4%j_D8D$#k$>m;zYF~WoU}}oq3r9V$0JNyf1|1T*}O+s}DR2 zK-0jf%}}LsL;8i;O@@7%io-!apK34V4WxrAHp8;SaaR`{T5mG-Af&QuC*+wHSyh(J zL8y1Xpbxejx@{cVpv(lH(2RbnY$B9W5XV)x6_o@}Y?5Jt*e34m57%0f9HL4tRA{5h zS;*H}Em7E-^u>a+U^K=1Trh6Jw3K8umshU{3WuUTi()wos};j}a_jHg4w2oFb0eGjbe{Qsb3xenS?h-^( zQ9Qx~>l}@C2MnavGb>Zas?S8oOT_>ak@560=%RSIaqLYy>>VLenFPv{!8oQTnj6Rd zD%#-v!d2*PTQ1}NvK>qWe5$;UFK+vUgW*$U5(DVC;1htJg4OS8w!xVB2-RSe5E$`- z3Xw$cgeseWPL1Eo#3)m2pl#{u;BjE&4Je3#zaM&{4QBcG6AqEfz|~lQVM~V?5rdrn_hn&MyUhR*<_q7&1q8jhqgY9) z`L$PLm8pxk_-M*h?fqqRfAj?1#jxDP$V>C@t-!AG5oGsP|9d}W{8oqzN5Fu#x8=Wg z<9x^r)lr?QS*kk@`V&a`)z=`BZ+Mr_1a@R>G(y41jg)fv1 zU9k7dN9Z3hMfu!4e)p?pu4LJx-e&4+1JtZ=~Rh#S|^iP>UeiHZ`k< z?nxP1TM$$#<+}Enf>aIo=t0{^Jl%hAbjeOo{qsB1M)hwBv}zMm-6pk9Bmds7zZBtK zwQ`Lvrli*G40`bF`73G~%VyMZrCdk1GBY=Htk5def<|5c)=v`DDSeL59w5`dbkV7` z*&9cj8(qtN;neZ1cOw_wbA1E*AJE7YrBat(m*;H_w;N?DCD;a7srg%&hTXAkr{&PT zJ~{S*>H3_Wfo1A9D#e7ErXl~;jH*_+Gb3u1Sh}0#Lj5rZeStsHReNxR;XhvC1+90= zla1x(3P$h*F4CpB0q2xA)n69k?G#IgIz~uR(08y#LA>imvTt#oop0sHl5LZ=V>GpL zv*xq^6~)n|7PLdVR9B%)opocCYgp~hP{ViWr&kmiOsd6%i zG%hG{u@p>KRco#ub=QX?-QY9VdzZI}$8Ka<4q(K0=tfys<3>xN62X3askTjTiJ8$O z9A^)FZkTX$a2%t4t{&Ftqqnd)HM$>saeriwz%6hK)zor@J{Nd{`ORbBwy-+RJ1PiQ zA2|zx>#t;4Xk$|s=WQEeuT%`W5vv-oUDfC1YI4cmcRE%Xrl#RlGUEU3htnvvW8hNd z_~5GWB+moDjs8dO2TwRg$Ul7fJSAP+{Ypjq+JxG-M!O4i&nCZ71l^JTR0}w&Di%goPiE~FB%!oH7S%Oy0kAd%t&n5boU11+05(nHnD03t!To1K}IZKXPKCb9Zz~1Wz zJT!m4;0sz8$^O*P2aQO$ZSbfD5-HKRdlN7L(e1KtTfTU)lTClul-fT;_P1}{M%21B z1Q>qid2ssQw<8`YPp+-v?NxhpXU@0ACEMrA`ztk9ss7F(BjUT9k>}VxXN-Mdp0Et4 z_l0Rd?pBY;E}BtY#821A)>=Dir7v1Mw}^L}Obc3X`CLq`xu*Etv#D8q4c+*Am;Vq_ zUouBFk38JPvi5So&lFuKuIYF|7L$uroa{RAY_Fh|EkmO!-u=d~jSV)x6 z; z4Fl!J@z!X?9M#8lx#`5Wm>Z z1COxs{!xB4UQ{!sj;>i>1M?3sK?*3z9ztL)fFsSbk1J_D&|P7wOHL8h6k&Dbk-cM# za0WAqX3yS`c)WDL;&@xSh)xFg+(n0`dQ3Qj`J$l zak0B_NRf(ub7|OOr9YasW?8GiOkAl|oGmci-I zn&&BbBD-ps(Db=%sbqUSXA+y}c(n^CDmpFLqbgiZc!wWMad@kFe~P)#j;ebS;uP2F zjrs0};<8`JD7cnUt!M&t0%SE3K1P-7Bvq?YxUap{bZ3*g+~jXj1~GO{TXdN9R$9xF zK2#N>P_s9AD_92pk^-#w6Hl`9xi0z1jedsb!Wr!1kbZgkVr7aQG|iXq1hEV_rqAyL z?Ng{w1-H&^@UoqacDKnNVqE{Ysa_{VyR}Txuq9RE(?vc4ck8x5+ftJADPe{lgMK!! zkjnHWsY*#!5n>q_f&2VxApemi4E}-6xkK7Y`z8g^f+x1t8`GnziuD6nKC<7c*@N(nZF?YN`tSL zaV=Mm6TuzqCvXAJ?qF-#SulI+tA9Fhv@fUB940^H=uSNf#-&ZJaXYaAuLTkZ`JbQv h^*3;$o0YnIFx~*q=+tue5(NCy(}rqQT)7kczW{vbJvIOU diff --git a/assets/images/screenshots/killed.PNG b/assets/images/screenshots/killed.PNG deleted file mode 100644 index 3f764f31bfbd16edc2866bcc04998c2efaedb765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31479 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 diff --git a/assets/images/vanilla_js.png b/assets/images/vanilla_js.png deleted file mode 100644 index 0b030a326fc4c4979caa8eb1c021ed3671345050..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^0YJ>p!VDxEpNH-NQYryHA+G<^7&`kZ)6&xZ!@#Ol ztANbUHzoiXKvh8YCeda0fUH7K7sn8diCZTfGPzkcujXNGkgX8B&sC*A`sXYh3Ob6Mw< G&;$U5nO>y; diff --git a/javascript/cards.js b/client/config/cards.js similarity index 53% rename from javascript/cards.js rename to client/config/cards.js index 0f64861..9229e93 100644 --- a/javascript/cards.js +++ b/client/config/cards.js @@ -1,57 +1,32 @@ -export let cards = [ +export const cards = [ { role: "Villager", team: "good", description: "During the day, find the wolves and kill them.", - isTypeOfWerewolf: false }, { role: "Werewolf", team: "evil", description: "During the night, choose a villager to kill. Don't get killed.", - isTypeOfWerewolf: true }, { role: "Dream Wolf", team: "evil", description: "If a Werewolf dies, you become a Werewolf. You do not wake up with the Werewolves until this happens. You count for parity only after converting to a wolf.", - isTypeOfWerewolf: false }, { role: "Minion", team: "evil", description: "You are an evil villager - you know who the wolves are, and you want them to win.", - isTypeOfWerewolf: false }, { role: "Seer", team: "good", description: "During each night, choose one person. The moderator will tell you whether that player is a wolf.", - isTypeOfWerewolf: false - }, - { - role: "Shadow", - team: "evil", - description: "If the Seer checks you, the Seer dies that night instead of whoever the wolves chose to kill. Reveal" + - " yourself to the moderator.", - isTypeOfWerewolf: false }, { role: "Hunter", team: "good", description: "If you are alive with a wolf at the end of the game, you best the wolf, and the village wins.", - isTypeOfWerewolf: false - }, - { - role: "Sorcerer", - team: "good", - description: "Once a game, change who the wolves are going to kill to someone else, including yourself.", - isTypeOfWerewolf: false - }, - { - role: "Mason", - team: "good", - description: "Masons know who other Masons are. Wake them up to see each other on the first night.", - isTypeOfWerewolf: false } ]; diff --git a/client/favicon_package/android-chrome-192x192.png b/client/favicon_package/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..9fd76452c905b393d6f60d26972bef4be9e7412b GIT binary patch literal 16342 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 literal 0 HcmV?d00001 diff --git a/client/favicon_package/android-chrome-256x256.png b/client/favicon_package/android-chrome-256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5e54ee5cf5423dcaa3cac3057d0a31d14b5b05 GIT binary patch literal 20372 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 literal 0 HcmV?d00001 diff --git a/client/favicon_package/apple-touch-icon.png b/client/favicon_package/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7e809316974dbd5dae788fb87477becd91dd8e GIT binary patch literal 15829 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 + + + + + #da532c + + + diff --git a/client/favicon_package/favicon-16x16.png b/client/favicon_package/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c973b9d5c7e3f93d6b1698cb3bf13c4cf578303f GIT binary patch literal 1158 zcmaJHZ{Jp=%Q@Kx>t0v#PHDRm;rS2k8QdkY63o@1M%R0{-Gz|`M&Rb=X?CV z@0=%F;tl+g>Jk6|KN?{ZPzu&Imjk&8FW(47L3bb)0N}mDyyf;nct+BZL=1r41^}{; z1Mnv_W$yqmKmah`1_1K{09Ad*&u;JlkaxYMIT?ZuqDn$eD5|L`l z=7)@W60^AYJh8>(#LUiGH(Te4ILJChwO*|>RSK%h`4Xu_Ba>(qGCjAbOm1`tE98Zo zQn^gGu7D4HvY5BX8#MBHBB9^tD8mosS{FJkvrs~~v z7(=YM#%j2Fa-TQubh4u%NH9T%bNs)kdu_1N?9G4BFGRdu(#3q{bSTWk=@Uy#Odo@F^0qZKurIze z`YI0|iZanysOUD2E3pClvo}71rYli4kd$6cw;^2V(Ob8UibW$w?oXzsr&F_1smuBP znSso&zwk?+ITUN2l}T$SznsGD_S2*IJN<9>&d8+h-XHsy7I`I4Kh?CL%;jX#LB)y2 ziE+#_aq{GeEra{sSp4wpwdt>}(N4#x^c z*u!5DE-xR*<}c*!6B^^kJe~7r?gl=-V=iVLmOWcsD_@8#)Lzce@cNH(~c4B^MQ-7H+?45n(1iIbTaAwPAGt#u#-4R z;)FYCw=+(fAqb2hATJjC9{pE9@Ai(Ku3i5fFc^8_5)627cd|8;Xy1wS^>6R$dAJkF z?CR@8dipYH00xiWTrB18c)(;DUq3M!y`2CYlvk7H2~lCwu!ti>*sSxD4i0)ciA>B) ke>yffrWh-l$VOgntO4^?6`94LYXp`6(NLT{6>Qu8H$UM(_W%F@ literal 0 HcmV?d00001 diff --git a/client/favicon_package/favicon-32x32.png b/client/favicon_package/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..a2b679ae9c2675f7a93e781f1af95731771db321 GIT binary patch literal 2093 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 literal 0 HcmV?d00001 diff --git a/client/favicon_package/mstile-150x150.png b/client/favicon_package/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..bd2c94c348b2b16fd9f397f6da9f228bcffa5818 GIT binary patch literal 12089 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 + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/client/favicon_package/site.webmanifest b/client/favicon_package/site.webmanifest new file mode 100644 index 0000000..de65106 --- /dev/null +++ b/client/favicon_package/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-256x256.png", + "sizes": "256x256", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/assets/images/roles/Werewolf.png b/client/images/Werewolf.png similarity index 100% rename from assets/images/roles/Werewolf.png rename to client/images/Werewolf.png diff --git a/assets/images/favicon.ico b/client/images/Werewolf_Small.png similarity index 100% rename from assets/images/favicon.ico rename to client/images/Werewolf_Small.png diff --git a/client/scripts/create.js b/client/scripts/create.js new file mode 100644 index 0000000..d00368b --- /dev/null +++ b/client/scripts/create.js @@ -0,0 +1,3 @@ +export const create = () => { + +} diff --git a/javascript/modules/heroku-client.js b/client/scripts/game.js similarity index 100% rename from javascript/modules/heroku-client.js rename to client/scripts/game.js diff --git a/client/scripts/home.js b/client/scripts/home.js new file mode 100644 index 0000000..26d0413 --- /dev/null +++ b/client/scripts/home.js @@ -0,0 +1,3 @@ +export const home = () => { + +}; diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css new file mode 100644 index 0000000..c1ffd0f --- /dev/null +++ b/client/styles/GLOBAL.css @@ -0,0 +1,17 @@ +canvas, caption, center, cite, code, +dd, del, dfn, div, dl, dt, em, embed, +fieldset, font, form, h1, h2, h3, h4, +h5, h6, hr, i, iframe, img, ins, kbd, +label, legend, li, menu, object, ol, p, +pre, q, s, samp, small, span, strike, +strong, sub, sup, table, tbody, td, tfoot, +th, thead, tr, tt, u, ul, var { + margin: 0; + padding: 0; + border: 0; + background: transparent; +} + +html { + font-family: sans-serif; +} diff --git a/client/styles/create.css b/client/styles/create.css new file mode 100644 index 0000000..e69de29 diff --git a/client/styles/home.css b/client/styles/home.css new file mode 100644 index 0000000..e69de29 diff --git a/client/views/404.html b/client/views/404.html new file mode 100644 index 0000000..07ab078 --- /dev/null +++ b/client/views/404.html @@ -0,0 +1,24 @@ + + + + + + + Werewolf Utility + + + + + + + + + + + + + + +

    404

    + + diff --git a/client/views/create.html b/client/views/create.html new file mode 100644 index 0000000..5198cf1 --- /dev/null +++ b/client/views/create.html @@ -0,0 +1,38 @@ + + + + + + Create A Game + + + + + + + + + + + + + +

    Create A Game

    +

    + Creating a game gives you the moderator role. You will not be dealt a card. You will know everyone's role and can + remove any player from the game. You can also play/pause the optional timer and, if desired, delegate your moderator + role to any other player. +

    +
    + +
    + + + + + diff --git a/client/views/home.html b/client/views/home.html new file mode 100644 index 0000000..bb16341 --- /dev/null +++ b/client/views/home.html @@ -0,0 +1,33 @@ + + + + + + + Werewolf Utility + + + + + + + + + + + + + + + +
    Create A Game + + + + + diff --git a/assets/images/roles-small/Werewolf.png b/favicon.ico similarity index 100% rename from assets/images/roles-small/Werewolf.png rename to favicon.ico diff --git a/javascript/game.js b/javascript/game.js deleted file mode 100644 index e1b4dae..0000000 --- a/javascript/game.js +++ /dev/null @@ -1,456 +0,0 @@ -import {utility} from './util.js' - -const socket = io(); - -const standardRoles = ["Villager", "Werewolf", "Seer", "Shadow", "Hunter", "Mason", "Minion", "Sorcerer", "Dream Wolf"]; -let clock; -let currentGame = null; -let lastGameState = null; -let cardFlippedOver = false; -let cardRendered = false; -let lastKilled = null; - -// respond to the game state received from the server -socket.on('state', function(game) { - currentGame = game; - if(detectChanges(game)) { - buildGameBasedOnState(game); - } -}); - -function buildGameBasedOnState(game) { - switch(game.status) { - case "lobby": - renderLobby(); - break; - case "started": - renderGame(); - break; - case "ended": - renderEndSplash(); - break; - default: - break; - } -} - -function detectChanges(game) { - if (lastGameState === null || - lastGameState.status !== game.status || - lastGameState.paused !== game.paused || - lastGameState.lastKilled !== game.lastKilled || - lastGameState.startTime !== game.startTime || - lastGameState.players.length !== game.players.length) { - lastGameState = game; - return true; - } - return false; -} - -function hideAfterExit(e) { - e.target.style.display = 'none'; - e.target.classList.remove(e.target.exitClass); -} - -function triggerExitAnimation(e) { - e.target.classList.remove(e.target.entranceClass); - e.target.classList.remove(e.target.exitClass); - e.target.offsetWidth; - e.target.classList.add(e.target.exitClass); - window.setTimeout(()=>{ - e.target.addEventListener('animationend', hideAfterExit, true); - },0); -} - -function triggerEntranceAnimation(selector, entranceClass, exitClass, image) { - let transitionEl = document.querySelector(selector); - transitionEl.style.display = 'flex'; - transitionEl.addEventListener('animationend', triggerExitAnimation, true); - transitionEl.classList.remove(entranceClass); - transitionEl.entranceClass = entranceClass; - transitionEl.exitClass = exitClass; - transitionEl.offsetWidth; - if (currentGame.reveals) { - if (image && standardRoles.includes(currentGame.killedRole)) { - transitionEl.classList.remove("killed-role-custom"); - transitionEl.setAttribute("src", "../assets/images/roles/" + currentGame.killedRole.replace(/\s/g, '') + ".png"); - } else { - if (image) { - transitionEl.setAttribute("src", "../assets/images/custom.svg"); - transitionEl.setAttribute("class", "killed-role-custom"); - } - } - } else { - transitionEl.setAttribute("src", "../assets/images/question_mark.svg"); - transitionEl.setAttribute("class", "killed-role-hidden"); - } - transitionEl.classList.add(entranceClass); -} - -function playKilledAnimation() { - triggerEntranceAnimation('#overlay', 'animate-overlay-in', 'animate-overlay-out', false); - triggerEntranceAnimation('#killed-role', 'animate-role-in', 'animate-role-out', true); - triggerEntranceAnimation('#killed-name', 'animate-name-in', 'animate-name-out', false); -} - -function launchGame() { - randomlyDealCardsToPlayers(); - utility.shuffle(currentGame.players); // put the players in a random order - socket.emit('startGame', { players: currentGame.players , code: currentGame.accessCode}); -} - -function randomlyDealCardsToPlayers() { - for (let player of currentGame.players) { - player.card = drawRandomCard(); - } -} - -function drawRandomCard() { - return currentGame.deck.splice(utility.getRandomInt(currentGame.deck.length) - 1, 1)[0]; -} - -function getLiveCount() { - let liveCount = 0; - for (let player of currentGame.players) { - if (!player.dead) { - liveCount ++; - } - } - return liveCount; -} - -function renderEndSplash() { - clearInterval(clock); - document.getElementById("game-container").remove(); - document.querySelector("#message-box").style.display = 'none'; - currentGame.winningTeam === "village" - ? document.getElementById("end-container").innerHTML ="

    Village

    wins!
    " - : document.getElementById("end-container").innerHTML ="

    Wolves

    win!
    "; - const rosterContainer = document.createElement("div"); - rosterContainer.setAttribute("id", "roster"); - document.getElementById("end-container").innerHTML += "
    Here's what everyone was:
    "; - let rosterContent = ""; - for (const player of currentGame.players) { - rosterContent += "
    "; - rosterContent += standardRoles.includes(player.card.role) - ? "" - : ""; - rosterContent += player.name + ": " + player.card.role + "
    " - } - rosterContainer.innerHTML = rosterContent; - document.getElementById("end-container").appendChild(rosterContainer); - document.getElementById("end-container").innerHTML += ""; - -} - -function renderGame() { - // remove lobby components if present - if (document.getElementById("lobby-container") !== null && document.getElementById("launch") !== null) { - document.getElementById("lobby-container").remove(); - document.getElementById("launch").remove(); - } - - document.querySelector("#message-box").style.display = 'block'; - if (currentGame.killedRole && currentGame.lastKilled !== lastKilled) { // a new player has been killed - lastKilled = currentGame.lastKilled; - document.getElementById("killed-name").innerText = currentGame.reveals - ? currentGame.killedPlayer + " was a " + currentGame.killedRole + "!" - : currentGame.killedPlayer + " has died!"; - playKilledAnimation(); - document.getElementById("message-box").innerText = currentGame.message; - } - const player = currentGame.players.find((player) => player.id === sessionStorage.getItem("id")); - - // render the header - document.getElementById("game-container").setAttribute("class", "game-container"); - const gameHeader = document.createElement("div"); - gameHeader.setAttribute("id", "game-header"); - gameHeader.innerHTML = - "
    " + getLiveCount() + "/" + currentGame.size + " alive
    " + - "
    " + - "
    "; - if (document.getElementById("game-header")) { - document.getElementById("card-container").removeChild(document.getElementById("game-header")); - } - document.getElementById("card-container").prepend(gameHeader); - - // render the card if it hasn't been yet - if (!cardRendered) { - renderPlayerCard(player); - cardRendered = true; - } - - // build the clock - if (currentGame.time) { - updateClock(); - document.getElementById("pause-container").innerHTML = currentGame.paused ? - "pause" - : "pause"; - document.getElementById("play-pause").addEventListener("click", pauseOrResumeGame) - } - - // add the "I'm dead" button - let killedBtn = document.createElement("button"); - killedBtn.setAttribute("id", "dead-btn"); - - if (player.dead) { - killedBtn.setAttribute("class", "app-btn killed-btn disabled"); - killedBtn.innerText = "Killed" - } else { - killedBtn.setAttribute("class", "app-btn killed-btn"); - killedBtn.innerText = "I'm dead"; - } - if (document.getElementById("dead-btn")) { - document.getElementById("card-container").removeChild(document.getElementById("dead-btn")); - } - document.getElementById("card-container").appendChild(killedBtn); - document.getElementById("dead-btn").addEventListener("click", killPlayer); - - // add the list of dead/alive players - renderDeadAndAliveInformation(); -} - -function renderDeadAndAliveInformation() { - // TODO: Refactor this function. - currentGame.players = currentGame.players.sort((a, b) => - { - return a.card.role > b.card.role ? 1 : -1; - }); - let infoContainer = document.getElementById("info-container"); - let alivePlayers = currentGame.players.filter((player) => !player.dead); - let deadPlayers = currentGame.players.filter((player) => player.dead); - deadPlayers.sort((a, b) => { // sort players by the time they died - return new Date(a.deadAt) > new Date(b.deadAt) ? -1 : 1; - }); - - let killedContainer = document.createElement("div"); - killedContainer.setAttribute("id", "killed-container"); - let killedHeader = document.createElement("h2"); - killedHeader.innerText = "Killed Players"; - killedContainer.appendChild(killedHeader); - - addDeadPlayers(deadPlayers, killedContainer); - - let aliveContainer = document.createElement("div"); - aliveContainer.setAttribute("id", "alive-container"); - let aliveHeader = document.createElement("h2"); - aliveContainer.appendChild(aliveHeader); - aliveHeader.innerText = currentGame.reveals - ? "Roles Still Alive" - : "Roles in the Game"; - var rollCounter = {}; // RTM - - if (currentGame.reveals) { - addAlivePlayers(alivePlayers, aliveContainer, rollCounter); - } else { - addAlivePlayers(currentGame.players, aliveContainer, rollCounter); - } - - if (infoContainer === null) { - infoContainer = document.createElement("div"); - infoContainer.setAttribute("id", "info-container"); - infoContainer.appendChild(killedContainer); - infoContainer.appendChild(aliveContainer); - document.getElementById("game-container").appendChild(infoContainer); - // Has to be done AFTER the infoContainer is rendered in the DOM to insert the updated counts - for (let x of document.getElementsByClassName("alive-player")) { - x.getElementsByClassName("rolecount")[0].innerText = rollCounter[x.getElementsByTagName("p")[0].innerText]; - } - } else { - document.getElementById("killed-container").remove(); - document.getElementById("alive-container").remove(); - document.getElementById("info-container").append(killedContainer); - document.getElementById("info-container").append(aliveContainer); - // Has to be done AFTER the infoContainer is rendered in the DOM to insert the updated counts - for (let x of document.getElementsByClassName("alive-player")) { - x.getElementsByClassName("rolecount")[0].innerText = rollCounter[x.getElementsByTagName("p")[0].innerText]; - } - } -} - -function addDeadPlayers(deadPlayers, killedContainer) { - deadPlayers.forEach((player) => { - let deadPlayerClass = player.card.team === "good" ? "dead-player-village" : "dead-player-evil"; - if (player.card.isTypeOfWerewolf) { - deadPlayerClass += " dead-player-wolf"; - } - const killedPlayer = document.createElement("div"); - if (currentGame.reveals) { - killedPlayer.setAttribute("class", "killed-player " + deadPlayerClass); - } else { - killedPlayer.setAttribute("class", "killed-player dead-player-no-reveals"); - } - killedPlayer.innerText = currentGame.reveals - ? player.name + ": " + player.card.role - : player.name; - killedContainer.appendChild(killedPlayer); - }); -} - -function addAlivePlayers(alivePlayers, aliveContainer, rollCounter) { - alivePlayers.forEach((player) => { - let alivePlayerClass = player.card.team === "good" ? "alive-player-village" : "alive-player-evil"; - if (player.card.isTypeOfWerewolf) { - alivePlayerClass += " alive-player-wolf"; - } - //RTM - if (rollCounter.hasOwnProperty(player.card.role)) { - rollCounter[player.card.role] += 1; - } else { - rollCounter[player.card.role] = 1; - //RTM - const alivePlayer = document.createElement("div"); - alivePlayer.setAttribute("class", "alive-player " + alivePlayerClass); - alivePlayer.innerHTML = "

    " + player.card.role + "

    "; - let roleCount = document.createElement("span"); // RTM - roleCount.setAttribute("class", "rolecount"); - //Add hidden description span - RTM 4/18/2020 - let playerCardInfo=document.createElement("span"); - playerCardInfo.setAttribute("class","tooltiptext"); - playerCardInfo.innerText=player.card.description; - alivePlayer.prepend(roleCount); - alivePlayer.appendChild(playerCardInfo); - aliveContainer.appendChild(alivePlayer); - } - }); -} - -function renderPlayerCard(player) { - const card = player.card; - const cardArt = standardRoles.includes(card.role) ? - "" + card.role + "" - : "
    Custom Role
    "; - const cardClass = player.card.team === "good" ? "game-card-inner village" : "game-card-inner wolf"; - const playerCard = document.createElement("div"); - playerCard.setAttribute("id", "game-card"); - playerCard.setAttribute("class", getFlipState()); - playerCard.innerHTML = - "
    " + - "
    " + - "

    " + card.role + "

    " + - cardArt + - "
    " + - "

    " + card.description + "

    " + - "

    Click to flip

    " + - "
    " + - "
    " + - "
    " + - "
    "; - document.getElementById("card-container").appendChild(playerCard); - document.getElementById("game-card").addEventListener("click", flipCard); -} - -function pauseOrResumeGame() { - if (currentGame.paused) { - socket.emit('resumeGame', currentGame.accessCode); - } else { - socket.emit('pauseGame', currentGame.accessCode); - } -} - -function getFlipState() { - return cardFlippedOver ? "flip-down" : "flip-up"; -} - -function flipCard() { - cardFlippedOver - ? flipUp() - : flipDown(); - - cardFlippedOver = !cardFlippedOver; -} - -function flipUp(){ - const card = document.getElementById("game-card"); - card.classList.add("flip-up"); - card.classList.remove("flip-down"); -} - -function flipDown(){ - const card = document.getElementById("game-card"); - card.classList.add("flip-down"); - card.classList.remove("flip-up"); -} - -function displayTime() { - const start = currentGame.paused ? new Date(currentGame.pauseTime) : new Date(); - const end = new Date(currentGame.endTime); - const delta = end - start; - let seconds = Math.floor((delta / 1000) % 60); - let minutes = Math.floor((delta / 1000 / 60) % 60); - let hours = Math.floor((delta / (1000 * 60 * 60)) % 24); - - seconds = seconds < 10 ? "0" + seconds : seconds; - minutes = minutes < 10 ? "0" + minutes : minutes; - - document.getElementById("clock").innerText = hours > 0 - ? hours + ":" + minutes + ":" + seconds - : minutes + ":" + seconds; -} - -function updateClock() { - clearInterval(clock); - if (document.getElementById("clock") !== null) { - displayTime(); - clock = setInterval(function() { - displayTime(); - }, 1000); - } -} - -function killPlayer() { - if(confirm("Are you sure you are dead?")) { - socket.emit("killPlayer", currentGame.players.find((player) => player.id === sessionStorage.getItem("id")).id, currentGame.accessCode); - } -} - -function renderLobby() { - document.querySelector("#message-box").style.display = 'none'; - // Render lobby header - if (document.getElementsByClassName("lobby-player").length === 0) { - let header = document.createElement("h2"); - header.setAttribute("class", "app-header-secondary"); - header.innerText = "Lobby"; - document.getElementById("lobby-container").appendChild(header); - let subHeader = document.createElement("div"); - subHeader.setAttribute("id", "lobby-subheader"); - subHeader.innerHTML = "
    " + - "" + currentGame.players.length + "" + - "/" + currentGame.size + " Players" + - "
    " + - "
    " + - "
    Access Code: " + currentGame.accessCode + "
    "; - document.getElementById("lobby-container").appendChild(subHeader); - } - // Render all players that are new - let i = 1; - for (let player of currentGame.players) { - if(!document.getElementById("player-" + i)) { - const playerContainer = document.createElement("div"); - player.id === sessionStorage.getItem("id") ? - playerContainer.setAttribute("class", "lobby-player highlighted") - : playerContainer.setAttribute("class", "lobby-player"); - playerContainer.setAttribute("id", "player-" + i); - playerContainer.innerHTML = "

    " + player.name + "

    "; - document.getElementById("lobby-container").appendChild(playerContainer); - document.getElementById("join-count").innerText = currentGame.players.length.toString(); - } - i ++; - } - // display the launch button if the player is the host - if (sessionStorage.getItem("host")) { - if (currentGame.players.length === currentGame.size) { - document.getElementById("launch").innerHTML = ""; - document.getElementById("launch").addEventListener("click", launchGame); - } else { - document.getElementById("launch").innerHTML = ""; - } - } else { - document.getElementById("launch").innerHTML = "

    The host will start the game.

    " - } -} - -// request game state from server periodically -setInterval(function () { - socket.emit('requestState', {code: sessionStorage.getItem("code")}); -}, 200); diff --git a/javascript/index.js b/javascript/index.js deleted file mode 100644 index 419cfa1..0000000 --- a/javascript/index.js +++ /dev/null @@ -1,27 +0,0 @@ -let gameModeSelect = false; - -window.onload = function() { - document.getElementById("create-game").addEventListener("click", toggleGameModeSelect); - document.getElementById("game-mode-back").addEventListener("click", toggleGameModeSelect) -}; - -function toggleGameModeSelect() { - gameModeSelect = !gameModeSelect; - let mainButtons = document.getElementById("main-buttons"); - let gameModes = document.getElementById("game-mode-select"); - if (gameModeSelect) { - mainButtons.classList.remove("slide-in"); - mainButtons.offsetWidth; - mainButtons.classList.add("slide-out"); - mainButtons.addEventListener("animationend", function() { mainButtons.style.display = "none" }, {capture: true, once: true}); - - gameModes.style.display = "flex"; - } else { - gameModes.style.display = "none"; - - mainButtons.style.display = "flex"; - mainButtons.classList.remove("slide-out"); - mainButtons.offsetWidth; - mainButtons.classList.add("slide-in"); - } -} diff --git a/javascript/join.js b/javascript/join.js deleted file mode 100644 index 512e2e0..0000000 --- a/javascript/join.js +++ /dev/null @@ -1,45 +0,0 @@ -const socket = io(); -import { utility } from './util.js' - -// respond to the game state received from the server -socket.on('joinError', function(message) { - document.getElementById("join-btn").classList.remove('disabled'); - document.getElementById("code").classList.add("error"); - document.getElementById("join-error").innerText = message; -}); - -// respond to the game state received from the server -socket.on('success', function() { - document.getElementById("join-btn").classList.remove('disabled'); - if (document.getElementById("code").classList.contains("error")) { - document.getElementById("code").classList.remove("error"); - document.getElementById("join-error").innerText = ""; - } - // If a player was a host of a previous game, don't make them the host of this one - if (sessionStorage.getItem("host")) { - sessionStorage.removeItem("host"); - } - window.location.replace('/' + document.getElementById("code").value.toString().trim().toLowerCase()); -}); - -document.getElementById("join-btn").addEventListener("click", function() { - document.getElementById("join-btn").classList.add('disabled'); - if (document.getElementById("name").value.length > 0) { - const code = document.getElementById("code").value.toString().trim().toLowerCase(); - if (document.getElementById("name").classList.contains("error")) { - document.getElementById("name").classList.remove("error"); - document.getElementById("name-error").innerText = ""; - } - sessionStorage.setItem("code", code); - let playerId = utility.generateID(); - sessionStorage.setItem("id", playerId); - const playerInfo = {name: document.getElementById("name").value, id: playerId, code: code}; - socket.emit('joinGame', playerInfo); - } else { - document.getElementById("join-btn").classList.remove('disabled'); - document.getElementById("name").classList.add("error"); - document.getElementById("name-error").innerText = "Name is required."; - } - -}); - diff --git a/javascript/modules/card-manager.js b/javascript/modules/card-manager.js deleted file mode 100644 index 1b852a7..0000000 --- a/javascript/modules/card-manager.js +++ /dev/null @@ -1,133 +0,0 @@ -const finishedArtArray = ["Villager", "Werewolf", "Seer", "Shadow", "Hunter", "Mason", "Minion", "Sorcerer", "Dream Wolf"]; - -export class CardManager { - constructor() {} - - static createCard(card) { - return new Card(card.role, card.team, card.description, card.quantity, card.isTypeOfWerewolf, card.custom, card.saved); - } - - // builds element for the informational role modal on the setup page - static constructModalRoleElement(card) { - const modalRole = document.createElement("div"); - modalRole.setAttribute("class", "modal-role"); - const roleClass = card.team === "good" ? "role-village" : "role-wolf"; - let roleImage; - if (card.custom === true) { - roleImage = "No art"; - } else { - roleImage = finishedArtArray.includes(card.role) ? - "No art" - : "Art soon."; - } - modalRole.innerHTML = - "
    " + - roleImage + - "
    " + - "

    " + card.role + "

    " + - "

    " + card.team + "

    " + - "
    " + - "
    " + - "

    " + card.description + "

    "; - return modalRole; - } - - static constructDeckBuilderElement(card, index) { - const cardContainer = document.createElement("div"); - - const quantityClass = card.team === "good" ? "card-quantity quantity-village" : "card-quantity quantity-wolf"; - - let cardClass = card.isTypeOfWerewolf ? "card card-werewolf" : "card"; - cardContainer.setAttribute("class", cardClass); - if (card.team === "good") { - cardContainer.setAttribute("id", "card-" + index); - } else { - cardContainer.setAttribute("id", "card-" + index); - } - cardContainer.innerHTML = - "
    " + - "
    " + - "
    " + - "

    " + card.role + "

    " + - "
    " + card.quantity + "
    " + - "
    " + - "

    +

    " + - "
    " + - "
    "; - cardContainer.innerHTML = card.custom - ? cardContainer.innerHTML += "" + card.role + "" - : cardContainer.innerHTML +="" + card.role + ""; - cardContainer.innerHTML += - "
    " + - "

    -

    " + - "
    "; - - return cardContainer; - } - - static constructCompactDeckBuilderElement(card, index) { - const cardContainer = document.createElement("div"); - - const quantityClass = card.team === "good" ? "card-quantity quantity-village" : "card-quantity quantity-wolf"; - - let cardClass = card.isTypeOfWerewolf ? "compact-card card-werewolf" : "compact-card"; - cardContainer.setAttribute("class", cardClass); - if (card.team === "good") { - cardContainer.setAttribute("id", "card-" + index); - } else { - cardContainer.setAttribute("id", "card-" + index); - } - cardContainer.innerHTML = - "
    " + - "

    -

    " + - "
    " + - "
    " + - "

    " + card.role + "

    " + - "
    " + card.quantity + "
    " + - "
    " + - "
    " + - "

    +

    " + - "
    "; - return cardContainer; - } - - static constructCustomCardIndicator(isCondensed, team) { - let customCard = document.createElement("div"); - if (isCondensed) { - customCard.classList.add("compact-card", "custom-card"); - } else { - customCard.classList.add("card", "custom-card"); - } - - if (team === "good") { - customCard.setAttribute("id", "custom-good"); - } else { - customCard.setAttribute("id", "custom-evil"); - } - - let cardHeader = document.createElement("h1"); - cardHeader.innerText = "Add Custom Role"; - - let cardBody = document.createElement("div"); - cardBody.innerText = "+"; - - customCard.appendChild(cardHeader); - customCard.appendChild(cardBody); - - return customCard; - } - -} - -class Card { - constructor(role, team, description, quantity, isTypeOfWerewolf, custom, saved) { - this.id = null; - this.role = role; - this.isTypeOfWerewolf = isTypeOfWerewolf; - this.team = team; - this.description = description; - this.quantity = quantity || 0; - this.custom = custom; - this.saved = saved; - } -} diff --git a/javascript/setup.js b/javascript/setup.js deleted file mode 100644 index 5dc8c5f..0000000 --- a/javascript/setup.js +++ /dev/null @@ -1,585 +0,0 @@ -import {cards} from './cards.js' -import {utility} from './util.js' -import {CardManager} from './modules/card-manager.js' - -const socket = io(); - -class Game { - constructor(accessCode, reveals, size, deck, time, hasDreamWolf) { - this.accessCode = accessCode; - this.reveals = reveals; - this.size = size; - this.deck = deck; - this.time = time; - this.players = []; - this.status = "lobby"; - this.hasDreamWolf = hasDreamWolf; - this.endTime = null; - } -} - -let gameSize = 0; -let atLeastOnePlayer = false; - -// register event listeners on buttons -document.getElementById("reset-btn").addEventListener("click", resetCardQuantities); -document.getElementById("create-btn").addEventListener("click", createGame); -document.getElementById("role-view-changer-gallery").addEventListener("click", function() { toggleViewChanger(false) }); -document.getElementById("role-view-changer-list").addEventListener("click", function() { toggleViewChanger(true) }); -document.getElementById("role-btn").addEventListener("click", function() { displayModal("role-modal", undefined) }); -document.getElementById("edit-role-btn").addEventListener("click", function() { displayModal("edit-custom-roles-modal", undefined) }); -document.getElementById("import-role-btn").addEventListener("click", function() { - document.getElementById("import-file-input").click(); -}); -document.getElementById("import-file-input").addEventListener("change", function(e) { - selectRoleImportFile(e); -}); -document.getElementById("custom-role-form").addEventListener("submit", function(e) { - addCustomCardToRoles(e); -}); -Array.from(document.getElementsByClassName("close")).forEach(function(element) { - element.addEventListener('click', closeModal); -}); - -// render all of the available cards to the user -window.onload = function() { - const urlParams = new URLSearchParams(window.location.search); - document.getElementById("create-game-header").innerText = urlParams.get('reveals') === "true" - ? "Create Reveal Game" - : "Create No-Reveal Game"; - readInUserCustomRoles(); - renderAvailableCards(false); -}; - -function renderAvailableCards(isCondensed) { - cards.sort(function(a, b) { - return a.role.toUpperCase().localeCompare(b.role); - }); - document.getElementById("card-select-good").innerHTML = ""; - document.getElementById("card-select-evil").innerHTML = ""; - document.getElementById("roles").innerHTML = ""; - document.getElementById("custom-roles").innerHTML = ""; - - for (let i = 0; i < cards.length; i ++) { - if (!cards[i].quantity) cards[i].quantity = 0; - cards[i].team === "good" - ? renderGoodRole(cards[i], i, isCondensed) - : renderEvilRole(cards[i], i, isCondensed); - } - - if (document.getElementById("custom-roles").getElementsByClassName("custom-role-edit").length === 0) { - document.getElementById("custom-roles").innerHTML = "

    You haven't added any custom cards.

    "; - } - - let customCardGood = CardManager.constructCustomCardIndicator(isCondensed, "good"); - let customCardEvil = CardManager.constructCustomCardIndicator(isCondensed, "evil"); - document.getElementById("card-select-good").appendChild(customCardGood); - document.getElementById("card-select-evil").appendChild(customCardEvil); - customCardGood.addEventListener("click", function() { - displayModal("custom-card-modal", "Good"); - }); - customCardEvil.addEventListener("click", function() { - displayModal("custom-card-modal", "Evil"); - }); -} - -function renderGoodRole(cardInfo, i, isCondensed) { - const card = CardManager.createCard(cardInfo); - if (card.custom) { - document.getElementById("custom-roles").appendChild(renderCustomRole(cardInfo)); - } - - document.getElementById("roles").appendChild(CardManager.constructModalRoleElement(card)); - if (isCondensed) { - document.getElementById("card-select-good").appendChild(CardManager.constructCompactDeckBuilderElement(card, i)); - let cardLeft = document.getElementById("card-" + i).getElementsByClassName("compact-card-left")[0]; - let cardQuantity = document.getElementById("card-" + i).getElementsByClassName("card-quantity")[0]; - let cardRight = document.getElementById("card-" + i).getElementsByClassName("compact-card-right")[0]; - cardRight.addEventListener("click", function() { incrementCardQuantity(cardRight) }, true); - cardLeft.addEventListener("click", function() { decrementCardQuantity(cardLeft) }, true); - cardRight.card = card; - cardRight.quantityEl = cardQuantity; - cardLeft.card = card; - cardLeft.quantityEl = cardQuantity; - } else { - document.getElementById("card-select-good").appendChild(CardManager.constructDeckBuilderElement(card, i)); - // Add event listeners to the top and bottom halves of the card to change the quantity. - let cardTop = document.getElementById("card-" + i).getElementsByClassName("card-top")[0]; - let cardQuantity = document.getElementById("card-" + i).getElementsByClassName("card-quantity")[0]; - let cardBottom = document.getElementById("card-" + i).getElementsByClassName("card-bottom")[0]; - cardTop.addEventListener("click", function() { incrementCardQuantity(cardTop) }, false); - cardBottom.addEventListener("click", function() { decrementCardQuantity(cardBottom) }, false); - cardTop.card = card; - cardTop.quantityEl = cardQuantity; - cardBottom.card = card; - cardBottom.quantityEl = cardQuantity; - } -} - -function renderEvilRole(cardInfo, i, isCondensed) { - const card = CardManager.createCard(cardInfo); - if (card.custom) { - document.getElementById("custom-roles").appendChild(renderCustomRole(cardInfo)); - } - - document.getElementById("roles").appendChild(CardManager.constructModalRoleElement(card)); - if (isCondensed) { - document.getElementById("card-select-evil").appendChild(CardManager.constructCompactDeckBuilderElement(card, i)); - let cardLeft = document.getElementById("card-" + i).getElementsByClassName("compact-card-left")[0]; - let cardQuantity = document.getElementById("card-" + i).getElementsByClassName("card-quantity")[0]; - let cardRight = document.getElementById("card-" + i).getElementsByClassName("compact-card-right")[0]; - cardRight.addEventListener("click", function() { incrementCardQuantity(cardRight) }, false); - cardLeft.addEventListener("click", function() { decrementCardQuantity(cardLeft) }, false); - cardRight.card = card; - cardRight.quantityEl = cardQuantity; - cardLeft.card = card; - cardLeft.quantityEl = cardQuantity; - } else { - document.getElementById("card-select-evil").appendChild(CardManager.constructDeckBuilderElement(card, i)); - // Add event listeners to the top and bottom halves of the card to change the quantity. - let cardTop = document.getElementById("card-" + i).getElementsByClassName("card-top")[0]; - let cardQuantity = document.getElementById("card-" + i).getElementsByClassName("card-quantity")[0]; - let cardBottom = document.getElementById("card-" + i).getElementsByClassName("card-bottom")[0]; - cardTop.addEventListener("click", function() { incrementCardQuantity(cardTop) }, false); - cardBottom.addEventListener("click", function() { decrementCardQuantity(cardBottom) }, false); - cardTop.card = card; - cardTop.quantityEl = cardQuantity; - cardBottom.card = card; - cardBottom.quantityEl = cardQuantity; - } -} - -function addCustomCardToRoles(e) { - e.preventDefault(); - if (!cards.find((card) => card.role === document.getElementById("custom-role-name").value)) { - let newCard = { - role: document.getElementById("custom-role-name").value, - team: document.getElementById("custom-role-team").value, - description: document.getElementById("custom-role-desc").value, - isTypeOfWerewolf: document.getElementById("custom-role-wolf").checked, - custom: true, - saved: document.getElementById("custom-role-remember").checked - }; - cards.push(newCard); - renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); - - if (newCard.saved === true) { - let existingRoles = localStorage.getItem("play-werewolf-custom-roles"); - if (existingRoles !== null) { - let rolesArray; - try { - rolesArray = JSON.parse(existingRoles); - } catch (e) { - console.error(e.message); - } - if (rolesArray) { - rolesArray.push(newCard); - } - localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(rolesArray)); - } else { - localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(new Array(newCard))); - } - } - updateCustomRoleModal(); - closeModal(); - document.getElementById("custom-role-form").reset(); - } else { - alert("A custom or standard card already exists with that name!") - } -} - -function addImportFileToRoles (e) { - //parse roles from file - let match = /^data:(.*);base64,(.*)$/.exec(e.target.result); - if (match == null) { - throw 'Could not parse result'; // should not happen - } - let mimeType = match[1]; - let content = match[2]; - let newRoles; - try { - newRoles = JSON.parse(atob(content)); - } catch(ex) { - console.error(ex.message); - } - - //add roles - let succesfullyAddedRoles = []; - let rolesThatFailedToImport = []; - let expectedKeys = ["role", "description", "team", "isTypeOfWerewolf"]; - newRoles.forEach(newRole => { - newRole.custom = true; - newRole.saved = true; - let newRoleValidationResult = validateNewRole(newRole, expectedKeys); - if (newRoleValidationResult.isValid) { - succesfullyAddedRoles.push(newRole); - } - else { - rolesThatFailedToImport.push(newRoleValidationResult); - } - }); - cards.push(...succesfullyAddedRoles); - renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); - // always save imported roles - let existingRoles = localStorage.getItem("play-werewolf-custom-roles"); - if (existingRoles !== null) { - let rolesArray; - try { - rolesArray = JSON.parse(existingRoles); - } catch (e) { - console.error(e.message); - } - if (rolesArray) { - rolesArray.push(...succesfullyAddedRoles); - } - localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(rolesArray)); - } else { - localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(succesfullyAddedRoles)); - } - updateCustomRoleModal(); - updateImportRolesModal(succesfullyAddedRoles, rolesThatFailedToImport); - displayModal("import-custom-roles-result-modal", undefined); -} - -function validateNewRole(newCard,expectedKeys) { - let newRoleValidationResult = {}; - newRoleValidationResult.role = newCard; - newRoleValidationResult.issues = []; - - //add warning if there already exists a loaded role with the same name - if (cards.find((card) => card.role === newCard.role)) { - newRoleValidationResult.issues.push({level: "warning", description: "duplicate entry"}); - } - - //For each required field, add error if the role is missing it - let missingKeys = expectedKeys.filter(function(key){ return Object.keys(newCard).indexOf(key) < 0 }); - missingKeys.forEach(missingKey => { - newRoleValidationResult.issues.push({level: "error", description: "Missing data: " + missingKey}); - }); - - newRoleValidationResult.isValid = ( newRoleValidationResult.issues.length == 0 ); - - return newRoleValidationResult; -} - -function updateCustomRoleModal() { - const customRoles = document.getElementById("custom-roles"); - customRoles.innerHTML = ""; - for (let i = 0; i < cards.length; i++){ - if (cards[i].custom) { - customRoles.appendChild(renderCustomRole(cards[i])); - } - } -} - -function updateImportRolesModal(succesfullyAddedRoles, rolesThatFailedToImport) { - let numAddedRoles = succesfullyAddedRoles.length; - if (numAddedRoles > 0) { - let successSubheader = (numAddedRoles == 1) ? "role successfully imported" : "roles successfully imported"; - document.getElementById("import-successes-subheader").innerHTML = numAddedRoles + " " + successSubheader; - const successfulRoleList = document.getElementById("import-successes-role-list"); - successfulRoleList.innerHTML = ""; - succesfullyAddedRoles.forEach(role => { - successfulRoleList.appendChild(renderCustomRole(role)); - }); - } - - let numFailedRoles = rolesThatFailedToImport.length; - if (numFailedRoles > 0) { - let failureSubheader = (numFailedRoles == 1) ? "role failed to import" : "roles failed to import"; - document.getElementById("import-failures-subheader").innerHTML = numFailedRoles + " " + failureSubheader; - document.getElementById("import-failures-issue-list").innerHTML = ""; - rolesThatFailedToImport.forEach(failureInfo => { - document.getElementById("import-failures-issue-list").appendChild(renderImportFailure(failureInfo)); - }); - } -} - -function renderImportFailure(failureInfo) { - let importFailure = document.createElement("div"); - importFailure.classList.add("import-failure"); - - let failureLabelContainer = document.createElement("div"); - failureLabelContainer.classList.add("import-failure-label"); - let triangle = document.createElement("div"); - triangle.classList.add("triangle"); - let roleName = document.createElement("p"); - roleName.innerText = failureInfo.role.role; - failureLabelContainer.appendChild(triangle); - failureLabelContainer.appendChild(roleName); - - let issueDescriptionContainer = document.createElement("div"); - issueDescriptionContainer.classList.add("import-failure-data"); - let levelSeverityOrder = ["warning", "error"]; - let levelIdx = 0; - let issueDescriptionList = document.createElement("ul"); - failureInfo.issues.forEach(issue => { - let description = document.createElement("li"); - description.innerText = issue.description; - let thisIssueLevelIdx = levelSeverityOrder.indexOf(issue.level); - if (thisIssueLevelIdx > levelIdx) { levelIdx = thisIssueLevelIdx; } - issueDescriptionList.appendChild(description); - }); - issueDescriptionContainer.appendChild(issueDescriptionList); - - importFailure.classList.add(levelSeverityOrder[levelIdx]); - importFailure.appendChild(failureLabelContainer); - importFailure.appendChild(issueDescriptionContainer); - return importFailure; -} - -function readInUserCustomRoles() { - let expectedKeys = ["role", "description", "team", "isTypeOfWerewolf", "custom", "saved"]; - let userCustomRoles = utility.validateCustomRolesJsonObject("play-werewolf-custom-roles", expectedKeys); - if (userCustomRoles) { - for (let i = 0; i < userCustomRoles.length; i++) { - cards.push(userCustomRoles[i]); - } - } -} - -function renderCustomRole(card) { - let roleElement = document.createElement("div"); - let editRemoveContainer = document.createElement("div"); - let editFormDiv = document.createElement("div"); - let roleLabel = document.createElement("div"); - let roleName = document.createElement("p"); - let remove = document.createElement("img"); - let edit = document.createElement("img"); - let editRoleTemplate = document.getElementById("edit-custom-role-template"); - let editForm = editRoleTemplate.content.cloneNode(true); - - roleName.innerText = card.role; - remove.setAttribute("src", "../assets/images/delete.svg"); - remove.setAttribute("title", "Delete"); - remove.classList.add("custom-role-button"); - remove.addEventListener("click", function() { removeCustomRole(card.role) }); - - edit.setAttribute("src", "../assets/images/pencil_green.svg"); - edit.setAttribute("title", "Edit"); - edit.classList.add("custom-role-button"); - edit.addEventListener("click", function(e) { toggleEditForm(e, editFormDiv, card) }); - roleElement.setAttribute("class", "custom-role-edit"); - - editRemoveContainer.appendChild(remove); - editRemoveContainer.appendChild(edit); - roleLabel.appendChild(roleName); - roleLabel.appendChild(editRemoveContainer); - roleElement.appendChild(roleLabel); - const shadowRoot = editFormDiv.attachShadow({mode: 'open'}); - shadowRoot.appendChild(editForm); - shadowRoot.getElementById("edit-role-form").addEventListener("submit", function(e) { - updateCustomRole(e, editFormDiv, card); - }); - - editFormDiv.style.display = "none"; - roleElement.appendChild(editFormDiv); - - return roleElement; -} - -function toggleEditForm(event, formDiv, card) { - event.preventDefault(); - let displayRule = formDiv.style.display; - formDiv.style.display = displayRule === "none" ? "block" : "none"; - - if (formDiv.style.display === "block") { - populateEditRoleForm(formDiv, card); - } -} - -function toggleViewChanger(isCondensed) { - - if (isCondensed) { - document.getElementById("role-view-changer-gallery").classList.remove("selected"); - document.getElementById("role-view-changer-list").classList.add("selected"); - } else { - document.getElementById("role-view-changer-gallery").classList.add("selected"); - document.getElementById("role-view-changer-list").classList.remove("selected"); - } - renderAvailableCards(isCondensed); -} - -function populateEditRoleForm(formDiv, card) { - formDiv.shadowRoot.querySelector("#edit-role-desc").value = card.description; - formDiv.shadowRoot.querySelector("#edit-role-team").value = card.team; - formDiv.shadowRoot.querySelector("#edit-role-wolf").checked = card.isTypeOfWerewolf; - formDiv.shadowRoot.querySelector("#edit-role-remember").checked = card.saved; -} - -function removeCustomRole(name) { - if (confirm("Delete this role?")) { - let matchingCards = cards.filter((card) => card.role === name); - matchingCards.forEach((card) => { - cards.splice(cards.indexOf(card), 1); - }); - let expectedKeys = ["role", "description", "team", "isTypeOfWerewolf", "custom", "saved"]; - let userCustomRoles = utility.validateCustomRolesJsonObject("play-werewolf-custom-roles", expectedKeys); - if (userCustomRoles) { - userCustomRoles = userCustomRoles.filter((card) => card.role !== name); - localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(userCustomRoles)); - } - updateCustomRoleModal(); - renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); - } -} - -function updateCustomRole(event, formDiv, cardToUpdate) { - event.preventDefault(); - cardToUpdate.team = formDiv.shadowRoot.querySelector("#edit-role-team").value; - cardToUpdate.description = formDiv.shadowRoot.querySelector("#edit-role-desc").value; - cardToUpdate.isTypeOfWerewolf = formDiv.shadowRoot.querySelector("#edit-role-wolf").checked; - cardToUpdate.saved = formDiv.shadowRoot.querySelector("#edit-role-remember").checked; - - removeOrAddSavedRoleIfNeeded(cardToUpdate); - toggleEditForm(event, formDiv, cardToUpdate); - renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected")); -} - -function removeOrAddSavedRoleIfNeeded(card) { - let expectedKeys = ["role", "description", "team", "isTypeOfWerewolf", "custom", "saved"]; - let userCustomRoles = utility.validateCustomRolesJsonObject("play-werewolf-custom-roles", expectedKeys); - if (userCustomRoles) { - if (card.saved) { - let roleToUpdate = userCustomRoles.find((savedCard) => savedCard.role === card.role); - if (roleToUpdate) { - userCustomRoles[userCustomRoles.indexOf(roleToUpdate)] = card; - } else { - userCustomRoles.push(card); - } - localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(userCustomRoles)); - } else { - let roleToRemove = userCustomRoles.find((savedCard) => savedCard.role === card.role); - if (roleToRemove) { - userCustomRoles.splice(userCustomRoles.indexOf(roleToRemove), 1); - localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(userCustomRoles)); - } - } - } -} - - -function incrementCardQuantity(e) { - if(e.card.quantity < 25) { - e.card.quantity += 1; - cards.find((card) => card.role === e.card.role).quantity += 1; - } - e.quantityEl.innerHTML = e.card.quantity; - updateGameSize(); -} - -function decrementCardQuantity(e) { - if(e.card.quantity > 0) { - e.card.quantity -= 1; - cards.find((card) => card.role === e.card.role).quantity -= 1; - } - e.quantityEl.innerHTML = e.card.quantity; - updateGameSize(); -} - -function updateGameSize() { - gameSize = 0; - for (let card of cards) { - gameSize += card.quantity; - } - document.getElementById("game-size").innerText = gameSize + " Players"; - atLeastOnePlayer = gameSize > 0; - return gameSize; -} - -function resetCardQuantities() { - for (let card of cards) { - card.quantity = 0; - } - updateGameSize(); - Array.prototype.filter.call(document.getElementsByClassName("card-quantity"), function(quantities){ - return quantities.innerHTML = 0; - }); -} - -function displayModal(modalId, teamForCustomRole) { - if (teamForCustomRole === "Good") { - document.getElementById("option-evil").removeAttribute("selected"); - document.getElementById("option-good").setAttribute("selected", "selected"); - } - if (teamForCustomRole === "Evil") { - document.getElementById("option-good").removeAttribute("selected"); - document.getElementById("option-evil").setAttribute("selected", "selected"); - } - document.getElementById(modalId).classList.remove("hidden"); - document.getElementById("app-content").classList.add("hidden"); -} - -function closeModal() { - document.getElementById("role-modal").classList.add("hidden"); - document.getElementById("custom-card-modal").classList.add("hidden"); - document.getElementById("edit-custom-roles-modal").classList.add("hidden"); - document.getElementById("import-custom-roles-result-modal").classList.add("hidden"); - document.getElementById("app-content").classList.remove("hidden"); -} - -function buildDeckFromQuantities() { - let playerDeck = []; - for (const card of cards) { - for (let i = 0; i < card.quantity; i++) { - card.id = utility.generateID(); - playerDeck.push(card); - } - } - return playerDeck; -} - -function createGame() { - if (document.getElementById("name").value.length > 0 && atLeastOnePlayer) { - const urlParams = new URLSearchParams(window.location.search); - const revealParam = urlParams.get('reveals'); - - // generate 6 digit access code - let code = ""; - let charPool = "abcdefghijklmnopqrstuvwxyz0123456789"; - for (let i = 0; i < 6; i++) { - code += charPool[utility.getRandomInt(36)] - } - - // generate unique player Id for session - let id = utility.generateID(); - sessionStorage.setItem("id", id); - - // player who creates the game is the host - sessionStorage.setItem("host", true); - - // send a new game to the server, and then join it - const playerInfo = {name: document.getElementById("name").value, code: code, id: id}; - let gameDeck = buildDeckFromQuantities(); - const game = new Game( - code, - revealParam === "true", - gameSize, - gameDeck, - Math.ceil(document.getElementById("time").value), - gameDeck.find((card) => card.role === "Dream Wolf") !== undefined - ); - socket.emit('newGame', game, function() { - socket.emit('joinGame', playerInfo); - sessionStorage.setItem('code', code); - window.location.replace('/' + code); - }); - } else { - document.getElementById("some-error").innerText = "There are problems with your above setup."; - if (!atLeastOnePlayer) { - document.getElementById("game-size").classList.add("error"); - } else { - document.getElementById("game-size").classList.remove("error"); - } - document.getElementById("name").classList.add("error"); - document.getElementById("name-error").innerText = "Name is required."; - } -} -function selectRoleImportFile(e) { - var files = e.target.files; - if (files.length < 1) { return; } - var file = files[0]; - var reader = new FileReader(); - reader.onload = addImportFileToRoles; - reader.readAsDataURL(file); -} diff --git a/javascript/util.js b/javascript/util.js deleted file mode 100644 index 90fddb5..0000000 --- a/javascript/util.js +++ /dev/null @@ -1,50 +0,0 @@ -export const utility = -{ - generateID() { - let code = ""; - let charPool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - for (let i = 0; i < 10; i++) { - code += charPool[this.getRandomInt(61)] - } - return code; - }, - - getRandomInt(max) { - return Math.floor(Math.random() * Math.floor(max)); - }, - - shuffle(array) { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; - }, - - validateCustomRolesJsonObject(name, expectedKeys) { - let value = localStorage.getItem(name); - if (value !== null) { - let valueJson; - try { - valueJson = JSON.parse(value); - } catch(e) { - console.error(e.message); - localStorage.removeItem(name); - return false; - } - if (valueJson && Array.isArray(valueJson)) { // some defensive programming - check if it's an array, and that the object has the expected structure - for (let i = 0; i < valueJson.length; i++){ - if (expectedKeys.some((key) => !Object.keys(valueJson[i]).includes(key))) { - console.error("tried to read invalid object: " + valueJson[i] + " with expected keys: " + expectedKeys); - valueJson.splice(i, 1); - localStorage.setItem(name, JSON.stringify(valueJson)); - } - } - return valueJson; - } else { // object has been messed with. remove it. - localStorage.removeItem(name); - return false; - } - } - } -}; diff --git a/lib/jasmine-3.5.0/boot.js b/lib/jasmine-3.5.0/boot.js deleted file mode 100644 index 2d68462..0000000 --- a/lib/jasmine-3.5.0/boot.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. - - If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. - - The location of `boot.js` can be specified and/or overridden in `jasmine.yml`. - - [jasmine-gem]: http://github.com/pivotal/jasmine-gem - */ - -(function() { - - /** - * ## Require & Instantiate - * - * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. - */ - window.jasmine = jasmineRequire.core(jasmineRequire); - - /** - * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. - */ - jasmineRequire.html(jasmine); - - /** - * Create the Jasmine environment. This is used to run all specs in a project. - */ - var env = jasmine.getEnv(); - - /** - * ## The Global Interface - * - * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. - */ - var jasmineInterface = jasmineRequire.interface(jasmine, env); - - /** - * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. - */ - extend(window, jasmineInterface); - - /** - * ## Runner Parameters - * - * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. - */ - - var queryString = new jasmine.QueryString({ - getWindowLocation: function() { return window.location; } - }); - - var filterSpecs = !!queryString.getParam("spec"); - - var config = { - failFast: queryString.getParam("failFast"), - oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"), - hideDisabled: queryString.getParam("hideDisabled") - }; - - var random = queryString.getParam("random"); - - if (random !== undefined && random !== "") { - config.random = random; - } - - var seed = queryString.getParam("seed"); - if (seed) { - config.seed = seed; - } - - /** - * ## Reporters - * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). - */ - var htmlReporter = new jasmine.HtmlReporter({ - env: env, - navigateWithNewParam: function(key, value) { return queryString.navigateWithNewParam(key, value); }, - addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, - getContainer: function() { return document.body; }, - createElement: function() { return document.createElement.apply(document, arguments); }, - createTextNode: function() { return document.createTextNode.apply(document, arguments); }, - timer: new jasmine.Timer(), - filterSpecs: filterSpecs - }); - - /** - * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. - */ - env.addReporter(jasmineInterface.jsApiReporter); - env.addReporter(htmlReporter); - - /** - * Filter which specs will be run by matching the start of the full name against the `spec` query param. - */ - var specFilter = new jasmine.HtmlSpecFilter({ - filterString: function() { return queryString.getParam("spec"); } - }); - - config.specFilter = function(spec) { - return specFilter.matches(spec.getFullName()); - }; - - env.configure(config); - - /** - * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. - */ - window.setTimeout = window.setTimeout; - window.setInterval = window.setInterval; - window.clearTimeout = window.clearTimeout; - window.clearInterval = window.clearInterval; - - /** - * ## Execution - * - * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. - */ - var currentWindowOnload = window.onload; - - window.onload = function() { - if (currentWindowOnload) { - currentWindowOnload(); - } - htmlReporter.initialize(); - env.execute(); - }; - - /** - * Helper function for readability above. - */ - function extend(destination, source) { - for (var property in source) destination[property] = source[property]; - return destination; - } - -}()); diff --git a/lib/jasmine-3.5.0/jasmine-html.js b/lib/jasmine-3.5.0/jasmine-html.js deleted file mode 100644 index aecf2f1..0000000 --- a/lib/jasmine-3.5.0/jasmine-html.js +++ /dev/null @@ -1,817 +0,0 @@ -/* -Copyright (c) 2008-2019 Pivotal Labs - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -jasmineRequire.html = function(j$) { - j$.ResultsNode = jasmineRequire.ResultsNode(); - j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); - j$.QueryString = jasmineRequire.QueryString(); - j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); -}; - -jasmineRequire.HtmlReporter = function(j$) { - function ResultsStateBuilder() { - this.topResults = new j$.ResultsNode({}, '', null); - this.currentParent = this.topResults; - this.specsExecuted = 0; - this.failureCount = 0; - this.pendingSpecCount = 0; - } - - ResultsStateBuilder.prototype.suiteStarted = function(result) { - this.currentParent.addChild(result, 'suite'); - this.currentParent = this.currentParent.last(); - }; - - ResultsStateBuilder.prototype.suiteDone = function(result) { - this.currentParent.updateResult(result); - if (this.currentParent !== this.topResults) { - this.currentParent = this.currentParent.parent; - } - - if (result.status === 'failed') { - this.failureCount++; - } - }; - - ResultsStateBuilder.prototype.specStarted = function(result) {}; - - ResultsStateBuilder.prototype.specDone = function(result) { - this.currentParent.addChild(result, 'spec'); - - if (result.status !== 'excluded') { - this.specsExecuted++; - } - - if (result.status === 'failed') { - this.failureCount++; - } - - if (result.status == 'pending') { - this.pendingSpecCount++; - } - }; - - function HtmlReporter(options) { - var config = function() { - return (options.env && options.env.configuration()) || {}; - }, - getContainer = options.getContainer, - createElement = options.createElement, - createTextNode = options.createTextNode, - navigateWithNewParam = options.navigateWithNewParam || function() {}, - addToExistingQueryString = - options.addToExistingQueryString || defaultQueryString, - filterSpecs = options.filterSpecs, - htmlReporterMain, - symbols, - deprecationWarnings = []; - - this.initialize = function() { - clearPrior(); - htmlReporterMain = createDom( - 'div', - { className: 'jasmine_html-reporter' }, - createDom( - 'div', - { className: 'jasmine-banner' }, - createDom('a', { - className: 'jasmine-title', - href: 'http://jasmine.github.io/', - target: '_blank' - }), - createDom('span', { className: 'jasmine-version' }, j$.version) - ), - createDom('ul', { className: 'jasmine-symbol-summary' }), - createDom('div', { className: 'jasmine-alert' }), - createDom( - 'div', - { className: 'jasmine-results' }, - createDom('div', { className: 'jasmine-failures' }) - ) - ); - getContainer().appendChild(htmlReporterMain); - }; - - var totalSpecsDefined; - this.jasmineStarted = function(options) { - totalSpecsDefined = options.totalSpecsDefined || 0; - }; - - var summary = createDom('div', { className: 'jasmine-summary' }); - - var stateBuilder = new ResultsStateBuilder(); - - this.suiteStarted = function(result) { - stateBuilder.suiteStarted(result); - }; - - this.suiteDone = function(result) { - stateBuilder.suiteDone(result); - - if (result.status === 'failed') { - failures.push(failureDom(result)); - } - addDeprecationWarnings(result); - }; - - this.specStarted = function(result) { - stateBuilder.specStarted(result); - }; - - var failures = []; - this.specDone = function(result) { - stateBuilder.specDone(result); - - if (noExpectations(result)) { - var noSpecMsg = "Spec '" + result.fullName + "' has no expectations."; - if (result.status === 'failed') { - console.error(noSpecMsg); - } else { - console.warn(noSpecMsg); - } - } - - if (!symbols) { - symbols = find('.jasmine-symbol-summary'); - } - - symbols.appendChild( - createDom('li', { - className: this.displaySpecInCorrectFormat(result), - id: 'spec_' + result.id, - title: result.fullName - }) - ); - - if (result.status === 'failed') { - failures.push(failureDom(result)); - } - - addDeprecationWarnings(result); - }; - - this.displaySpecInCorrectFormat = function(result) { - return noExpectations(result) && result.status === 'passed' - ? 'jasmine-empty' - : this.resultStatus(result.status); - }; - - this.resultStatus = function(status) { - if (status === 'excluded') { - return config().hideDisabled - ? 'jasmine-excluded-no-display' - : 'jasmine-excluded'; - } - return 'jasmine-' + status; - }; - - this.jasmineDone = function(doneResult) { - var banner = find('.jasmine-banner'); - var alert = find('.jasmine-alert'); - var order = doneResult && doneResult.order; - var i; - alert.appendChild( - createDom( - 'span', - { className: 'jasmine-duration' }, - 'finished in ' + doneResult.totalTime / 1000 + 's' - ) - ); - - banner.appendChild(optionsMenu(config())); - - if (stateBuilder.specsExecuted < totalSpecsDefined) { - var skippedMessage = - 'Ran ' + - stateBuilder.specsExecuted + - ' of ' + - totalSpecsDefined + - ' specs - run all'; - var skippedLink = addToExistingQueryString('spec', ''); - alert.appendChild( - createDom( - 'span', - { className: 'jasmine-bar jasmine-skipped' }, - createDom( - 'a', - { href: skippedLink, title: 'Run all specs' }, - skippedMessage - ) - ) - ); - } - var statusBarMessage = ''; - var statusBarClassName = 'jasmine-overall-result jasmine-bar '; - var globalFailures = (doneResult && doneResult.failedExpectations) || []; - var failed = stateBuilder.failureCount + globalFailures.length > 0; - - if (totalSpecsDefined > 0 || failed) { - statusBarMessage += - pluralize('spec', stateBuilder.specsExecuted) + - ', ' + - pluralize('failure', stateBuilder.failureCount); - if (stateBuilder.pendingSpecCount) { - statusBarMessage += - ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); - } - } - - if (doneResult.overallStatus === 'passed') { - statusBarClassName += ' jasmine-passed '; - } else if (doneResult.overallStatus === 'incomplete') { - statusBarClassName += ' jasmine-incomplete '; - statusBarMessage = - 'Incomplete: ' + - doneResult.incompleteReason + - ', ' + - statusBarMessage; - } else { - statusBarClassName += ' jasmine-failed '; - } - - var seedBar; - if (order && order.random) { - seedBar = createDom( - 'span', - { className: 'jasmine-seed-bar' }, - ', randomized with seed ', - createDom( - 'a', - { - title: 'randomized with seed ' + order.seed, - href: seedHref(order.seed) - }, - order.seed - ) - ); - } - - alert.appendChild( - createDom( - 'span', - { className: statusBarClassName }, - statusBarMessage, - seedBar - ) - ); - - var errorBarClassName = 'jasmine-bar jasmine-errored'; - var afterAllMessagePrefix = 'AfterAll '; - - for (i = 0; i < globalFailures.length; i++) { - alert.appendChild( - createDom( - 'span', - { className: errorBarClassName }, - globalFailureMessage(globalFailures[i]) - ) - ); - } - - function globalFailureMessage(failure) { - if (failure.globalErrorType === 'load') { - var prefix = 'Error during loading: ' + failure.message; - - if (failure.filename) { - return ( - prefix + ' in ' + failure.filename + ' line ' + failure.lineno - ); - } else { - return prefix; - } - } else { - return afterAllMessagePrefix + failure.message; - } - } - - addDeprecationWarnings(doneResult); - - var warningBarClassName = 'jasmine-bar jasmine-warning'; - for (i = 0; i < deprecationWarnings.length; i++) { - var warning = deprecationWarnings[i]; - alert.appendChild( - createDom( - 'span', - { className: warningBarClassName }, - 'DEPRECATION: ' + warning - ) - ); - } - - var results = find('.jasmine-results'); - results.appendChild(summary); - - summaryList(stateBuilder.topResults, summary); - - if (failures.length) { - alert.appendChild( - createDom( - 'span', - { className: 'jasmine-menu jasmine-bar jasmine-spec-list' }, - createDom('span', {}, 'Spec List | '), - createDom( - 'a', - { className: 'jasmine-failures-menu', href: '#' }, - 'Failures' - ) - ) - ); - alert.appendChild( - createDom( - 'span', - { className: 'jasmine-menu jasmine-bar jasmine-failure-list' }, - createDom( - 'a', - { className: 'jasmine-spec-list-menu', href: '#' }, - 'Spec List' - ), - createDom('span', {}, ' | Failures ') - ) - ); - - find('.jasmine-failures-menu').onclick = function() { - setMenuModeTo('jasmine-failure-list'); - }; - find('.jasmine-spec-list-menu').onclick = function() { - setMenuModeTo('jasmine-spec-list'); - }; - - setMenuModeTo('jasmine-failure-list'); - - var failureNode = find('.jasmine-failures'); - for (i = 0; i < failures.length; i++) { - failureNode.appendChild(failures[i]); - } - } - }; - - return this; - - function failureDom(result) { - var failure = createDom( - 'div', - { className: 'jasmine-spec-detail jasmine-failed' }, - failureDescription(result, stateBuilder.currentParent), - createDom('div', { className: 'jasmine-messages' }) - ); - var messages = failure.childNodes[1]; - - for (var i = 0; i < result.failedExpectations.length; i++) { - var expectation = result.failedExpectations[i]; - messages.appendChild( - createDom( - 'div', - { className: 'jasmine-result-message' }, - expectation.message - ) - ); - messages.appendChild( - createDom( - 'div', - { className: 'jasmine-stack-trace' }, - expectation.stack - ) - ); - } - - if (result.failedExpectations.length === 0) { - messages.appendChild( - createDom( - 'div', - { className: 'jasmine-result-message' }, - 'Spec has no expectations' - ) - ); - } - - return failure; - } - - function summaryList(resultsTree, domParent) { - var specListNode; - for (var i = 0; i < resultsTree.children.length; i++) { - var resultNode = resultsTree.children[i]; - if (filterSpecs && !hasActiveSpec(resultNode)) { - continue; - } - if (resultNode.type === 'suite') { - var suiteListNode = createDom( - 'ul', - { className: 'jasmine-suite', id: 'suite-' + resultNode.result.id }, - createDom( - 'li', - { - className: - 'jasmine-suite-detail jasmine-' + resultNode.result.status - }, - createDom( - 'a', - { href: specHref(resultNode.result) }, - resultNode.result.description - ) - ) - ); - - summaryList(resultNode, suiteListNode); - domParent.appendChild(suiteListNode); - } - if (resultNode.type === 'spec') { - if (domParent.getAttribute('class') !== 'jasmine-specs') { - specListNode = createDom('ul', { className: 'jasmine-specs' }); - domParent.appendChild(specListNode); - } - var specDescription = resultNode.result.description; - if (noExpectations(resultNode.result)) { - specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; - } - if ( - resultNode.result.status === 'pending' && - resultNode.result.pendingReason !== '' - ) { - specDescription = - specDescription + - ' PENDING WITH MESSAGE: ' + - resultNode.result.pendingReason; - } - specListNode.appendChild( - createDom( - 'li', - { - className: 'jasmine-' + resultNode.result.status, - id: 'spec-' + resultNode.result.id - }, - createDom( - 'a', - { href: specHref(resultNode.result) }, - specDescription - ) - ) - ); - } - } - } - - function optionsMenu(config) { - var optionsMenuDom = createDom( - 'div', - { className: 'jasmine-run-options' }, - createDom('span', { className: 'jasmine-trigger' }, 'Options'), - createDom( - 'div', - { className: 'jasmine-payload' }, - createDom( - 'div', - { className: 'jasmine-stop-on-failure' }, - createDom('input', { - className: 'jasmine-fail-fast', - id: 'jasmine-fail-fast', - type: 'checkbox' - }), - createDom( - 'label', - { className: 'jasmine-label', for: 'jasmine-fail-fast' }, - 'stop execution on spec failure' - ) - ), - createDom( - 'div', - { className: 'jasmine-throw-failures' }, - createDom('input', { - className: 'jasmine-throw', - id: 'jasmine-throw-failures', - type: 'checkbox' - }), - createDom( - 'label', - { className: 'jasmine-label', for: 'jasmine-throw-failures' }, - 'stop spec on expectation failure' - ) - ), - createDom( - 'div', - { className: 'jasmine-random-order' }, - createDom('input', { - className: 'jasmine-random', - id: 'jasmine-random-order', - type: 'checkbox' - }), - createDom( - 'label', - { className: 'jasmine-label', for: 'jasmine-random-order' }, - 'run tests in random order' - ) - ), - createDom( - 'div', - { className: 'jasmine-hide-disabled' }, - createDom('input', { - className: 'jasmine-disabled', - id: 'jasmine-hide-disabled', - type: 'checkbox' - }), - createDom( - 'label', - { className: 'jasmine-label', for: 'jasmine-hide-disabled' }, - 'hide disabled tests' - ) - ) - ) - ); - - var failFastCheckbox = optionsMenuDom.querySelector('#jasmine-fail-fast'); - failFastCheckbox.checked = config.failFast; - failFastCheckbox.onclick = function() { - navigateWithNewParam('failFast', !config.failFast); - }; - - var throwCheckbox = optionsMenuDom.querySelector( - '#jasmine-throw-failures' - ); - throwCheckbox.checked = config.oneFailurePerSpec; - throwCheckbox.onclick = function() { - navigateWithNewParam('throwFailures', !config.oneFailurePerSpec); - }; - - var randomCheckbox = optionsMenuDom.querySelector( - '#jasmine-random-order' - ); - randomCheckbox.checked = config.random; - randomCheckbox.onclick = function() { - navigateWithNewParam('random', !config.random); - }; - - var hideDisabled = optionsMenuDom.querySelector('#jasmine-hide-disabled'); - hideDisabled.checked = config.hideDisabled; - hideDisabled.onclick = function() { - navigateWithNewParam('hideDisabled', !config.hideDisabled); - }; - - var optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), - optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'), - isOpen = /\bjasmine-open\b/; - - optionsTrigger.onclick = function() { - if (isOpen.test(optionsPayload.className)) { - optionsPayload.className = optionsPayload.className.replace( - isOpen, - '' - ); - } else { - optionsPayload.className += ' jasmine-open'; - } - }; - - return optionsMenuDom; - } - - function failureDescription(result, suite) { - var wrapper = createDom( - 'div', - { className: 'jasmine-description' }, - createDom( - 'a', - { title: result.description, href: specHref(result) }, - result.description - ) - ); - var suiteLink; - - while (suite && suite.parent) { - wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild); - suiteLink = createDom( - 'a', - { href: suiteHref(suite) }, - suite.result.description - ); - wrapper.insertBefore(suiteLink, wrapper.firstChild); - - suite = suite.parent; - } - - return wrapper; - } - - function suiteHref(suite) { - var els = []; - - while (suite && suite.parent) { - els.unshift(suite.result.description); - suite = suite.parent; - } - - return addToExistingQueryString('spec', els.join(' ')); - } - - function addDeprecationWarnings(result) { - if (result && result.deprecationWarnings) { - for (var i = 0; i < result.deprecationWarnings.length; i++) { - var warning = result.deprecationWarnings[i].message; - if (!j$.util.arrayContains(warning)) { - deprecationWarnings.push(warning); - } - } - } - } - - function find(selector) { - return getContainer().querySelector('.jasmine_html-reporter ' + selector); - } - - function clearPrior() { - // return the reporter - var oldReporter = find(''); - - if (oldReporter) { - getContainer().removeChild(oldReporter); - } - } - - function createDom(type, attrs, childrenVarArgs) { - var el = createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(createTextNode(child)); - } else { - if (child) { - el.appendChild(child); - } - } - } - - for (var attr in attrs) { - if (attr == 'className') { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; - } - - function pluralize(singular, count) { - var word = count == 1 ? singular : singular + 's'; - - return '' + count + ' ' + word; - } - - function specHref(result) { - return addToExistingQueryString('spec', result.fullName); - } - - function seedHref(seed) { - return addToExistingQueryString('seed', seed); - } - - function defaultQueryString(key, value) { - return '?' + key + '=' + value; - } - - function setMenuModeTo(mode) { - htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); - } - - function noExpectations(result) { - var allExpectations = - result.failedExpectations.length + result.passedExpectations.length; - - return ( - allExpectations === 0 && - (result.status === 'passed' || result.status === 'failed') - ); - } - - function hasActiveSpec(resultNode) { - if (resultNode.type == 'spec' && resultNode.result.status != 'excluded') { - return true; - } - - if (resultNode.type == 'suite') { - for (var i = 0, j = resultNode.children.length; i < j; i++) { - if (hasActiveSpec(resultNode.children[i])) { - return true; - } - } - } - } - } - - return HtmlReporter; -}; - -jasmineRequire.HtmlSpecFilter = function() { - function HtmlSpecFilter(options) { - var filterString = - options && - options.filterString() && - options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); - var filterPattern = new RegExp(filterString); - - this.matches = function(specName) { - return filterPattern.test(specName); - }; - } - - return HtmlSpecFilter; -}; - -jasmineRequire.ResultsNode = function() { - function ResultsNode(result, type, parent) { - this.result = result; - this.type = type; - this.parent = parent; - - this.children = []; - - this.addChild = function(result, type) { - this.children.push(new ResultsNode(result, type, this)); - }; - - this.last = function() { - return this.children[this.children.length - 1]; - }; - - this.updateResult = function(result) { - this.result = result; - }; - } - - return ResultsNode; -}; - -jasmineRequire.QueryString = function() { - function QueryString(options) { - this.navigateWithNewParam = function(key, value) { - options.getWindowLocation().search = this.fullStringWithNewParam( - key, - value - ); - }; - - this.fullStringWithNewParam = function(key, value) { - var paramMap = queryStringToParamMap(); - paramMap[key] = value; - return toQueryString(paramMap); - }; - - this.getParam = function(key) { - return queryStringToParamMap()[key]; - }; - - return this; - - function toQueryString(paramMap) { - var qStrPairs = []; - for (var prop in paramMap) { - qStrPairs.push( - encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]) - ); - } - return '?' + qStrPairs.join('&'); - } - - function queryStringToParamMap() { - var paramStr = options.getWindowLocation().search.substring(1), - params = [], - paramMap = {}; - - if (paramStr.length > 0) { - params = paramStr.split('&'); - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - var value = decodeURIComponent(p[1]); - if (value === 'true' || value === 'false') { - value = JSON.parse(value); - } - paramMap[decodeURIComponent(p[0])] = value; - } - } - - return paramMap; - } - } - - return QueryString; -}; diff --git a/lib/jasmine-3.5.0/jasmine.css b/lib/jasmine-3.5.0/jasmine.css deleted file mode 100644 index 81dd5b3..0000000 --- a/lib/jasmine-3.5.0/jasmine.css +++ /dev/null @@ -1,128 +0,0 @@ -@charset "UTF-8"; -body { overflow-y: scroll; } - -.jasmine_html-reporter { width: 100%; background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; } - -.jasmine_html-reporter a { text-decoration: none; } - -.jasmine_html-reporter a:hover { text-decoration: underline; } - -.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; } - -.jasmine_html-reporter .jasmine-banner, .jasmine_html-reporter .jasmine-symbol-summary, .jasmine_html-reporter .jasmine-summary, .jasmine_html-reporter .jasmine-result-message, .jasmine_html-reporter .jasmine-spec .jasmine-description, .jasmine_html-reporter .jasmine-spec-detail .jasmine-description, .jasmine_html-reporter .jasmine-alert .jasmine-bar, .jasmine_html-reporter .jasmine-stack-trace { padding-left: 9px; padding-right: 9px; } - -.jasmine_html-reporter .jasmine-banner { position: relative; } - -.jasmine_html-reporter .jasmine-banner .jasmine-title { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==") no-repeat; background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=") no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } - -.jasmine_html-reporter .jasmine-banner .jasmine-version { margin-left: 14px; position: relative; top: 6px; } - -.jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } - -.jasmine_html-reporter .jasmine-version { color: #aaa; } - -.jasmine_html-reporter .jasmine-banner { margin-top: 14px; } - -.jasmine_html-reporter .jasmine-duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; } - -.jasmine_html-reporter .jasmine-symbol-summary { overflow: hidden; margin: 14px 0; } - -.jasmine_html-reporter .jasmine-symbol-summary li { display: inline-block; height: 10px; width: 14px; font-size: 16px; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { font-size: 14px; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { color: #007069; content: "•"; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { line-height: 9px; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { color: #ca3a11; content: "×"; font-weight: bold; margin-left: -1px; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded { font-size: 14px; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded:before { color: #bababa; content: "•"; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded-no-display { font-size: 14px; display: none; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { line-height: 17px; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { color: #ba9d37; content: "*"; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { font-size: 14px; } - -.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { color: #ba9d37; content: "•"; } - -.jasmine_html-reporter .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; } - -.jasmine_html-reporter .jasmine-run-options .jasmine-trigger { cursor: pointer; padding: 8px 16px; } - -.jasmine_html-reporter .jasmine-run-options .jasmine-payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; } - -.jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { display: block; } - -.jasmine_html-reporter .jasmine-bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } - -.jasmine_html-reporter .jasmine-bar.jasmine-failed, .jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; border-bottom: 1px solid #eee; } - -.jasmine_html-reporter .jasmine-bar.jasmine-passed { background-color: #007069; } - -.jasmine_html-reporter .jasmine-bar.jasmine-incomplete { background-color: #bababa; } - -.jasmine_html-reporter .jasmine-bar.jasmine-skipped { background-color: #bababa; } - -.jasmine_html-reporter .jasmine-bar.jasmine-warning { background-color: #ba9d37; color: #333; } - -.jasmine_html-reporter .jasmine-bar.jasmine-menu { background-color: #fff; color: #aaa; } - -.jasmine_html-reporter .jasmine-bar.jasmine-menu a { color: #333; } - -.jasmine_html-reporter .jasmine-bar a { color: white; } - -.jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list, .jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures { display: none; } - -.jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine_html-reporter.jasmine-failure-list .jasmine-summary { display: none; } - -.jasmine_html-reporter .jasmine-results { margin-top: 14px; } - -.jasmine_html-reporter .jasmine-summary { margin-top: 14px; } - -.jasmine_html-reporter .jasmine-summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } - -.jasmine_html-reporter .jasmine-summary ul.jasmine-suite { margin-top: 7px; margin-bottom: 7px; } - -.jasmine_html-reporter .jasmine-summary li.jasmine-passed a { color: #007069; } - -.jasmine_html-reporter .jasmine-summary li.jasmine-failed a { color: #ca3a11; } - -.jasmine_html-reporter .jasmine-summary li.jasmine-empty a { color: #ba9d37; } - -.jasmine_html-reporter .jasmine-summary li.jasmine-pending a { color: #ba9d37; } - -.jasmine_html-reporter .jasmine-summary li.jasmine-excluded a { color: #bababa; } - -.jasmine_html-reporter .jasmine-specs li.jasmine-passed a:before { content: "• "; } - -.jasmine_html-reporter .jasmine-specs li.jasmine-failed a:before { content: "× "; } - -.jasmine_html-reporter .jasmine-specs li.jasmine-empty a:before { content: "* "; } - -.jasmine_html-reporter .jasmine-specs li.jasmine-pending a:before { content: "• "; } - -.jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before { content: "• "; } - -.jasmine_html-reporter .jasmine-description + .jasmine-suite { margin-top: 0; } - -.jasmine_html-reporter .jasmine-suite { margin-top: 14px; } - -.jasmine_html-reporter .jasmine-suite a { color: #333; } - -.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { margin-bottom: 28px; } - -.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { background-color: #ca3a11; color: white; } - -.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { color: white; } - -.jasmine_html-reporter .jasmine-result-message { padding-top: 14px; color: #333; white-space: pre-wrap; } - -.jasmine_html-reporter .jasmine-result-message span.jasmine-result { display: block; } - -.jasmine_html-reporter .jasmine-stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; } diff --git a/lib/jasmine-3.5.0/jasmine.js b/lib/jasmine-3.5.0/jasmine.js deleted file mode 100644 index 4875624..0000000 --- a/lib/jasmine-3.5.0/jasmine.js +++ /dev/null @@ -1,8218 +0,0 @@ -/* -Copyright (c) 2008-2019 Pivotal Labs - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -// eslint-disable-next-line no-unused-vars -var getJasmineRequireObj = (function(jasmineGlobal) { - var jasmineRequire; - - if ( - typeof module !== 'undefined' && - module.exports && - typeof exports !== 'undefined' - ) { - if (typeof global !== 'undefined') { - jasmineGlobal = global; - } else { - jasmineGlobal = {}; - } - jasmineRequire = exports; - } else { - if ( - typeof window !== 'undefined' && - typeof window.toString === 'function' && - window.toString() === '[object GjsGlobal]' - ) { - jasmineGlobal = window; - } - jasmineRequire = jasmineGlobal.jasmineRequire = {}; - } - - function getJasmineRequire() { - return jasmineRequire; - } - - getJasmineRequire().core = function(jRequire) { - var j$ = {}; - - jRequire.base(j$, jasmineGlobal); - j$.util = jRequire.util(j$); - j$.errors = jRequire.errors(); - j$.formatErrorMsg = jRequire.formatErrorMsg(); - j$.Any = jRequire.Any(j$); - j$.Anything = jRequire.Anything(j$); - j$.CallTracker = jRequire.CallTracker(j$); - j$.MockDate = jRequire.MockDate(); - j$.getClearStack = jRequire.clearStack(j$); - j$.Clock = jRequire.Clock(); - j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); - j$.Env = jRequire.Env(j$); - j$.StackTrace = jRequire.StackTrace(j$); - j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); - j$.ExpectationFilterChain = jRequire.ExpectationFilterChain(); - j$.Expector = jRequire.Expector(j$); - j$.Expectation = jRequire.Expectation(j$); - j$.buildExpectationResult = jRequire.buildExpectationResult(); - j$.noopTimer = jRequire.noopTimer(); - j$.JsApiReporter = jRequire.JsApiReporter(j$); - j$.matchersUtil = jRequire.matchersUtil(j$); - j$.ObjectContaining = jRequire.ObjectContaining(j$); - j$.ArrayContaining = jRequire.ArrayContaining(j$); - j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); - j$.MapContaining = jRequire.MapContaining(j$); - j$.SetContaining = jRequire.SetContaining(j$); - j$.pp = jRequire.pp(j$); - j$.QueueRunner = jRequire.QueueRunner(j$); - j$.ReportDispatcher = jRequire.ReportDispatcher(j$); - j$.Spec = jRequire.Spec(j$); - j$.Spy = jRequire.Spy(j$); - j$.SpyFactory = jRequire.SpyFactory(j$); - j$.SpyRegistry = jRequire.SpyRegistry(j$); - j$.SpyStrategy = jRequire.SpyStrategy(j$); - j$.StringMatching = jRequire.StringMatching(j$); - j$.UserContext = jRequire.UserContext(j$); - j$.Suite = jRequire.Suite(j$); - j$.Timer = jRequire.Timer(); - j$.TreeProcessor = jRequire.TreeProcessor(); - j$.version = jRequire.version(); - j$.Order = jRequire.Order(); - j$.DiffBuilder = jRequire.DiffBuilder(j$); - j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); - j$.ObjectPath = jRequire.ObjectPath(j$); - j$.GlobalErrors = jRequire.GlobalErrors(j$); - - j$.Truthy = jRequire.Truthy(j$); - j$.Falsy = jRequire.Falsy(j$); - j$.Empty = jRequire.Empty(j$); - j$.NotEmpty = jRequire.NotEmpty(j$); - - j$.matchers = jRequire.requireMatchers(jRequire, j$); - j$.asyncMatchers = jRequire.requireAsyncMatchers(jRequire, j$); - - return j$; - }; - - return getJasmineRequire; -})(this); - -getJasmineRequireObj().requireMatchers = function(jRequire, j$) { - var availableMatchers = [ - 'nothing', - 'toBe', - 'toBeCloseTo', - 'toBeDefined', - 'toBeInstanceOf', - 'toBeFalse', - 'toBeFalsy', - 'toBeGreaterThan', - 'toBeGreaterThanOrEqual', - 'toBeLessThan', - 'toBeLessThanOrEqual', - 'toBeNaN', - 'toBeNegativeInfinity', - 'toBeNull', - 'toBePositiveInfinity', - 'toBeTrue', - 'toBeTruthy', - 'toBeUndefined', - 'toContain', - 'toEqual', - 'toHaveBeenCalled', - 'toHaveBeenCalledBefore', - 'toHaveBeenCalledTimes', - 'toHaveBeenCalledWith', - 'toHaveClass', - 'toMatch', - 'toThrow', - 'toThrowError', - 'toThrowMatching' - ], - matchers = {}; - - for (var i = 0; i < availableMatchers.length; i++) { - var name = availableMatchers[i]; - matchers[name] = jRequire[name](j$); - } - - return matchers; -}; - -getJasmineRequireObj().base = function(j$, jasmineGlobal) { - j$.unimplementedMethod_ = function() { - throw new Error('unimplemented method'); - }; - - /** - * Maximum object depth the pretty printer will print to. - * Set this to a lower value to speed up pretty printing if you have large objects. - * @name jasmine.MAX_PRETTY_PRINT_DEPTH - * @since 1.3.0 - */ - j$.MAX_PRETTY_PRINT_DEPTH = 8; - /** - * Maximum number of array elements to display when pretty printing objects. - * This will also limit the number of keys and values displayed for an object. - * Elements past this number will be ellipised. - * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH - * @since 2.7.0 - */ - j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; - /** - * Maximum number of characters to display when pretty printing objects. - * Characters past this number will be ellipised. - * @name jasmine.MAX_PRETTY_PRINT_CHARS - * @since 2.9.0 - */ - j$.MAX_PRETTY_PRINT_CHARS = 1000; - /** - * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. - * @name jasmine.DEFAULT_TIMEOUT_INTERVAL - * @since 1.3.0 - */ - j$.DEFAULT_TIMEOUT_INTERVAL = 5000; - - j$.getGlobal = function() { - return jasmineGlobal; - }; - - /** - * Get the currently booted Jasmine Environment. - * - * @name jasmine.getEnv - * @since 1.3.0 - * @function - * @return {Env} - */ - j$.getEnv = function(options) { - var env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); - //jasmine. singletons in here (setTimeout blah blah). - return env; - }; - - j$.isArray_ = function(value) { - return j$.isA_('Array', value); - }; - - j$.isObject_ = function(value) { - return ( - !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value) - ); - }; - - j$.isString_ = function(value) { - return j$.isA_('String', value); - }; - - j$.isNumber_ = function(value) { - return j$.isA_('Number', value); - }; - - j$.isFunction_ = function(value) { - return j$.isA_('Function', value); - }; - - j$.isAsyncFunction_ = function(value) { - return j$.isA_('AsyncFunction', value); - }; - - j$.isTypedArray_ = function(value) { - return ( - j$.isA_('Float32Array', value) || - j$.isA_('Float64Array', value) || - j$.isA_('Int16Array', value) || - j$.isA_('Int32Array', value) || - j$.isA_('Int8Array', value) || - j$.isA_('Uint16Array', value) || - j$.isA_('Uint32Array', value) || - j$.isA_('Uint8Array', value) || - j$.isA_('Uint8ClampedArray', value) - ); - }; - - j$.isA_ = function(typeName, value) { - return j$.getType_(value) === '[object ' + typeName + ']'; - }; - - j$.isError_ = function(value) { - if (value instanceof Error) { - return true; - } - if (value && value.constructor && value.constructor.constructor) { - var valueGlobal = value.constructor.constructor('return this'); - if (j$.isFunction_(valueGlobal)) { - valueGlobal = valueGlobal(); - } - - if (valueGlobal.Error && value instanceof valueGlobal.Error) { - return true; - } - } - return false; - }; - - j$.getType_ = function(value) { - return Object.prototype.toString.apply(value); - }; - - j$.isDomNode = function(obj) { - // Node is a function, because constructors - return typeof jasmineGlobal.Node !== 'undefined' - ? obj instanceof jasmineGlobal.Node - : obj !== null && - typeof obj === 'object' && - typeof obj.nodeType === 'number' && - typeof obj.nodeName === 'string'; - // return obj.nodeType > 0; - }; - - j$.isMap = function(obj) { - return ( - obj !== null && - typeof obj !== 'undefined' && - typeof jasmineGlobal.Map !== 'undefined' && - obj.constructor === jasmineGlobal.Map - ); - }; - - j$.isSet = function(obj) { - return ( - obj !== null && - typeof obj !== 'undefined' && - typeof jasmineGlobal.Set !== 'undefined' && - obj.constructor === jasmineGlobal.Set - ); - }; - - j$.isPromise = function(obj) { - return ( - typeof jasmineGlobal.Promise !== 'undefined' && - !!obj && - obj.constructor === jasmineGlobal.Promise - ); - }; - - j$.isPromiseLike = function(obj) { - return !!obj && j$.isFunction_(obj.then); - }; - - j$.fnNameFor = function(func) { - if (func.name) { - return func.name; - } - - var matches = - func.toString().match(/^\s*function\s*(\w+)\s*\(/) || - func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/); - - return matches ? matches[1] : ''; - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value being compared is an instance of the specified class/constructor. - * @name jasmine.any - * @since 1.3.0 - * @function - * @param {Constructor} clazz - The constructor to check against. - */ - j$.any = function(clazz) { - return new j$.Any(clazz); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value being compared is not `null` and not `undefined`. - * @name jasmine.anything - * @since 2.2.0 - * @function - */ - j$.anything = function() { - return new j$.Anything(); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value being compared is `true` or anything truthy. - * @name jasmine.truthy - * @since 3.1.0 - * @function - */ - j$.truthy = function() { - return new j$.Truthy(); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey. - * @name jasmine.falsy - * @since 3.1.0 - * @function - */ - j$.falsy = function() { - return new j$.Falsy(); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value being compared is empty. - * @name jasmine.empty - * @since 3.1.0 - * @function - */ - j$.empty = function() { - return new j$.Empty(); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value being compared is not empty. - * @name jasmine.notEmpty - * @since 3.1.0 - * @function - */ - j$.notEmpty = function() { - return new j$.NotEmpty(); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value being compared contains at least the keys and values. - * @name jasmine.objectContaining - * @since 1.3.0 - * @function - * @param {Object} sample - The subset of properties that _must_ be in the actual. - */ - j$.objectContaining = function(sample) { - return new j$.ObjectContaining(sample); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`. - * @name jasmine.stringMatching - * @since 2.2.0 - * @function - * @param {RegExp|String} expected - */ - j$.stringMatching = function(expected) { - return new j$.StringMatching(expected); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value is an `Array` that contains at least the elements in the sample. - * @name jasmine.arrayContaining - * @since 2.2.0 - * @function - * @param {Array} sample - */ - j$.arrayContaining = function(sample) { - return new j$.ArrayContaining(sample); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. - * @name jasmine.arrayWithExactContents - * @since 2.8.0 - * @function - * @param {Array} sample - */ - j$.arrayWithExactContents = function(sample) { - return new j$.ArrayWithExactContents(sample); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if every key/value pair in the sample passes the deep equality comparison - * with at least one key/value pair in the actual value being compared - * @name jasmine.mapContaining - * @since 3.5.0 - * @function - * @param {Map} sample - The subset of items that _must_ be in the actual. - */ - j$.mapContaining = function(sample) { - return new j$.MapContaining(sample); - }; - - /** - * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), - * that will succeed if every item in the sample passes the deep equality comparison - * with at least one item in the actual value being compared - * @name jasmine.setContaining - * @since 3.5.0 - * @function - * @param {Set} sample - The subset of items that _must_ be in the actual. - */ - j$.setContaining = function(sample) { - return new j$.SetContaining(sample); - }; - - j$.isSpy = function(putativeSpy) { - if (!putativeSpy) { - return false; - } - return ( - putativeSpy.and instanceof j$.SpyStrategy && - putativeSpy.calls instanceof j$.CallTracker - ); - }; -}; - -getJasmineRequireObj().util = function(j$) { - var util = {}; - - util.inherit = function(childClass, parentClass) { - var Subclass = function() {}; - Subclass.prototype = parentClass.prototype; - childClass.prototype = new Subclass(); - }; - - util.htmlEscape = function(str) { - if (!str) { - return str; - } - return str - .replace(/&/g, '&') - .replace(//g, '>'); - }; - - util.argsToArray = function(args) { - var arrayOfArgs = []; - for (var i = 0; i < args.length; i++) { - arrayOfArgs.push(args[i]); - } - return arrayOfArgs; - }; - - util.isUndefined = function(obj) { - return obj === void 0; - }; - - util.arrayContains = function(array, search) { - var i = array.length; - while (i--) { - if (array[i] === search) { - return true; - } - } - return false; - }; - - util.clone = function(obj) { - if (Object.prototype.toString.apply(obj) === '[object Array]') { - return obj.slice(); - } - - var cloned = {}; - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { - cloned[prop] = obj[prop]; - } - } - - return cloned; - }; - - util.cloneArgs = function(args) { - var clonedArgs = []; - var argsAsArray = j$.util.argsToArray(args); - for (var i = 0; i < argsAsArray.length; i++) { - var str = Object.prototype.toString.apply(argsAsArray[i]), - primitives = /^\[object (Boolean|String|RegExp|Number)/; - - // All falsey values are either primitives, `null`, or `undefined. - if (!argsAsArray[i] || str.match(primitives)) { - clonedArgs.push(argsAsArray[i]); - } else { - clonedArgs.push(j$.util.clone(argsAsArray[i])); - } - } - return clonedArgs; - }; - - util.getPropertyDescriptor = function(obj, methodName) { - var descriptor, - proto = obj; - - do { - descriptor = Object.getOwnPropertyDescriptor(proto, methodName); - proto = Object.getPrototypeOf(proto); - } while (!descriptor && proto); - - return descriptor; - }; - - util.objectDifference = function(obj, toRemove) { - var diff = {}; - - for (var key in obj) { - if (util.has(obj, key) && !util.has(toRemove, key)) { - diff[key] = obj[key]; - } - } - - return diff; - }; - - util.has = function(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); - }; - - util.errorWithStack = function errorWithStack() { - // Don't throw and catch if we don't have to, because it makes it harder - // for users to debug their code with exception breakpoints. - var error = new Error(); - - if (error.stack) { - return error; - } - - // But some browsers (e.g. Phantom) only provide a stack trace if we throw. - try { - throw new Error(); - } catch (e) { - return e; - } - }; - - function callerFile() { - var trace = new j$.StackTrace(util.errorWithStack()); - return trace.frames[2].file; - } - - util.jasmineFile = (function() { - var result; - - return function() { - if (!result) { - result = callerFile(); - } - - return result; - }; - })(); - - function StopIteration() {} - StopIteration.prototype = Object.create(Error.prototype); - StopIteration.prototype.constructor = StopIteration; - - // useful for maps and sets since `forEach` is the only IE11-compatible way to iterate them - util.forEachBreakable = function(iterable, iteratee) { - function breakLoop() { - throw new StopIteration(); - } - - try { - iterable.forEach(function(value, key) { - iteratee(breakLoop, value, key, iterable); - }); - } catch (error) { - if (!(error instanceof StopIteration)) throw error; - } - }; - - return util; -}; - -getJasmineRequireObj().Spec = function(j$) { - function Spec(attrs) { - this.expectationFactory = attrs.expectationFactory; - this.asyncExpectationFactory = attrs.asyncExpectationFactory; - this.resultCallback = attrs.resultCallback || function() {}; - this.id = attrs.id; - this.description = attrs.description || ''; - this.queueableFn = attrs.queueableFn; - this.beforeAndAfterFns = - attrs.beforeAndAfterFns || - function() { - return { befores: [], afters: [] }; - }; - this.userContext = - attrs.userContext || - function() { - return {}; - }; - this.onStart = attrs.onStart || function() {}; - this.getSpecName = - attrs.getSpecName || - function() { - return ''; - }; - this.expectationResultFactory = - attrs.expectationResultFactory || function() {}; - this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; - this.catchingExceptions = - attrs.catchingExceptions || - function() { - return true; - }; - this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; - this.timer = attrs.timer || j$.noopTimer; - - if (!this.queueableFn.fn) { - this.pend(); - } - - /** - * @typedef SpecResult - * @property {Int} id - The unique id of this spec. - * @property {String} description - The description passed to the {@link it} that created this spec. - * @property {String} fullName - The full description including all ancestors of this spec. - * @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec. - * @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec. - * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec. - * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. - * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. - * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. - */ - this.result = { - id: this.id, - description: this.description, - fullName: this.getFullName(), - failedExpectations: [], - passedExpectations: [], - deprecationWarnings: [], - pendingReason: '', - duration: null - }; - } - - Spec.prototype.addExpectationResult = function(passed, data, isError) { - var expectationResult = this.expectationResultFactory(data); - if (passed) { - this.result.passedExpectations.push(expectationResult); - } else { - this.result.failedExpectations.push(expectationResult); - - if (this.throwOnExpectationFailure && !isError) { - throw new j$.errors.ExpectationFailed(); - } - } - }; - - Spec.prototype.expect = function(actual) { - return this.expectationFactory(actual, this); - }; - - Spec.prototype.expectAsync = function(actual) { - return this.asyncExpectationFactory(actual, this); - }; - - Spec.prototype.execute = function(onComplete, excluded, failSpecWithNoExp) { - var self = this; - - var onStart = { - fn: function(done) { - self.timer.start(); - self.onStart(self, done); - } - }; - - var complete = { - fn: function(done) { - self.queueableFn.fn = null; - self.result.status = self.status(excluded, failSpecWithNoExp); - self.resultCallback(self.result, done); - } - }; - - var fns = this.beforeAndAfterFns(); - var regularFns = fns.befores.concat(this.queueableFn); - - var runnerConfig = { - isLeaf: true, - queueableFns: regularFns, - cleanupFns: fns.afters, - onException: function() { - self.onException.apply(self, arguments); - }, - onComplete: function() { - self.result.duration = self.timer.elapsed(); - onComplete( - self.result.status === 'failed' && - new j$.StopExecutionError('spec failed') - ); - }, - userContext: this.userContext() - }; - - if (this.markedPending || excluded === true) { - runnerConfig.queueableFns = []; - runnerConfig.cleanupFns = []; - } - - runnerConfig.queueableFns.unshift(onStart); - runnerConfig.cleanupFns.push(complete); - - this.queueRunnerFactory(runnerConfig); - }; - - Spec.prototype.onException = function onException(e) { - if (Spec.isPendingSpecException(e)) { - this.pend(extractCustomPendingMessage(e)); - return; - } - - if (e instanceof j$.errors.ExpectationFailed) { - return; - } - - this.addExpectationResult( - false, - { - matcherName: '', - passed: false, - expected: '', - actual: '', - error: e - }, - true - ); - }; - - Spec.prototype.pend = function(message) { - this.markedPending = true; - if (message) { - this.result.pendingReason = message; - } - }; - - Spec.prototype.getResult = function() { - this.result.status = this.status(); - return this.result; - }; - - Spec.prototype.status = function(excluded, failSpecWithNoExpectations) { - if (excluded === true) { - return 'excluded'; - } - - if (this.markedPending) { - return 'pending'; - } - - if ( - this.result.failedExpectations.length > 0 || - (failSpecWithNoExpectations && - this.result.failedExpectations.length + - this.result.passedExpectations.length === - 0) - ) { - return 'failed'; - } - - return 'passed'; - }; - - Spec.prototype.getFullName = function() { - return this.getSpecName(this); - }; - - Spec.prototype.addDeprecationWarning = function(deprecation) { - if (typeof deprecation === 'string') { - deprecation = { message: deprecation }; - } - this.result.deprecationWarnings.push( - this.expectationResultFactory(deprecation) - ); - }; - - var extractCustomPendingMessage = function(e) { - var fullMessage = e.toString(), - boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), - boilerplateEnd = - boilerplateStart + Spec.pendingSpecExceptionMessage.length; - - return fullMessage.substr(boilerplateEnd); - }; - - Spec.pendingSpecExceptionMessage = '=> marked Pending'; - - Spec.isPendingSpecException = function(e) { - return !!( - e && - e.toString && - e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1 - ); - }; - - return Spec; -}; - -if (typeof window == void 0 && typeof exports == 'object') { - /* globals exports */ - exports.Spec = jasmineRequire.Spec; -} - -/*jshint bitwise: false*/ - -getJasmineRequireObj().Order = function() { - function Order(options) { - this.random = 'random' in options ? options.random : true; - var seed = (this.seed = options.seed || generateSeed()); - this.sort = this.random ? randomOrder : naturalOrder; - - function naturalOrder(items) { - return items; - } - - function randomOrder(items) { - var copy = items.slice(); - copy.sort(function(a, b) { - return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); - }); - return copy; - } - - function generateSeed() { - return String(Math.random()).slice(-5); - } - - // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function - // used to get a different output when the key changes slightly. - // We use your return to sort the children randomly in a consistent way when - // used in conjunction with a seed - - function jenkinsHash(key) { - var hash, i; - for (hash = i = 0; i < key.length; ++i) { - hash += key.charCodeAt(i); - hash += hash << 10; - hash ^= hash >> 6; - } - hash += hash << 3; - hash ^= hash >> 11; - hash += hash << 15; - return hash; - } - } - - return Order; -}; - -getJasmineRequireObj().Env = function(j$) { - /** - * _Note:_ Do not construct this directly, Jasmine will make one during booting. - * @name Env - * @since 2.0.0 - * @classdesc The Jasmine environment - * @constructor - */ - function Env(options) { - options = options || {}; - - var self = this; - var global = options.global || j$.getGlobal(); - var customPromise; - - var totalSpecsDefined = 0; - - var realSetTimeout = global.setTimeout; - var realClearTimeout = global.clearTimeout; - var clearStack = j$.getClearStack(global); - this.clock = new j$.Clock( - global, - function() { - return new j$.DelayedFunctionScheduler(); - }, - new j$.MockDate(global) - ); - - var runnableResources = {}; - - var currentSpec = null; - var currentlyExecutingSuites = []; - var currentDeclarationSuite = null; - var hasFailures = false; - - /** - * This represents the available options to configure Jasmine. - * Options that are not provided will use their default values - * @interface Configuration - * @since 3.3.0 - */ - var config = { - /** - * Whether to randomize spec execution order - * @name Configuration#random - * @since 3.3.0 - * @type Boolean - * @default true - */ - random: true, - /** - * Seed to use as the basis of randomization. - * Null causes the seed to be determined randomly at the start of execution. - * @name Configuration#seed - * @since 3.3.0 - * @type function - * @default null - */ - seed: null, - /** - * Whether to stop execution of the suite after the first spec failure - * @name Configuration#failFast - * @since 3.3.0 - * @type Boolean - * @default false - */ - failFast: false, - /** - * Whether to fail the spec if it ran no expectations. By default - * a spec that ran no expectations is reported as passed. Setting this - * to true will report such spec as a failure. - * @name Configuration#failSpecWithNoExpectations - * @since 3.5.0 - * @type Boolean - * @default false - */ - failSpecWithNoExpectations: false, - /** - * Whether to cause specs to only have one expectation failure. - * @name Configuration#oneFailurePerSpec - * @since 3.3.0 - * @type Boolean - * @default false - */ - oneFailurePerSpec: false, - /** - * Function to use to filter specs - * @name Configuration#specFilter - * @since 3.3.0 - * @type function - * @default true - */ - specFilter: function() { - return true; - }, - /** - * Whether or not reporters should hide disabled specs from their output. - * Currently only supported by Jasmine's HTMLReporter - * @name Configuration#hideDisabled - * @since 3.3.0 - * @type Boolean - * @default false - */ - hideDisabled: false, - /** - * Set to provide a custom promise library that Jasmine will use if it needs - * to create a promise. If not set, it will default to whatever global Promise - * library is available (if any). - * @name Configuration#Promise - * @since 3.5.0 - * @type function - * @default undefined - */ - Promise: undefined - }; - - var currentSuite = function() { - return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; - }; - - var currentRunnable = function() { - return currentSpec || currentSuite(); - }; - - var globalErrors = null; - - var installGlobalErrors = function() { - if (globalErrors) { - return; - } - - globalErrors = new j$.GlobalErrors(); - globalErrors.install(); - }; - - if (!options.suppressLoadErrors) { - installGlobalErrors(); - globalErrors.pushListener(function( - message, - filename, - lineno, - colNo, - err - ) { - topSuite.result.failedExpectations.push({ - passed: false, - globalErrorType: 'load', - message: message, - stack: err && err.stack, - filename: filename, - lineno: lineno - }); - }); - } - - /** - * Configure your jasmine environment - * @name Env#configure - * @since 3.3.0 - * @argument {Configuration} configuration - * @function - */ - this.configure = function(configuration) { - if (configuration.specFilter) { - config.specFilter = configuration.specFilter; - } - - if (configuration.hasOwnProperty('random')) { - config.random = !!configuration.random; - } - - if (configuration.hasOwnProperty('seed')) { - config.seed = configuration.seed; - } - - if (configuration.hasOwnProperty('failFast')) { - config.failFast = configuration.failFast; - } - - if (configuration.hasOwnProperty('failSpecWithNoExpectations')) { - config.failSpecWithNoExpectations = - configuration.failSpecWithNoExpectations; - } - - if (configuration.hasOwnProperty('oneFailurePerSpec')) { - config.oneFailurePerSpec = configuration.oneFailurePerSpec; - } - - if (configuration.hasOwnProperty('hideDisabled')) { - config.hideDisabled = configuration.hideDisabled; - } - - // Don't use hasOwnProperty to check for Promise existence because Promise - // can be initialized to undefined, either explicitly or by using the - // object returned from Env#configuration. In particular, Karma does this. - if (configuration.Promise) { - if ( - typeof configuration.Promise.resolve === 'function' && - typeof configuration.Promise.reject === 'function' - ) { - customPromise = configuration.Promise; - } else { - throw new Error( - 'Custom promise library missing `resolve`/`reject` functions' - ); - } - } - }; - - /** - * Get the current configuration for your jasmine environment - * @name Env#configuration - * @since 3.3.0 - * @function - * @returns {Configuration} - */ - this.configuration = function() { - var result = {}; - for (var property in config) { - result[property] = config[property]; - } - return result; - }; - - Object.defineProperty(this, 'specFilter', { - get: function() { - self.deprecated( - 'Getting specFilter directly from Env is deprecated and will be removed in a future version of Jasmine, please check the specFilter option from `configuration`' - ); - return config.specFilter; - }, - set: function(val) { - self.deprecated( - 'Setting specFilter directly on Env is deprecated and will be removed in a future version of Jasmine, please use the specFilter option in `configure`' - ); - config.specFilter = val; - } - }); - - this.setDefaultSpyStrategy = function(defaultStrategyFn) { - if (!currentRunnable()) { - throw new Error( - 'Default spy strategy must be set in a before function or a spec' - ); - } - runnableResources[ - currentRunnable().id - ].defaultStrategyFn = defaultStrategyFn; - }; - - this.addSpyStrategy = function(name, fn) { - if (!currentRunnable()) { - throw new Error( - 'Custom spy strategies must be added in a before function or a spec' - ); - } - runnableResources[currentRunnable().id].customSpyStrategies[name] = fn; - }; - - this.addCustomEqualityTester = function(tester) { - if (!currentRunnable()) { - throw new Error( - 'Custom Equalities must be added in a before function or a spec' - ); - } - runnableResources[currentRunnable().id].customEqualityTesters.push( - tester - ); - }; - - this.addMatchers = function(matchersToAdd) { - if (!currentRunnable()) { - throw new Error( - 'Matchers must be added in a before function or a spec' - ); - } - var customMatchers = - runnableResources[currentRunnable().id].customMatchers; - for (var matcherName in matchersToAdd) { - customMatchers[matcherName] = matchersToAdd[matcherName]; - } - }; - - this.addAsyncMatchers = function(matchersToAdd) { - if (!currentRunnable()) { - throw new Error( - 'Async Matchers must be added in a before function or a spec' - ); - } - var customAsyncMatchers = - runnableResources[currentRunnable().id].customAsyncMatchers; - for (var matcherName in matchersToAdd) { - customAsyncMatchers[matcherName] = matchersToAdd[matcherName]; - } - }; - - j$.Expectation.addCoreMatchers(j$.matchers); - j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); - - var nextSpecId = 0; - var getNextSpecId = function() { - return 'spec' + nextSpecId++; - }; - - var nextSuiteId = 0; - var getNextSuiteId = function() { - return 'suite' + nextSuiteId++; - }; - - var expectationFactory = function(actual, spec) { - return j$.Expectation.factory({ - util: j$.matchersUtil, - customEqualityTesters: runnableResources[spec.id].customEqualityTesters, - customMatchers: runnableResources[spec.id].customMatchers, - actual: actual, - addExpectationResult: addExpectationResult - }); - - function addExpectationResult(passed, result) { - return spec.addExpectationResult(passed, result); - } - }; - - var asyncExpectationFactory = function(actual, spec) { - return j$.Expectation.asyncFactory({ - util: j$.matchersUtil, - customEqualityTesters: runnableResources[spec.id].customEqualityTesters, - customAsyncMatchers: runnableResources[spec.id].customAsyncMatchers, - actual: actual, - addExpectationResult: addExpectationResult - }); - - function addExpectationResult(passed, result) { - return spec.addExpectationResult(passed, result); - } - }; - - var defaultResourcesForRunnable = function(id, parentRunnableId) { - var resources = { - spies: [], - customEqualityTesters: [], - customMatchers: {}, - customAsyncMatchers: {}, - customSpyStrategies: {}, - defaultStrategyFn: undefined - }; - - if (runnableResources[parentRunnableId]) { - resources.customEqualityTesters = j$.util.clone( - runnableResources[parentRunnableId].customEqualityTesters - ); - resources.customMatchers = j$.util.clone( - runnableResources[parentRunnableId].customMatchers - ); - resources.customAsyncMatchers = j$.util.clone( - runnableResources[parentRunnableId].customAsyncMatchers - ); - resources.defaultStrategyFn = - runnableResources[parentRunnableId].defaultStrategyFn; - } - - runnableResources[id] = resources; - }; - - var clearResourcesForRunnable = function(id) { - spyRegistry.clearSpies(); - delete runnableResources[id]; - }; - - var beforeAndAfterFns = function(suite) { - return function() { - var befores = [], - afters = []; - - while (suite) { - befores = befores.concat(suite.beforeFns); - afters = afters.concat(suite.afterFns); - - suite = suite.parentSuite; - } - - return { - befores: befores.reverse(), - afters: afters - }; - }; - }; - - var getSpecName = function(spec, suite) { - var fullName = [spec.description], - suiteFullName = suite.getFullName(); - - if (suiteFullName !== '') { - fullName.unshift(suiteFullName); - } - return fullName.join(' '); - }; - - // TODO: we may just be able to pass in the fn instead of wrapping here - var buildExpectationResult = j$.buildExpectationResult, - exceptionFormatter = new j$.ExceptionFormatter(), - expectationResultFactory = function(attrs) { - attrs.messageFormatter = exceptionFormatter.message; - attrs.stackFormatter = exceptionFormatter.stack; - - return buildExpectationResult(attrs); - }; - - /** - * Sets whether Jasmine should throw an Error when an expectation fails. - * This causes a spec to only have one expectation failure. - * @name Env#throwOnExpectationFailure - * @since 2.3.0 - * @function - * @param {Boolean} value Whether to throw when a expectation fails - * @deprecated Use the `oneFailurePerSpec` option with {@link Env#configure} - */ - this.throwOnExpectationFailure = function(value) { - this.deprecated( - 'Setting throwOnExpectationFailure directly on Env is deprecated and will be removed in a future version of Jasmine, please use the oneFailurePerSpec option in `configure`' - ); - this.configure({ oneFailurePerSpec: !!value }); - }; - - this.throwingExpectationFailures = function() { - this.deprecated( - 'Getting throwingExpectationFailures directly from Env is deprecated and will be removed in a future version of Jasmine, please check the oneFailurePerSpec option from `configuration`' - ); - return config.oneFailurePerSpec; - }; - - /** - * Set whether to stop suite execution when a spec fails - * @name Env#stopOnSpecFailure - * @since 2.7.0 - * @function - * @param {Boolean} value Whether to stop suite execution when a spec fails - * @deprecated Use the `failFast` option with {@link Env#configure} - */ - this.stopOnSpecFailure = function(value) { - this.deprecated( - 'Setting stopOnSpecFailure directly is deprecated and will be removed in a future version of Jasmine, please use the failFast option in `configure`' - ); - this.configure({ failFast: !!value }); - }; - - this.stoppingOnSpecFailure = function() { - this.deprecated( - 'Getting stoppingOnSpecFailure directly from Env is deprecated and will be removed in a future version of Jasmine, please check the failFast option from `configuration`' - ); - return config.failFast; - }; - - /** - * Set whether to randomize test execution order - * @name Env#randomizeTests - * @since 2.4.0 - * @function - * @param {Boolean} value Whether to randomize execution order - * @deprecated Use the `random` option with {@link Env#configure} - */ - this.randomizeTests = function(value) { - this.deprecated( - 'Setting randomizeTests directly is deprecated and will be removed in a future version of Jasmine, please use the random option in `configure`' - ); - config.random = !!value; - }; - - this.randomTests = function() { - this.deprecated( - 'Getting randomTests directly from Env is deprecated and will be removed in a future version of Jasmine, please check the random option from `configuration`' - ); - return config.random; - }; - - /** - * Set the random number seed for spec randomization - * @name Env#seed - * @since 2.4.0 - * @function - * @param {Number} value The seed value - * @deprecated Use the `seed` option with {@link Env#configure} - */ - this.seed = function(value) { - this.deprecated( - 'Setting seed directly is deprecated and will be removed in a future version of Jasmine, please use the seed option in `configure`' - ); - if (value) { - config.seed = value; - } - return config.seed; - }; - - this.hidingDisabled = function(value) { - this.deprecated( - 'Getting hidingDisabled directly from Env is deprecated and will be removed in a future version of Jasmine, please check the hideDisabled option from `configuration`' - ); - return config.hideDisabled; - }; - - /** - * @name Env#hideDisabled - * @since 3.2.0 - * @function - */ - this.hideDisabled = function(value) { - this.deprecated( - 'Setting hideDisabled directly is deprecated and will be removed in a future version of Jasmine, please use the hideDisabled option in `configure`' - ); - config.hideDisabled = !!value; - }; - - this.deprecated = function(deprecation) { - var runnable = currentRunnable() || topSuite; - runnable.addDeprecationWarning(deprecation); - if ( - typeof console !== 'undefined' && - typeof console.error === 'function' - ) { - console.error('DEPRECATION:', deprecation); - } - }; - - var queueRunnerFactory = function(options, args) { - var failFast = false; - if (options.isLeaf) { - failFast = config.oneFailurePerSpec; - } else if (!options.isReporter) { - failFast = config.failFast; - } - options.clearStack = options.clearStack || clearStack; - options.timeout = { - setTimeout: realSetTimeout, - clearTimeout: realClearTimeout - }; - options.fail = self.fail; - options.globalErrors = globalErrors; - options.completeOnFirstError = failFast; - options.onException = - options.onException || - function(e) { - (currentRunnable() || topSuite).onException(e); - }; - options.deprecated = self.deprecated; - - new j$.QueueRunner(options).execute(args); - }; - - var topSuite = new j$.Suite({ - env: this, - id: getNextSuiteId(), - description: 'Jasmine__TopLevel__Suite', - expectationFactory: expectationFactory, - asyncExpectationFactory: asyncExpectationFactory, - expectationResultFactory: expectationResultFactory - }); - defaultResourcesForRunnable(topSuite.id); - currentDeclarationSuite = topSuite; - - this.topSuite = function() { - return topSuite; - }; - - /** - * This represents the available reporter callback for an object passed to {@link Env#addReporter}. - * @interface Reporter - * @see custom_reporter - */ - var reporter = new j$.ReportDispatcher( - [ - /** - * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts. - * @function - * @name Reporter#jasmineStarted - * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run - * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. - * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. - * @see async - */ - 'jasmineStarted', - /** - * When the entire suite has finished execution `jasmineDone` is called - * @function - * @name Reporter#jasmineDone - * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running. - * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. - * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. - * @see async - */ - 'jasmineDone', - /** - * `suiteStarted` is invoked when a `describe` starts to run - * @function - * @name Reporter#suiteStarted - * @param {SuiteResult} result Information about the individual {@link describe} being run - * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. - * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. - * @see async - */ - 'suiteStarted', - /** - * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run - * - * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`. - * @function - * @name Reporter#suiteDone - * @param {SuiteResult} result - * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. - * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. - * @see async - */ - 'suiteDone', - /** - * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) - * @function - * @name Reporter#specStarted - * @param {SpecResult} result Information about the individual {@link it} being run - * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. - * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. - * @see async - */ - 'specStarted', - /** - * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run. - * - * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed. - * @function - * @name Reporter#specDone - * @param {SpecResult} result - * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. - * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. - * @see async - */ - 'specDone' - ], - queueRunnerFactory - ); - - this.execute = function(runnablesToRun) { - installGlobalErrors(); - - if (!runnablesToRun) { - if (focusedRunnables.length) { - runnablesToRun = focusedRunnables; - } else { - runnablesToRun = [topSuite.id]; - } - } - - var order = new j$.Order({ - random: config.random, - seed: config.seed - }); - - var processor = new j$.TreeProcessor({ - tree: topSuite, - runnableIds: runnablesToRun, - queueRunnerFactory: queueRunnerFactory, - failSpecWithNoExpectations: config.failSpecWithNoExpectations, - nodeStart: function(suite, next) { - currentlyExecutingSuites.push(suite); - defaultResourcesForRunnable(suite.id, suite.parentSuite.id); - reporter.suiteStarted(suite.result, next); - suite.startTimer(); - }, - nodeComplete: function(suite, result, next) { - if (suite !== currentSuite()) { - throw new Error('Tried to complete the wrong suite'); - } - - clearResourcesForRunnable(suite.id); - currentlyExecutingSuites.pop(); - - if (result.status === 'failed') { - hasFailures = true; - } - suite.endTimer(); - reporter.suiteDone(result, next); - }, - orderChildren: function(node) { - return order.sort(node.children); - }, - excludeNode: function(spec) { - return !config.specFilter(spec); - } - }); - - if (!processor.processTree().valid) { - throw new Error( - 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' - ); - } - - var jasmineTimer = new j$.Timer(); - jasmineTimer.start(); - - /** - * Information passed to the {@link Reporter#jasmineStarted} event. - * @typedef JasmineStartedInfo - * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. - * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. - */ - reporter.jasmineStarted( - { - totalSpecsDefined: totalSpecsDefined, - order: order - }, - function() { - currentlyExecutingSuites.push(topSuite); - - processor.execute(function() { - clearResourcesForRunnable(topSuite.id); - currentlyExecutingSuites.pop(); - var overallStatus, incompleteReason; - - if (hasFailures || topSuite.result.failedExpectations.length > 0) { - overallStatus = 'failed'; - } else if (focusedRunnables.length > 0) { - overallStatus = 'incomplete'; - incompleteReason = 'fit() or fdescribe() was found'; - } else if (totalSpecsDefined === 0) { - overallStatus = 'incomplete'; - incompleteReason = 'No specs found'; - } else { - overallStatus = 'passed'; - } - - /** - * Information passed to the {@link Reporter#jasmineDone} event. - * @typedef JasmineDoneInfo - * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'. - * @property {Int} totalTime - The total time (in ms) that it took to execute the suite - * @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete. - * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. - * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. - * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. - */ - reporter.jasmineDone( - { - overallStatus: overallStatus, - totalTime: jasmineTimer.elapsed(), - incompleteReason: incompleteReason, - order: order, - failedExpectations: topSuite.result.failedExpectations, - deprecationWarnings: topSuite.result.deprecationWarnings - }, - function() {} - ); - }); - } - ); - }; - - /** - * Add a custom reporter to the Jasmine environment. - * @name Env#addReporter - * @since 2.0.0 - * @function - * @param {Reporter} reporterToAdd The reporter to be added. - * @see custom_reporter - */ - this.addReporter = function(reporterToAdd) { - reporter.addReporter(reporterToAdd); - }; - - /** - * Provide a fallback reporter if no other reporters have been specified. - * @name Env#provideFallbackReporter - * @since 2.5.0 - * @function - * @param {Reporter} reporterToAdd The reporter - * @see custom_reporter - */ - this.provideFallbackReporter = function(reporterToAdd) { - reporter.provideFallbackReporter(reporterToAdd); - }; - - /** - * Clear all registered reporters - * @name Env#clearReporters - * @since 2.5.2 - * @function - */ - this.clearReporters = function() { - reporter.clearReporters(); - }; - - var spyFactory = new j$.SpyFactory( - function getCustomStrategies() { - var runnable = currentRunnable(); - - if (runnable) { - return runnableResources[runnable.id].customSpyStrategies; - } - - return {}; - }, - function getDefaultStrategyFn() { - var runnable = currentRunnable(); - - if (runnable) { - return runnableResources[runnable.id].defaultStrategyFn; - } - - return undefined; - }, - function getPromise() { - return customPromise || global.Promise; - } - ); - - var spyRegistry = new j$.SpyRegistry({ - currentSpies: function() { - if (!currentRunnable()) { - throw new Error( - 'Spies must be created in a before function or a spec' - ); - } - return runnableResources[currentRunnable().id].spies; - }, - createSpy: function(name, originalFn) { - return self.createSpy(name, originalFn); - } - }); - - this.allowRespy = function(allow) { - spyRegistry.allowRespy(allow); - }; - - this.spyOn = function() { - return spyRegistry.spyOn.apply(spyRegistry, arguments); - }; - - this.spyOnProperty = function() { - return spyRegistry.spyOnProperty.apply(spyRegistry, arguments); - }; - - this.spyOnAllFunctions = function() { - return spyRegistry.spyOnAllFunctions.apply(spyRegistry, arguments); - }; - - this.createSpy = function(name, originalFn) { - if (arguments.length === 1 && j$.isFunction_(name)) { - originalFn = name; - name = originalFn.name; - } - - return spyFactory.createSpy(name, originalFn); - }; - - this.createSpyObj = function(baseName, methodNames, propertyNames) { - return spyFactory.createSpyObj(baseName, methodNames, propertyNames); - }; - - var ensureIsFunction = function(fn, caller) { - if (!j$.isFunction_(fn)) { - throw new Error( - caller + ' expects a function argument; received ' + j$.getType_(fn) - ); - } - }; - - var ensureIsFunctionOrAsync = function(fn, caller) { - if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { - throw new Error( - caller + ' expects a function argument; received ' + j$.getType_(fn) - ); - } - }; - - function ensureIsNotNested(method) { - var runnable = currentRunnable(); - if (runnable !== null && runnable !== undefined) { - throw new Error( - "'" + method + "' should only be used in 'describe' function" - ); - } - } - - var suiteFactory = function(description) { - var suite = new j$.Suite({ - env: self, - id: getNextSuiteId(), - description: description, - parentSuite: currentDeclarationSuite, - expectationFactory: expectationFactory, - asyncExpectationFactory: asyncExpectationFactory, - expectationResultFactory: expectationResultFactory, - throwOnExpectationFailure: config.oneFailurePerSpec - }); - - return suite; - }; - - this.describe = function(description, specDefinitions) { - ensureIsNotNested('describe'); - ensureIsFunction(specDefinitions, 'describe'); - var suite = suiteFactory(description); - if (specDefinitions.length > 0) { - throw new Error('describe does not expect any arguments'); - } - if (currentDeclarationSuite.markedPending) { - suite.pend(); - } - addSpecsToSuite(suite, specDefinitions); - return suite; - }; - - this.xdescribe = function(description, specDefinitions) { - ensureIsNotNested('xdescribe'); - ensureIsFunction(specDefinitions, 'xdescribe'); - var suite = suiteFactory(description); - suite.pend(); - addSpecsToSuite(suite, specDefinitions); - return suite; - }; - - var focusedRunnables = []; - - this.fdescribe = function(description, specDefinitions) { - ensureIsNotNested('fdescribe'); - ensureIsFunction(specDefinitions, 'fdescribe'); - var suite = suiteFactory(description); - suite.isFocused = true; - - focusedRunnables.push(suite.id); - unfocusAncestor(); - addSpecsToSuite(suite, specDefinitions); - - return suite; - }; - - function addSpecsToSuite(suite, specDefinitions) { - var parentSuite = currentDeclarationSuite; - parentSuite.addChild(suite); - currentDeclarationSuite = suite; - - var declarationError = null; - try { - specDefinitions.call(suite); - } catch (e) { - declarationError = e; - } - - if (declarationError) { - suite.onException(declarationError); - } - - currentDeclarationSuite = parentSuite; - } - - function findFocusedAncestor(suite) { - while (suite) { - if (suite.isFocused) { - return suite.id; - } - suite = suite.parentSuite; - } - - return null; - } - - function unfocusAncestor() { - var focusedAncestor = findFocusedAncestor(currentDeclarationSuite); - if (focusedAncestor) { - for (var i = 0; i < focusedRunnables.length; i++) { - if (focusedRunnables[i] === focusedAncestor) { - focusedRunnables.splice(i, 1); - break; - } - } - } - } - - var specFactory = function(description, fn, suite, timeout) { - totalSpecsDefined++; - var spec = new j$.Spec({ - id: getNextSpecId(), - beforeAndAfterFns: beforeAndAfterFns(suite), - expectationFactory: expectationFactory, - asyncExpectationFactory: asyncExpectationFactory, - resultCallback: specResultCallback, - getSpecName: function(spec) { - return getSpecName(spec, suite); - }, - onStart: specStarted, - description: description, - expectationResultFactory: expectationResultFactory, - queueRunnerFactory: queueRunnerFactory, - userContext: function() { - return suite.clonedSharedUserContext(); - }, - queueableFn: { - fn: fn, - timeout: timeout || 0 - }, - throwOnExpectationFailure: config.oneFailurePerSpec, - timer: new j$.Timer() - }); - return spec; - - function specResultCallback(result, next) { - clearResourcesForRunnable(spec.id); - currentSpec = null; - - if (result.status === 'failed') { - hasFailures = true; - } - - reporter.specDone(result, next); - } - - function specStarted(spec, next) { - currentSpec = spec; - defaultResourcesForRunnable(spec.id, suite.id); - reporter.specStarted(spec.result, next); - } - }; - - this.it = function(description, fn, timeout) { - ensureIsNotNested('it'); - // it() sometimes doesn't have a fn argument, so only check the type if - // it's given. - if (arguments.length > 1 && typeof fn !== 'undefined') { - ensureIsFunctionOrAsync(fn, 'it'); - } - var spec = specFactory(description, fn, currentDeclarationSuite, timeout); - if (currentDeclarationSuite.markedPending) { - spec.pend(); - } - currentDeclarationSuite.addChild(spec); - return spec; - }; - - this.xit = function(description, fn, timeout) { - ensureIsNotNested('xit'); - // xit(), like it(), doesn't always have a fn argument, so only check the - // type when needed. - if (arguments.length > 1 && typeof fn !== 'undefined') { - ensureIsFunctionOrAsync(fn, 'xit'); - } - var spec = this.it.apply(this, arguments); - spec.pend('Temporarily disabled with xit'); - return spec; - }; - - this.fit = function(description, fn, timeout) { - ensureIsNotNested('fit'); - ensureIsFunctionOrAsync(fn, 'fit'); - var spec = specFactory(description, fn, currentDeclarationSuite, timeout); - currentDeclarationSuite.addChild(spec); - focusedRunnables.push(spec.id); - unfocusAncestor(); - return spec; - }; - - this.expect = function(actual) { - if (!currentRunnable()) { - throw new Error( - "'expect' was used when there was no current spec, this could be because an asynchronous test timed out" - ); - } - - return currentRunnable().expect(actual); - }; - - this.expectAsync = function(actual) { - if (!currentRunnable()) { - throw new Error( - "'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out" - ); - } - - return currentRunnable().expectAsync(actual); - }; - - this.beforeEach = function(beforeEachFunction, timeout) { - ensureIsNotNested('beforeEach'); - ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); - currentDeclarationSuite.beforeEach({ - fn: beforeEachFunction, - timeout: timeout || 0 - }); - }; - - this.beforeAll = function(beforeAllFunction, timeout) { - ensureIsNotNested('beforeAll'); - ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); - currentDeclarationSuite.beforeAll({ - fn: beforeAllFunction, - timeout: timeout || 0 - }); - }; - - this.afterEach = function(afterEachFunction, timeout) { - ensureIsNotNested('afterEach'); - ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); - afterEachFunction.isCleanup = true; - currentDeclarationSuite.afterEach({ - fn: afterEachFunction, - timeout: timeout || 0 - }); - }; - - this.afterAll = function(afterAllFunction, timeout) { - ensureIsNotNested('afterAll'); - ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); - currentDeclarationSuite.afterAll({ - fn: afterAllFunction, - timeout: timeout || 0 - }); - }; - - this.pending = function(message) { - var fullMessage = j$.Spec.pendingSpecExceptionMessage; - if (message) { - fullMessage += message; - } - throw fullMessage; - }; - - this.fail = function(error) { - if (!currentRunnable()) { - throw new Error( - "'fail' was used when there was no current spec, this could be because an asynchronous test timed out" - ); - } - - var message = 'Failed'; - if (error) { - message += ': '; - if (error.message) { - message += error.message; - } else if (j$.isString_(error)) { - message += error; - } else { - // pretty print all kind of objects. This includes arrays. - message += j$.pp(error); - } - } - - currentRunnable().addExpectationResult(false, { - matcherName: '', - passed: false, - expected: '', - actual: '', - message: message, - error: error && error.message ? error : null - }); - - if (config.oneFailurePerSpec) { - throw new Error(message); - } - }; - } - - return Env; -}; - -getJasmineRequireObj().JsApiReporter = function(j$) { - /** - * @name jsApiReporter - * @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object. - * @class - * @hideconstructor - */ - function JsApiReporter(options) { - var timer = options.timer || j$.noopTimer, - status = 'loaded'; - - this.started = false; - this.finished = false; - this.runDetails = {}; - - this.jasmineStarted = function() { - this.started = true; - status = 'started'; - timer.start(); - }; - - var executionTime; - - this.jasmineDone = function(runDetails) { - this.finished = true; - this.runDetails = runDetails; - executionTime = timer.elapsed(); - status = 'done'; - }; - - /** - * Get the current status for the Jasmine environment. - * @name jsApiReporter#status - * @since 2.0.0 - * @function - * @return {String} - One of `loaded`, `started`, or `done` - */ - this.status = function() { - return status; - }; - - var suites = [], - suites_hash = {}; - - this.suiteStarted = function(result) { - suites_hash[result.id] = result; - }; - - this.suiteDone = function(result) { - storeSuite(result); - }; - - /** - * Get the results for a set of suites. - * - * Retrievable in slices for easier serialization. - * @name jsApiReporter#suiteResults - * @since 2.1.0 - * @function - * @param {Number} index - The position in the suites list to start from. - * @param {Number} length - Maximum number of suite results to return. - * @return {SuiteResult[]} - */ - this.suiteResults = function(index, length) { - return suites.slice(index, index + length); - }; - - function storeSuite(result) { - suites.push(result); - suites_hash[result.id] = result; - } - - /** - * Get all of the suites in a single object, with their `id` as the key. - * @name jsApiReporter#suites - * @since 2.0.0 - * @function - * @return {Object} - Map of suite id to {@link SuiteResult} - */ - this.suites = function() { - return suites_hash; - }; - - var specs = []; - - this.specDone = function(result) { - specs.push(result); - }; - - /** - * Get the results for a set of specs. - * - * Retrievable in slices for easier serialization. - * @name jsApiReporter#specResults - * @since 2.0.0 - * @function - * @param {Number} index - The position in the specs list to start from. - * @param {Number} length - Maximum number of specs results to return. - * @return {SpecResult[]} - */ - this.specResults = function(index, length) { - return specs.slice(index, index + length); - }; - - /** - * Get all spec results. - * @name jsApiReporter#specs - * @since 2.0.0 - * @function - * @return {SpecResult[]} - */ - this.specs = function() { - return specs; - }; - - /** - * Get the number of milliseconds it took for the full Jasmine suite to run. - * @name jsApiReporter#executionTime - * @since 2.0.0 - * @function - * @return {Number} - */ - this.executionTime = function() { - return executionTime; - }; - } - - return JsApiReporter; -}; - -getJasmineRequireObj().Any = function(j$) { - - function Any(expectedObject) { - if (typeof expectedObject === 'undefined') { - throw new TypeError( - 'jasmine.any() expects to be passed a constructor function. ' + - 'Please pass one or use jasmine.anything() to match any object.' - ); - } - this.expectedObject = expectedObject; - } - - Any.prototype.asymmetricMatch = function(other) { - if (this.expectedObject == String) { - return typeof other == 'string' || other instanceof String; - } - - if (this.expectedObject == Number) { - return typeof other == 'number' || other instanceof Number; - } - - if (this.expectedObject == Function) { - return typeof other == 'function' || other instanceof Function; - } - - if (this.expectedObject == Object) { - return other !== null && typeof other == 'object'; - } - - if (this.expectedObject == Boolean) { - return typeof other == 'boolean'; - } - - /* jshint -W122 */ - /* global Symbol */ - if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) { - return typeof other == 'symbol'; - } - /* jshint +W122 */ - - return other instanceof this.expectedObject; - }; - - Any.prototype.jasmineToString = function() { - return ''; - }; - - return Any; -}; - -getJasmineRequireObj().Anything = function(j$) { - - function Anything() {} - - Anything.prototype.asymmetricMatch = function(other) { - return !j$.util.isUndefined(other) && other !== null; - }; - - Anything.prototype.jasmineToString = function() { - return ''; - }; - - return Anything; -}; - -getJasmineRequireObj().ArrayContaining = function(j$) { - function ArrayContaining(sample) { - this.sample = sample; - } - - ArrayContaining.prototype.asymmetricMatch = function(other, customTesters) { - if (!j$.isArray_(this.sample)) { - throw new Error('You must provide an array to arrayContaining, not ' + j$.pp(this.sample) + '.'); - } - - // If the actual parameter is not an array, we can fail immediately, since it couldn't - // possibly be an "array containing" anything. However, we also want an empty sample - // array to match anything, so we need to double-check we aren't in that case - if (!j$.isArray_(other) && this.sample.length > 0) { - return false; - } - - for (var i = 0; i < this.sample.length; i++) { - var item = this.sample[i]; - if (!j$.matchersUtil.contains(other, item, customTesters)) { - return false; - } - } - - return true; - }; - - ArrayContaining.prototype.jasmineToString = function () { - return ''; - }; - - return ArrayContaining; -}; - -getJasmineRequireObj().ArrayWithExactContents = function(j$) { - - function ArrayWithExactContents(sample) { - this.sample = sample; - } - - ArrayWithExactContents.prototype.asymmetricMatch = function(other, customTesters) { - if (!j$.isArray_(this.sample)) { - throw new Error('You must provide an array to arrayWithExactContents, not ' + j$.pp(this.sample) + '.'); - } - - if (this.sample.length !== other.length) { - return false; - } - - for (var i = 0; i < this.sample.length; i++) { - var item = this.sample[i]; - if (!j$.matchersUtil.contains(other, item, customTesters)) { - return false; - } - } - - return true; - }; - - ArrayWithExactContents.prototype.jasmineToString = function() { - return ''; - }; - - return ArrayWithExactContents; -}; - -getJasmineRequireObj().Empty = function (j$) { - - function Empty() {} - - Empty.prototype.asymmetricMatch = function (other) { - if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { - return other.length === 0; - } - - if (j$.isMap(other) || j$.isSet(other)) { - return other.size === 0; - } - - if (j$.isObject_(other)) { - return Object.keys(other).length === 0; - } - return false; - }; - - Empty.prototype.jasmineToString = function () { - return ''; - }; - - return Empty; -}; - -getJasmineRequireObj().Falsy = function(j$) { - - function Falsy() {} - - Falsy.prototype.asymmetricMatch = function(other) { - return !other; - }; - - Falsy.prototype.jasmineToString = function() { - return ''; - }; - - return Falsy; -}; - -getJasmineRequireObj().MapContaining = function(j$) { - function MapContaining(sample) { - if (!j$.isMap(sample)) { - throw new Error('You must provide a map to `mapContaining`, not ' + j$.pp(sample)); - } - - this.sample = sample; - } - - MapContaining.prototype.asymmetricMatch = function(other, customTesters) { - if (!j$.isMap(other)) return false; - - var hasAllMatches = true; - j$.util.forEachBreakable(this.sample, function(breakLoop, value, key) { - // for each key/value pair in `sample` - // there should be at least one pair in `other` whose key and value both match - var hasMatch = false; - j$.util.forEachBreakable(other, function(oBreakLoop, oValue, oKey) { - if ( - j$.matchersUtil.equals(oKey, key, customTesters) - && j$.matchersUtil.equals(oValue, value, customTesters) - ) { - hasMatch = true; - oBreakLoop(); - } - }); - if (!hasMatch) { - hasAllMatches = false; - breakLoop(); - } - }); - - return hasAllMatches; - }; - - MapContaining.prototype.jasmineToString = function() { - return ''; - }; - - return MapContaining; -}; - -getJasmineRequireObj().NotEmpty = function (j$) { - - function NotEmpty() {} - - NotEmpty.prototype.asymmetricMatch = function (other) { - if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { - return other.length !== 0; - } - - if (j$.isMap(other) || j$.isSet(other)) { - return other.size !== 0; - } - - if (j$.isObject_(other)) { - return Object.keys(other).length !== 0; - } - - return false; - }; - - NotEmpty.prototype.jasmineToString = function () { - return ''; - }; - - return NotEmpty; -}; - -getJasmineRequireObj().ObjectContaining = function(j$) { - - function ObjectContaining(sample) { - this.sample = sample; - } - - function getPrototype(obj) { - if (Object.getPrototypeOf) { - return Object.getPrototypeOf(obj); - } - - if (obj.constructor.prototype == obj) { - return null; - } - - return obj.constructor.prototype; - } - - function hasProperty(obj, property) { - if (!obj) { - return false; - } - - if (Object.prototype.hasOwnProperty.call(obj, property)) { - return true; - } - - return hasProperty(getPrototype(obj), property); - } - - ObjectContaining.prototype.asymmetricMatch = function(other, customTesters) { - if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } - - for (var property in this.sample) { - if (!hasProperty(other, property) || - !j$.matchersUtil.equals(this.sample[property], other[property], customTesters)) { - return false; - } - } - - return true; - }; - - ObjectContaining.prototype.jasmineToString = function() { - return ''; - }; - - return ObjectContaining; -}; - -getJasmineRequireObj().SetContaining = function(j$) { - function SetContaining(sample) { - if (!j$.isSet(sample)) { - throw new Error('You must provide a set to `setContaining`, not ' + j$.pp(sample)); - } - - this.sample = sample; - } - - SetContaining.prototype.asymmetricMatch = function(other, customTesters) { - if (!j$.isSet(other)) return false; - - var hasAllMatches = true; - j$.util.forEachBreakable(this.sample, function(breakLoop, item) { - // for each item in `sample` there should be at least one matching item in `other` - // (not using `j$.matchersUtil.contains` because it compares set members by reference, - // not by deep value equality) - var hasMatch = false; - j$.util.forEachBreakable(other, function(oBreakLoop, oItem) { - if (j$.matchersUtil.equals(oItem, item, customTesters)) { - hasMatch = true; - oBreakLoop(); - } - }); - if (!hasMatch) { - hasAllMatches = false; - breakLoop(); - } - }); - - return hasAllMatches; - }; - - SetContaining.prototype.jasmineToString = function() { - return ''; - }; - - return SetContaining; -}; - -getJasmineRequireObj().StringMatching = function(j$) { - - function StringMatching(expected) { - if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { - throw new Error('Expected is not a String or a RegExp'); - } - - this.regexp = new RegExp(expected); - } - - StringMatching.prototype.asymmetricMatch = function(other) { - return this.regexp.test(other); - }; - - StringMatching.prototype.jasmineToString = function() { - return ''; - }; - - return StringMatching; -}; - -getJasmineRequireObj().Truthy = function(j$) { - - function Truthy() {} - - Truthy.prototype.asymmetricMatch = function(other) { - return !!other; - }; - - Truthy.prototype.jasmineToString = function() { - return ''; - }; - - return Truthy; -}; - -getJasmineRequireObj().CallTracker = function(j$) { - /** - * @namespace Spy#calls - * @since 2.0.0 - */ - function CallTracker() { - var calls = []; - var opts = {}; - - this.track = function(context) { - if (opts.cloneArgs) { - context.args = j$.util.cloneArgs(context.args); - } - calls.push(context); - }; - - /** - * Check whether this spy has been invoked. - * @name Spy#calls#any - * @since 2.0.0 - * @function - * @return {Boolean} - */ - this.any = function() { - return !!calls.length; - }; - - /** - * Get the number of invocations of this spy. - * @name Spy#calls#count - * @since 2.0.0 - * @function - * @return {Integer} - */ - this.count = function() { - return calls.length; - }; - - /** - * Get the arguments that were passed to a specific invocation of this spy. - * @name Spy#calls#argsFor - * @since 2.0.0 - * @function - * @param {Integer} index The 0-based invocation index. - * @return {Array} - */ - this.argsFor = function(index) { - var call = calls[index]; - return call ? call.args : []; - }; - - /** - * Get the raw calls array for this spy. - * @name Spy#calls#all - * @since 2.0.0 - * @function - * @return {Spy.callData[]} - */ - this.all = function() { - return calls; - }; - - /** - * Get all of the arguments for each invocation of this spy in the order they were received. - * @name Spy#calls#allArgs - * @since 2.0.0 - * @function - * @return {Array} - */ - this.allArgs = function() { - var callArgs = []; - for (var i = 0; i < calls.length; i++) { - callArgs.push(calls[i].args); - } - - return callArgs; - }; - - /** - * Get the first invocation of this spy. - * @name Spy#calls#first - * @since 2.0.0 - * @function - * @return {ObjecSpy.callData} - */ - this.first = function() { - return calls[0]; - }; - - /** - * Get the most recent invocation of this spy. - * @name Spy#calls#mostRecent - * @since 2.0.0 - * @function - * @return {ObjecSpy.callData} - */ - this.mostRecent = function() { - return calls[calls.length - 1]; - }; - - /** - * Reset this spy as if it has never been called. - * @name Spy#calls#reset - * @since 2.0.0 - * @function - */ - this.reset = function() { - calls = []; - }; - - /** - * Set this spy to do a shallow clone of arguments passed to each invocation. - * @name Spy#calls#saveArgumentsByValue - * @since 2.5.0 - * @function - */ - this.saveArgumentsByValue = function() { - opts.cloneArgs = true; - }; - } - - return CallTracker; -}; - -getJasmineRequireObj().clearStack = function(j$) { - var maxInlineCallCount = 10; - - function messageChannelImpl(global, setTimeout) { - var channel = new global.MessageChannel(), - head = {}, - tail = head; - - var taskRunning = false; - channel.port1.onmessage = function() { - head = head.next; - var task = head.task; - delete head.task; - - if (taskRunning) { - global.setTimeout(task, 0); - } else { - try { - taskRunning = true; - task(); - } finally { - taskRunning = false; - } - } - }; - - var currentCallCount = 0; - return function clearStack(fn) { - currentCallCount++; - - if (currentCallCount < maxInlineCallCount) { - tail = tail.next = { task: fn }; - channel.port2.postMessage(0); - } else { - currentCallCount = 0; - setTimeout(fn); - } - }; - } - - function getClearStack(global) { - var currentCallCount = 0; - var realSetTimeout = global.setTimeout; - var setTimeoutImpl = function clearStack(fn) { - Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]); - }; - - if (j$.isFunction_(global.setImmediate)) { - var realSetImmediate = global.setImmediate; - return function(fn) { - currentCallCount++; - - if (currentCallCount < maxInlineCallCount) { - realSetImmediate(fn); - } else { - currentCallCount = 0; - - setTimeoutImpl(fn); - } - }; - } else if (!j$.util.isUndefined(global.MessageChannel)) { - return messageChannelImpl(global, setTimeoutImpl); - } else { - return setTimeoutImpl; - } - } - - return getClearStack; -}; - -getJasmineRequireObj().Clock = function() { - /* global process */ - var NODE_JS = - typeof process !== 'undefined' && - process.versions && - typeof process.versions.node === 'string'; - - /** - * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}. - * @class Clock - * @classdesc Jasmine's mock clock is used when testing time dependent code. - */ - function Clock(global, delayedFunctionSchedulerFactory, mockDate) { - var self = this, - realTimingFunctions = { - setTimeout: global.setTimeout, - clearTimeout: global.clearTimeout, - setInterval: global.setInterval, - clearInterval: global.clearInterval - }, - fakeTimingFunctions = { - setTimeout: setTimeout, - clearTimeout: clearTimeout, - setInterval: setInterval, - clearInterval: clearInterval - }, - installed = false, - delayedFunctionScheduler, - timer; - - self.FakeTimeout = FakeTimeout; - - /** - * Install the mock clock over the built-in methods. - * @name Clock#install - * @since 2.0.0 - * @function - * @return {Clock} - */ - self.install = function() { - if (!originalTimingFunctionsIntact()) { - throw new Error( - 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?' - ); - } - replace(global, fakeTimingFunctions); - timer = fakeTimingFunctions; - delayedFunctionScheduler = delayedFunctionSchedulerFactory(); - installed = true; - - return self; - }; - - /** - * Uninstall the mock clock, returning the built-in methods to their places. - * @name Clock#uninstall - * @since 2.0.0 - * @function - */ - self.uninstall = function() { - delayedFunctionScheduler = null; - mockDate.uninstall(); - replace(global, realTimingFunctions); - - timer = realTimingFunctions; - installed = false; - }; - - /** - * Execute a function with a mocked Clock - * - * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes. - * @name Clock#withMock - * @since 2.3.0 - * @function - * @param {Function} closure The function to be called. - */ - self.withMock = function(closure) { - this.install(); - try { - closure(); - } finally { - this.uninstall(); - } - }; - - /** - * Instruct the installed Clock to also mock the date returned by `new Date()` - * @name Clock#mockDate - * @since 2.1.0 - * @function - * @param {Date} [initialDate=now] The `Date` to provide. - */ - self.mockDate = function(initialDate) { - mockDate.install(initialDate); - }; - - self.setTimeout = function(fn, delay, params) { - return Function.prototype.apply.apply(timer.setTimeout, [ - global, - arguments - ]); - }; - - self.setInterval = function(fn, delay, params) { - return Function.prototype.apply.apply(timer.setInterval, [ - global, - arguments - ]); - }; - - self.clearTimeout = function(id) { - return Function.prototype.call.apply(timer.clearTimeout, [global, id]); - }; - - self.clearInterval = function(id) { - return Function.prototype.call.apply(timer.clearInterval, [global, id]); - }; - - /** - * Tick the Clock forward, running any enqueued timeouts along the way - * @name Clock#tick - * @since 1.3.0 - * @function - * @param {int} millis The number of milliseconds to tick. - */ - self.tick = function(millis) { - if (installed) { - delayedFunctionScheduler.tick(millis, function(millis) { - mockDate.tick(millis); - }); - } else { - throw new Error( - 'Mock clock is not installed, use jasmine.clock().install()' - ); - } - }; - - return self; - - function originalTimingFunctionsIntact() { - return ( - global.setTimeout === realTimingFunctions.setTimeout && - global.clearTimeout === realTimingFunctions.clearTimeout && - global.setInterval === realTimingFunctions.setInterval && - global.clearInterval === realTimingFunctions.clearInterval - ); - } - - function replace(dest, source) { - for (var prop in source) { - dest[prop] = source[prop]; - } - } - - function setTimeout(fn, delay) { - if (!NODE_JS) { - return delayedFunctionScheduler.scheduleFunction( - fn, - delay, - argSlice(arguments, 2) - ); - } - - var timeout = new FakeTimeout(); - - delayedFunctionScheduler.scheduleFunction( - fn, - delay, - argSlice(arguments, 2), - false, - timeout - ); - - return timeout; - } - - function clearTimeout(id) { - return delayedFunctionScheduler.removeFunctionWithId(id); - } - - function setInterval(fn, interval) { - if (!NODE_JS) { - return delayedFunctionScheduler.scheduleFunction( - fn, - interval, - argSlice(arguments, 2), - true - ); - } - - var timeout = new FakeTimeout(); - - delayedFunctionScheduler.scheduleFunction( - fn, - interval, - argSlice(arguments, 2), - true, - timeout - ); - - return timeout; - } - - function clearInterval(id) { - return delayedFunctionScheduler.removeFunctionWithId(id); - } - - function argSlice(argsObj, n) { - return Array.prototype.slice.call(argsObj, n); - } - } - - /** - * Mocks Node.js Timeout class - */ - function FakeTimeout() {} - - FakeTimeout.prototype.ref = function() { - return this; - }; - - FakeTimeout.prototype.unref = function() { - return this; - }; - - return Clock; -}; - -getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { - function DelayedFunctionScheduler() { - var self = this; - var scheduledLookup = []; - var scheduledFunctions = {}; - var currentTime = 0; - var delayedFnCount = 0; - var deletedKeys = []; - - self.tick = function(millis, tickDate) { - millis = millis || 0; - var endTime = currentTime + millis; - - runScheduledFunctions(endTime, tickDate); - currentTime = endTime; - }; - - self.scheduleFunction = function( - funcToCall, - millis, - params, - recurring, - timeoutKey, - runAtMillis - ) { - var f; - if (typeof funcToCall === 'string') { - /* jshint evil: true */ - f = function() { - return eval(funcToCall); - }; - /* jshint evil: false */ - } else { - f = funcToCall; - } - - millis = millis || 0; - timeoutKey = timeoutKey || ++delayedFnCount; - runAtMillis = runAtMillis || currentTime + millis; - - var funcToSchedule = { - runAtMillis: runAtMillis, - funcToCall: f, - recurring: recurring, - params: params, - timeoutKey: timeoutKey, - millis: millis - }; - - if (runAtMillis in scheduledFunctions) { - scheduledFunctions[runAtMillis].push(funcToSchedule); - } else { - scheduledFunctions[runAtMillis] = [funcToSchedule]; - scheduledLookup.push(runAtMillis); - scheduledLookup.sort(function(a, b) { - return a - b; - }); - } - - return timeoutKey; - }; - - self.removeFunctionWithId = function(timeoutKey) { - deletedKeys.push(timeoutKey); - - for (var runAtMillis in scheduledFunctions) { - var funcs = scheduledFunctions[runAtMillis]; - var i = indexOfFirstToPass(funcs, function(func) { - return func.timeoutKey === timeoutKey; - }); - - if (i > -1) { - if (funcs.length === 1) { - delete scheduledFunctions[runAtMillis]; - deleteFromLookup(runAtMillis); - } else { - funcs.splice(i, 1); - } - - // intervals get rescheduled when executed, so there's never more - // than a single scheduled function with a given timeoutKey - break; - } - } - }; - - return self; - - function indexOfFirstToPass(array, testFn) { - var index = -1; - - for (var i = 0; i < array.length; ++i) { - if (testFn(array[i])) { - index = i; - break; - } - } - - return index; - } - - function deleteFromLookup(key) { - var value = Number(key); - var i = indexOfFirstToPass(scheduledLookup, function(millis) { - return millis === value; - }); - - if (i > -1) { - scheduledLookup.splice(i, 1); - } - } - - function reschedule(scheduledFn) { - self.scheduleFunction( - scheduledFn.funcToCall, - scheduledFn.millis, - scheduledFn.params, - true, - scheduledFn.timeoutKey, - scheduledFn.runAtMillis + scheduledFn.millis - ); - } - - function forEachFunction(funcsToRun, callback) { - for (var i = 0; i < funcsToRun.length; ++i) { - callback(funcsToRun[i]); - } - } - - function runScheduledFunctions(endTime, tickDate) { - tickDate = tickDate || function() {}; - if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { - tickDate(endTime - currentTime); - return; - } - - do { - deletedKeys = []; - var newCurrentTime = scheduledLookup.shift(); - tickDate(newCurrentTime - currentTime); - - currentTime = newCurrentTime; - - var funcsToRun = scheduledFunctions[currentTime]; - - delete scheduledFunctions[currentTime]; - - forEachFunction(funcsToRun, function(funcToRun) { - if (funcToRun.recurring) { - reschedule(funcToRun); - } - }); - - forEachFunction(funcsToRun, function(funcToRun) { - if (j$.util.arrayContains(deletedKeys, funcToRun.timeoutKey)) { - // skip a timeoutKey deleted whilst we were running - return; - } - funcToRun.funcToCall.apply(null, funcToRun.params || []); - }); - deletedKeys = []; - } while ( - scheduledLookup.length > 0 && - // checking first if we're out of time prevents setTimeout(0) - // scheduled in a funcToRun from forcing an extra iteration - currentTime !== endTime && - scheduledLookup[0] <= endTime - ); - - // ran out of functions to call, but still time left on the clock - if (currentTime !== endTime) { - tickDate(endTime - currentTime); - } - } - } - - return DelayedFunctionScheduler; -}; - -getJasmineRequireObj().errors = function() { - function ExpectationFailed() {} - - ExpectationFailed.prototype = new Error(); - ExpectationFailed.prototype.constructor = ExpectationFailed; - - return { - ExpectationFailed: ExpectationFailed - }; -}; - -getJasmineRequireObj().ExceptionFormatter = function(j$) { - var ignoredProperties = [ - 'name', - 'message', - 'stack', - 'fileName', - 'sourceURL', - 'line', - 'lineNumber', - 'column', - 'description', - 'jasmineMessage' - ]; - - function ExceptionFormatter(options) { - var jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile(); - this.message = function(error) { - var message = ''; - - if (error.jasmineMessage) { - message += error.jasmineMessage; - } else if (error.name && error.message) { - message += error.name + ': ' + error.message; - } else if (error.message) { - message += error.message; - } else { - message += error.toString() + ' thrown'; - } - - if (error.fileName || error.sourceURL) { - message += ' in ' + (error.fileName || error.sourceURL); - } - - if (error.line || error.lineNumber) { - message += ' (line ' + (error.line || error.lineNumber) + ')'; - } - - return message; - }; - - this.stack = function(error) { - if (!error || !error.stack) { - return null; - } - - var stackTrace = new j$.StackTrace(error); - var lines = filterJasmine(stackTrace); - var result = ''; - - if (stackTrace.message) { - lines.unshift(stackTrace.message); - } - - result += formatProperties(error); - result += lines.join('\n'); - - return result; - }; - - function filterJasmine(stackTrace) { - var result = [], - jasmineMarker = - stackTrace.style === 'webkit' ? '' : ' at '; - - stackTrace.frames.forEach(function(frame) { - if (frame.file && frame.file !== jasmineFile) { - result.push(frame.raw); - } else if (result[result.length - 1] !== jasmineMarker) { - result.push(jasmineMarker); - } - }); - - return result; - } - - function formatProperties(error) { - if (!(error instanceof Object)) { - return; - } - - var result = {}; - var empty = true; - - for (var prop in error) { - if (j$.util.arrayContains(ignoredProperties, prop)) { - continue; - } - result[prop] = error[prop]; - empty = false; - } - - if (!empty) { - return 'error properties: ' + j$.pp(result) + '\n'; - } - - return ''; - } - } - - return ExceptionFormatter; -}; - -getJasmineRequireObj().Expectation = function(j$) { - /** - * Matchers that come with Jasmine out of the box. - * @namespace matchers - */ - function Expectation(options) { - this.expector = new j$.Expector(options); - - var customMatchers = options.customMatchers || {}; - for (var matcherName in customMatchers) { - this[matcherName] = wrapSyncCompare( - matcherName, - customMatchers[matcherName] - ); - } - } - - /** - * Add some context for an {@link expect} - * @function - * @name matchers#withContext - * @since 3.3.0 - * @param {String} message - Additional context to show when the matcher fails - * @return {matchers} - */ - Expectation.prototype.withContext = function withContext(message) { - return addFilter(this, new ContextAddingFilter(message)); - }; - - /** - * Invert the matcher following this {@link expect} - * @member - * @name matchers#not - * @since 1.3.0 - * @type {matchers} - * @example - * expect(something).not.toBe(true); - */ - Object.defineProperty(Expectation.prototype, 'not', { - get: function() { - return addFilter(this, syncNegatingFilter); - } - }); - - /** - * Asynchronous matchers. - * @namespace async-matchers - */ - function AsyncExpectation(options) { - var global = options.global || j$.getGlobal(); - this.expector = new j$.Expector(options); - - if (!global.Promise) { - throw new Error( - 'expectAsync is unavailable because the environment does not support promises.' - ); - } - - var customAsyncMatchers = options.customAsyncMatchers || {}; - for (var matcherName in customAsyncMatchers) { - this[matcherName] = wrapAsyncCompare( - matcherName, - customAsyncMatchers[matcherName] - ); - } - } - - /** - * Add some context for an {@link expectAsync} - * @function - * @name async-matchers#withContext - * @since 3.3.0 - * @param {String} message - Additional context to show when the async matcher fails - * @return {async-matchers} - */ - AsyncExpectation.prototype.withContext = function withContext(message) { - return addFilter(this, new ContextAddingFilter(message)); - }; - - /** - * Invert the matcher following this {@link expectAsync} - * @member - * @name async-matchers#not - * @type {async-matchers} - * @example - * await expectAsync(myPromise).not.toBeResolved(); - * @example - * return expectAsync(myPromise).not.toBeResolved(); - */ - Object.defineProperty(AsyncExpectation.prototype, 'not', { - get: function() { - return addFilter(this, asyncNegatingFilter); - } - }); - - function wrapSyncCompare(name, matcherFactory) { - return function() { - var result = this.expector.compare(name, matcherFactory, arguments); - this.expector.processResult(result); - }; - } - - function wrapAsyncCompare(name, matcherFactory) { - return function() { - var self = this; - - // Capture the call stack here, before we go async, so that it will contain - // frames that are relevant to the user instead of just parts of Jasmine. - var errorForStack = j$.util.errorWithStack(); - - return this.expector - .compare(name, matcherFactory, arguments) - .then(function(result) { - self.expector.processResult(result, errorForStack); - }); - }; - } - - function addCoreMatchers(prototype, matchers, wrapper) { - for (var matcherName in matchers) { - var matcher = matchers[matcherName]; - prototype[matcherName] = wrapper(matcherName, matcher); - } - } - - function addFilter(source, filter) { - var result = Object.create(source); - result.expector = source.expector.addFilter(filter); - return result; - } - - function negatedFailureMessage(result, matcherName, args, util) { - if (result.message) { - if (j$.isFunction_(result.message)) { - return result.message(); - } else { - return result.message; - } - } - - args = args.slice(); - args.unshift(true); - args.unshift(matcherName); - return util.buildFailureMessage.apply(null, args); - } - - function negate(result) { - result.pass = !result.pass; - return result; - } - - var syncNegatingFilter = { - selectComparisonFunc: function(matcher) { - function defaultNegativeCompare() { - return negate(matcher.compare.apply(null, arguments)); - } - - return matcher.negativeCompare || defaultNegativeCompare; - }, - buildFailureMessage: negatedFailureMessage - }; - - var asyncNegatingFilter = { - selectComparisonFunc: function(matcher) { - function defaultNegativeCompare() { - return matcher.compare.apply(this, arguments).then(negate); - } - - return matcher.negativeCompare || defaultNegativeCompare; - }, - buildFailureMessage: negatedFailureMessage - }; - - function ContextAddingFilter(message) { - this.message = message; - } - - ContextAddingFilter.prototype.modifyFailureMessage = function(msg) { - return this.message + ': ' + msg; - }; - - return { - factory: function(options) { - return new Expectation(options || {}); - }, - addCoreMatchers: function(matchers) { - addCoreMatchers(Expectation.prototype, matchers, wrapSyncCompare); - }, - asyncFactory: function(options) { - return new AsyncExpectation(options || {}); - }, - addAsyncCoreMatchers: function(matchers) { - addCoreMatchers(AsyncExpectation.prototype, matchers, wrapAsyncCompare); - } - }; -}; - -getJasmineRequireObj().ExpectationFilterChain = function() { - function ExpectationFilterChain(maybeFilter, prev) { - this.filter_ = maybeFilter; - this.prev_ = prev; - } - - ExpectationFilterChain.prototype.addFilter = function(filter) { - return new ExpectationFilterChain(filter, this); - }; - - ExpectationFilterChain.prototype.selectComparisonFunc = function(matcher) { - return this.callFirst_('selectComparisonFunc', arguments).result; - }; - - ExpectationFilterChain.prototype.buildFailureMessage = function( - result, - matcherName, - args, - util - ) { - return this.callFirst_('buildFailureMessage', arguments).result; - }; - - ExpectationFilterChain.prototype.modifyFailureMessage = function(msg) { - var result = this.callFirst_('modifyFailureMessage', arguments).result; - return result || msg; - }; - - ExpectationFilterChain.prototype.callFirst_ = function(fname, args) { - var prevResult; - - if (this.prev_) { - prevResult = this.prev_.callFirst_(fname, args); - - if (prevResult.found) { - return prevResult; - } - } - - if (this.filter_ && this.filter_[fname]) { - return { - found: true, - result: this.filter_[fname].apply(this.filter_, args) - }; - } - - return { found: false }; - }; - - return ExpectationFilterChain; -}; - -//TODO: expectation result may make more sense as a presentation of an expectation. -getJasmineRequireObj().buildExpectationResult = function() { - function buildExpectationResult(options) { - var messageFormatter = options.messageFormatter || function() {}, - stackFormatter = options.stackFormatter || function() {}; - - /** - * @typedef Expectation - * @property {String} matcherName - The name of the matcher that was executed for this expectation. - * @property {String} message - The failure message for the expectation. - * @property {String} stack - The stack trace for the failure if available. - * @property {Boolean} passed - Whether the expectation passed or failed. - * @property {Object} expected - If the expectation failed, what was the expected value. - * @property {Object} actual - If the expectation failed, what actual value was produced. - */ - var result = { - matcherName: options.matcherName, - message: message(), - stack: stack(), - passed: options.passed - }; - - if (!result.passed) { - result.expected = options.expected; - result.actual = options.actual; - } - - return result; - - function message() { - if (options.passed) { - return 'Passed.'; - } else if (options.message) { - return options.message; - } else if (options.error) { - return messageFormatter(options.error); - } - return ''; - } - - function stack() { - if (options.passed) { - return ''; - } - - var error = options.error; - if (!error) { - if (options.errorForStack) { - error = options.errorForStack; - } else if (options.stack) { - error = options; - } else { - try { - throw new Error(message()); - } catch (e) { - error = e; - } - } - } - return stackFormatter(error); - } - } - - return buildExpectationResult; -}; - -getJasmineRequireObj().Expector = function(j$) { - function Expector(options) { - this.util = options.util || { buildFailureMessage: function() {} }; - this.customEqualityTesters = options.customEqualityTesters || []; - this.actual = options.actual; - this.addExpectationResult = options.addExpectationResult || function() {}; - this.filters = new j$.ExpectationFilterChain(); - } - - Expector.prototype.instantiateMatcher = function( - matcherName, - matcherFactory, - args - ) { - this.matcherName = matcherName; - this.args = Array.prototype.slice.call(args, 0); - this.expected = this.args.slice(0); - - this.args.unshift(this.actual); - - var matcher = matcherFactory(this.util, this.customEqualityTesters); - var comparisonFunc = this.filters.selectComparisonFunc(matcher); - return comparisonFunc || matcher.compare; - }; - - Expector.prototype.buildMessage = function(result) { - var self = this; - - if (result.pass) { - return ''; - } - - var msg = this.filters.buildFailureMessage( - result, - this.matcherName, - this.args, - this.util, - defaultMessage - ); - return this.filters.modifyFailureMessage(msg || defaultMessage()); - - function defaultMessage() { - if (!result.message) { - var args = self.args.slice(); - args.unshift(false); - args.unshift(self.matcherName); - return self.util.buildFailureMessage.apply(null, args); - } else if (j$.isFunction_(result.message)) { - return result.message(); - } else { - return result.message; - } - } - }; - - Expector.prototype.compare = function(matcherName, matcherFactory, args) { - var matcherCompare = this.instantiateMatcher( - matcherName, - matcherFactory, - args - ); - return matcherCompare.apply(null, this.args); - }; - - Expector.prototype.addFilter = function(filter) { - var result = Object.create(this); - result.filters = this.filters.addFilter(filter); - return result; - }; - - Expector.prototype.processResult = function(result, errorForStack) { - var message = this.buildMessage(result); - - if (this.expected.length === 1) { - this.expected = this.expected[0]; - } - - this.addExpectationResult(result.pass, { - matcherName: this.matcherName, - passed: result.pass, - message: message, - error: errorForStack ? undefined : result.error, - errorForStack: errorForStack || undefined, - actual: this.actual, - expected: this.expected // TODO: this may need to be arrayified/sliced - }); - }; - - return Expector; -}; - -getJasmineRequireObj().formatErrorMsg = function() { - function generateErrorMsg(domain, usage) { - var usageDefinition = usage ? '\nUsage: ' + usage : ''; - - return function errorMsg(msg) { - return domain + ' : ' + msg + usageDefinition; - }; - } - - return generateErrorMsg; -}; - -getJasmineRequireObj().GlobalErrors = function(j$) { - function GlobalErrors(global) { - var handlers = []; - global = global || j$.getGlobal(); - - var onerror = function onerror() { - var handler = handlers[handlers.length - 1]; - - if (handler) { - handler.apply(null, Array.prototype.slice.call(arguments, 0)); - } else { - throw arguments[0]; - } - }; - - this.originalHandlers = {}; - this.jasmineHandlers = {}; - this.installOne_ = function installOne_(errorType, jasmineMessage) { - function taggedOnError(error) { - error.jasmineMessage = jasmineMessage + ': ' + error; - - var handler = handlers[handlers.length - 1]; - - if (handler) { - handler(error); - } else { - throw error; - } - } - - this.originalHandlers[errorType] = global.process.listeners(errorType); - this.jasmineHandlers[errorType] = taggedOnError; - - global.process.removeAllListeners(errorType); - global.process.on(errorType, taggedOnError); - - this.uninstall = function uninstall() { - var errorTypes = Object.keys(this.originalHandlers); - for (var iType = 0; iType < errorTypes.length; iType++) { - var errorType = errorTypes[iType]; - global.process.removeListener( - errorType, - this.jasmineHandlers[errorType] - ); - for (var i = 0; i < this.originalHandlers[errorType].length; i++) { - global.process.on(errorType, this.originalHandlers[errorType][i]); - } - delete this.originalHandlers[errorType]; - delete this.jasmineHandlers[errorType]; - } - }; - }; - - this.install = function install() { - if ( - global.process && - global.process.listeners && - j$.isFunction_(global.process.on) - ) { - this.installOne_('uncaughtException', 'Uncaught exception'); - this.installOne_('unhandledRejection', 'Unhandled promise rejection'); - } else { - var originalHandler = global.onerror; - global.onerror = onerror; - - this.uninstall = function uninstall() { - global.onerror = originalHandler; - }; - } - }; - - this.pushListener = function pushListener(listener) { - handlers.push(listener); - }; - - this.popListener = function popListener() { - handlers.pop(); - }; - } - - return GlobalErrors; -}; - -getJasmineRequireObj().toBeRejected = function(j$) { - /** - * Expect a promise to be rejected. - * @function - * @async - * @name async-matchers#toBeRejected - * @since 3.1.0 - * @example - * await expectAsync(aPromise).toBeRejected(); - * @example - * return expectAsync(aPromise).toBeRejected(); - */ - return function toBeRejected(util) { - return { - compare: function(actual) { - if (!j$.isPromiseLike(actual)) { - throw new Error('Expected toBeRejected to be called on a promise.'); - } - return actual.then( - function() { return {pass: false}; }, - function() { return {pass: true}; } - ); - } - }; - }; -}; - -getJasmineRequireObj().toBeRejectedWith = function(j$) { - /** - * Expect a promise to be rejected with a value equal to the expected, using deep equality comparison. - * @function - * @async - * @name async-matchers#toBeRejectedWith - * @since 3.3.0 - * @param {Object} expected - Value that the promise is expected to be rejected with - * @example - * await expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); - * @example - * return expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); - */ - return function toBeRejectedWith(util, customEqualityTesters) { - return { - compare: function(actualPromise, expectedValue) { - if (!j$.isPromiseLike(actualPromise)) { - throw new Error('Expected toBeRejectedWith to be called on a promise.'); - } - - function prefix(passed) { - return 'Expected a promise ' + - (passed ? 'not ' : '') + - 'to be rejected with ' + j$.pp(expectedValue); - } - - return actualPromise.then( - function() { - return { - pass: false, - message: prefix(false) + ' but it was resolved.' - }; - }, - function(actualValue) { - if (util.equals(actualValue, expectedValue, customEqualityTesters)) { - return { - pass: true, - message: prefix(true) + '.' - }; - } else { - return { - pass: false, - message: prefix(false) + ' but it was rejected with ' + j$.pp(actualValue) + '.' - }; - } - } - ); - } - }; - }; -}; - -getJasmineRequireObj().toBeRejectedWithError = function(j$) { - /** - * Expect a promise to be rejected with a value matched to the expected - * @function - * @async - * @name async-matchers#toBeRejectedWithError - * @since 3.5.0 - * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. - * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` - * @example - * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message'); - * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/); - * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError); - * await expectAsync(aPromise).toBeRejectedWithError('Error message'); - * return expectAsync(aPromise).toBeRejectedWithError(/Error message/); - */ - return function toBeRejectedWithError() { - return { - compare: function(actualPromise, arg1, arg2) { - if (!j$.isPromiseLike(actualPromise)) { - throw new Error('Expected toBeRejectedWithError to be called on a promise.'); - } - - var expected = getExpectedFromArgs(arg1, arg2); - - return actualPromise.then( - function() { - return { - pass: false, - message: 'Expected a promise to be rejected but it was resolved.' - }; - }, - function(actualValue) { return matchError(actualValue, expected); } - ); - } - }; - }; - - function matchError(actual, expected) { - if (!j$.isError_(actual)) { - return fail(expected, 'rejected with ' + j$.pp(actual)); - } - - if (!(actual instanceof expected.error)) { - return fail(expected, 'rejected with type ' + j$.fnNameFor(actual.constructor)); - } - - var actualMessage = actual.message; - - if (actualMessage === expected.message || typeof expected.message === 'undefined') { - return pass(expected); - } - - if (expected.message instanceof RegExp && expected.message.test(actualMessage)) { - return pass(expected); - } - - return fail(expected, 'rejected with ' + j$.pp(actual)); - } - - function pass(expected) { - return { - pass: true, - message: 'Expected a promise not to be rejected with ' + expected.printValue + ', but it was.' - }; - } - - function fail(expected, message) { - return { - pass: false, - message: 'Expected a promise to be rejected with ' + expected.printValue + ' but it was ' + message + '.' - }; - } - - - function getExpectedFromArgs(arg1, arg2) { - var error, message; - - if (isErrorConstructor(arg1)) { - error = arg1; - message = arg2; - } else { - error = Error; - message = arg1; - } - - return { - error: error, - message: message, - printValue: j$.fnNameFor(error) + (typeof message === 'undefined' ? '' : ': ' + j$.pp(message)) - }; - } - - function isErrorConstructor(value) { - return typeof value === 'function' && (value === Error || j$.isError_(value.prototype)); - } -}; - -getJasmineRequireObj().toBeResolved = function(j$) { - /** - * Expect a promise to be resolved. - * @function - * @async - * @name async-matchers#toBeResolved - * @since 3.1.0 - * @example - * await expectAsync(aPromise).toBeResolved(); - * @example - * return expectAsync(aPromise).toBeResolved(); - */ - return function toBeResolved(util) { - return { - compare: function(actual) { - if (!j$.isPromiseLike(actual)) { - throw new Error('Expected toBeResolved to be called on a promise.'); - } - - return actual.then( - function() { return {pass: true}; }, - function() { return {pass: false}; } - ); - } - }; - }; -}; - -getJasmineRequireObj().toBeResolvedTo = function(j$) { - /** - * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. - * @function - * @async - * @name async-matchers#toBeResolvedTo - * @since 3.1.0 - * @param {Object} expected - Value that the promise is expected to resolve to - * @example - * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); - * @example - * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); - */ - return function toBeResolvedTo(util, customEqualityTesters) { - return { - compare: function(actualPromise, expectedValue) { - if (!j$.isPromiseLike(actualPromise)) { - throw new Error('Expected toBeResolvedTo to be called on a promise.'); - } - - function prefix(passed) { - return 'Expected a promise ' + - (passed ? 'not ' : '') + - 'to be resolved to ' + j$.pp(expectedValue); - } - - return actualPromise.then( - function(actualValue) { - if (util.equals(actualValue, expectedValue, customEqualityTesters)) { - return { - pass: true, - message: prefix(true) + '.' - }; - } else { - return { - pass: false, - message: prefix(false) + ' but it was resolved to ' + j$.pp(actualValue) + '.' - }; - } - }, - function() { - return { - pass: false, - message: prefix(false) + ' but it was rejected.' - }; - } - ); - } - }; - }; -}; - -getJasmineRequireObj().DiffBuilder = function(j$) { - return function DiffBuilder() { - var path = new j$.ObjectPath(), - mismatches = []; - - return { - record: function (actual, expected, formatter) { - formatter = formatter || defaultFormatter; - mismatches.push(formatter(actual, expected, path)); - }, - - getMessage: function () { - return mismatches.join('\n'); - }, - - withPath: function (pathComponent, block) { - var oldPath = path; - path = path.add(pathComponent); - block(); - path = oldPath; - } - }; - - function defaultFormatter (actual, expected, path) { - return 'Expected ' + - path + (path.depth() ? ' = ' : '') + - j$.pp(actual) + - ' to equal ' + - j$.pp(expected) + - '.'; - } - }; -}; - -getJasmineRequireObj().matchersUtil = function(j$) { - // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? - - return { - equals: equals, - - contains: function(haystack, needle, customTesters) { - customTesters = customTesters || []; - - if (j$.isSet(haystack)) { - return haystack.has(needle); - } - - if ((Object.prototype.toString.apply(haystack) === '[object Array]') || - (!!haystack && !haystack.indexOf)) - { - for (var i = 0; i < haystack.length; i++) { - if (equals(haystack[i], needle, customTesters)) { - return true; - } - } - return false; - } - - return !!haystack && haystack.indexOf(needle) >= 0; - }, - - buildFailureMessage: function() { - var args = Array.prototype.slice.call(arguments, 0), - matcherName = args[0], - isNot = args[1], - actual = args[2], - expected = args.slice(3), - englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); - - var message = 'Expected ' + - j$.pp(actual) + - (isNot ? ' not ' : ' ') + - englishyPredicate; - - if (expected.length > 0) { - for (var i = 0; i < expected.length; i++) { - if (i > 0) { - message += ','; - } - message += ' ' + j$.pp(expected[i]); - } - } - - return message + '.'; - } - }; - - function isAsymmetric(obj) { - return obj && j$.isA_('Function', obj.asymmetricMatch); - } - - function asymmetricMatch(a, b, customTesters, diffBuilder) { - var asymmetricA = isAsymmetric(a), - asymmetricB = isAsymmetric(b), - result; - - if (asymmetricA && asymmetricB) { - return undefined; - } - - if (asymmetricA) { - result = a.asymmetricMatch(b, customTesters); - if (!result) { - diffBuilder.record(a, b); - } - return result; - } - - if (asymmetricB) { - result = b.asymmetricMatch(a, customTesters); - if (!result) { - diffBuilder.record(a, b); - } - return result; - } - } - - function equals(a, b, customTesters, diffBuilder) { - customTesters = customTesters || []; - diffBuilder = diffBuilder || j$.NullDiffBuilder(); - - return eq(a, b, [], [], customTesters, diffBuilder); - } - - // Equality function lovingly adapted from isEqual in - // [Underscore](http://underscorejs.org) - function eq(a, b, aStack, bStack, customTesters, diffBuilder) { - var result = true, i; - - var asymmetricResult = asymmetricMatch(a, b, customTesters, diffBuilder); - if (!j$.util.isUndefined(asymmetricResult)) { - return asymmetricResult; - } - - for (i = 0; i < customTesters.length; i++) { - var customTesterResult = customTesters[i](a, b); - if (!j$.util.isUndefined(customTesterResult)) { - if (!customTesterResult) { - diffBuilder.record(a, b); - } - return customTesterResult; - } - } - - if (a instanceof Error && b instanceof Error) { - result = a.message == b.message; - if (!result) { - diffBuilder.record(a, b); - } - return result; - } - - // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). - if (a === b) { - result = a !== 0 || 1 / a == 1 / b; - if (!result) { - diffBuilder.record(a, b); - } - return result; - } - // A strict comparison is necessary because `null == undefined`. - if (a === null || b === null) { - result = a === b; - if (!result) { - diffBuilder.record(a, b); - } - return result; - } - var className = Object.prototype.toString.call(a); - if (className != Object.prototype.toString.call(b)) { - diffBuilder.record(a, b); - return false; - } - switch (className) { - // Strings, numbers, dates, and booleans are compared by value. - case '[object String]': - // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is - // equivalent to `new String("5")`. - result = a == String(b); - if (!result) { - diffBuilder.record(a, b); - } - return result; - case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for - // other numeric values. - result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); - if (!result) { - diffBuilder.record(a, b); - } - return result; - case '[object Date]': - case '[object Boolean]': - // Coerce dates and booleans to numeric primitive values. Dates are compared by their - // millisecond representations. Note that invalid dates with millisecond representations - // of `NaN` are not equivalent. - result = +a == +b; - if (!result) { - diffBuilder.record(a, b); - } - return result; - // RegExps are compared by their source patterns and flags. - case '[object RegExp]': - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; - } - if (typeof a != 'object' || typeof b != 'object') { - diffBuilder.record(a, b); - return false; - } - - var aIsDomNode = j$.isDomNode(a); - var bIsDomNode = j$.isDomNode(b); - if (aIsDomNode && bIsDomNode) { - // At first try to use DOM3 method isEqualNode - result = a.isEqualNode(b); - if (!result) { - diffBuilder.record(a, b); - } - return result; - } - if (aIsDomNode || bIsDomNode) { - diffBuilder.record(a, b); - return false; - } - - var aIsPromise = j$.isPromise(a); - var bIsPromise = j$.isPromise(b); - if (aIsPromise && bIsPromise) { - return a === b; - } - - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = aStack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (aStack[length] == a) { return bStack[length] == b; } - } - // Add the first object to the stack of traversed objects. - aStack.push(a); - bStack.push(b); - var size = 0; - // Recursively compare objects and arrays. - // Compare array lengths to determine if a deep comparison is necessary. - if (className == '[object Array]') { - var aLength = a.length; - var bLength = b.length; - - diffBuilder.withPath('length', function() { - if (aLength !== bLength) { - diffBuilder.record(aLength, bLength); - result = false; - } - }); - - for (i = 0; i < aLength || i < bLength; i++) { - diffBuilder.withPath(i, function() { - if (i >= bLength) { - diffBuilder.record(a[i], void 0, actualArrayIsLongerFormatter); - result = false; - } else { - result = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result; - } - }); - } - if (!result) { - return false; - } - } else if (j$.isMap(a) && j$.isMap(b)) { - if (a.size != b.size) { - diffBuilder.record(a, b); - return false; - } - - var keysA = []; - var keysB = []; - a.forEach( function( valueA, keyA ) { - keysA.push( keyA ); - }); - b.forEach( function( valueB, keyB ) { - keysB.push( keyB ); - }); - - // For both sets of keys, check they map to equal values in both maps. - // Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys. - var mapKeys = [keysA, keysB]; - var cmpKeys = [keysB, keysA]; - var mapIter, mapKey, mapValueA, mapValueB; - var cmpIter, cmpKey; - for (i = 0; result && i < mapKeys.length; i++) { - mapIter = mapKeys[i]; - cmpIter = cmpKeys[i]; - - for (var j = 0; result && j < mapIter.length; j++) { - mapKey = mapIter[j]; - cmpKey = cmpIter[j]; - mapValueA = a.get(mapKey); - - // Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches, - // otherwise explicitly look up the mapKey in the other Map since we want keys with unique - // obj identity (that are otherwise equal) to not match. - if (isAsymmetric(mapKey) || isAsymmetric(cmpKey) && - eq(mapKey, cmpKey, aStack, bStack, customTesters, j$.NullDiffBuilder())) { - mapValueB = b.get(cmpKey); - } else { - mapValueB = b.get(mapKey); - } - result = eq(mapValueA, mapValueB, aStack, bStack, customTesters, j$.NullDiffBuilder()); - } - } - - if (!result) { - diffBuilder.record(a, b); - return false; - } - } else if (j$.isSet(a) && j$.isSet(b)) { - if (a.size != b.size) { - diffBuilder.record(a, b); - return false; - } - - var valuesA = []; - a.forEach( function( valueA ) { - valuesA.push( valueA ); - }); - var valuesB = []; - b.forEach( function( valueB ) { - valuesB.push( valueB ); - }); - - // For both sets, check they are all contained in the other set - var setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; - var stackPairs = [[aStack, bStack], [bStack, aStack]]; - var baseValues, baseValue, baseStack; - var otherValues, otherValue, otherStack; - var found; - var prevStackSize; - for (i = 0; result && i < setPairs.length; i++) { - baseValues = setPairs[i][0]; - otherValues = setPairs[i][1]; - baseStack = stackPairs[i][0]; - otherStack = stackPairs[i][1]; - // For each value in the base set... - for (var k = 0; result && k < baseValues.length; k++) { - baseValue = baseValues[k]; - found = false; - // ... test that it is present in the other set - for (var l = 0; !found && l < otherValues.length; l++) { - otherValue = otherValues[l]; - prevStackSize = baseStack.length; - // compare by value equality - found = eq(baseValue, otherValue, baseStack, otherStack, customTesters, j$.NullDiffBuilder()); - if (!found && prevStackSize !== baseStack.length) { - baseStack.splice(prevStackSize); - otherStack.splice(prevStackSize); - } - } - result = result && found; - } - } - - if (!result) { - diffBuilder.record(a, b); - return false; - } - } else { - - // Objects with different constructors are not equivalent, but `Object`s - // or `Array`s from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && - isFunction(aCtor) && isFunction(bCtor) && - a instanceof aCtor && b instanceof bCtor && - !(aCtor instanceof aCtor && bCtor instanceof bCtor)) { - - diffBuilder.record(a, b, constructorsAreDifferentFormatter); - return false; - } - } - - // Deep compare objects. - var aKeys = keys(a, className == '[object Array]'), key; - size = aKeys.length; - - // Ensure that both objects contain the same number of properties before comparing deep equality. - if (keys(b, className == '[object Array]').length !== size) { - diffBuilder.record(a, b, objectKeysAreDifferentFormatter); - return false; - } - - for (i = 0; i < size; i++) { - key = aKeys[i]; - // Deep compare each member - if (!j$.util.has(b, key)) { - diffBuilder.record(a, b, objectKeysAreDifferentFormatter); - result = false; - continue; - } - - diffBuilder.withPath(key, function() { - if(!eq(a[key], b[key], aStack, bStack, customTesters, diffBuilder)) { - result = false; - } - }); - } - - if (!result) { - return false; - } - - // Remove the first object from the stack of traversed objects. - aStack.pop(); - bStack.pop(); - - return result; - } - - function keys(obj, isArray) { - var allKeys = Object.keys ? Object.keys(obj) : - (function(o) { - var keys = []; - for (var key in o) { - if (j$.util.has(o, key)) { - keys.push(key); - } - } - return keys; - })(obj); - - if (!isArray) { - return allKeys; - } - - if (allKeys.length === 0) { - return allKeys; - } - - var extraKeys = []; - for (var i = 0; i < allKeys.length; i++) { - if (!/^[0-9]+$/.test(allKeys[i])) { - extraKeys.push(allKeys[i]); - } - } - - return extraKeys; - } - - function isFunction(obj) { - return typeof obj === 'function'; - } - - function objectKeysAreDifferentFormatter(actual, expected, path) { - var missingProperties = j$.util.objectDifference(expected, actual), - extraProperties = j$.util.objectDifference(actual, expected), - missingPropertiesMessage = formatKeyValuePairs(missingProperties), - extraPropertiesMessage = formatKeyValuePairs(extraProperties), - messages = []; - - if (!path.depth()) { - path = 'object'; - } - - if (missingPropertiesMessage.length) { - messages.push('Expected ' + path + ' to have properties' + missingPropertiesMessage); - } - - if (extraPropertiesMessage.length) { - messages.push('Expected ' + path + ' not to have properties' + extraPropertiesMessage); - } - - return messages.join('\n'); - } - - function constructorsAreDifferentFormatter(actual, expected, path) { - if (!path.depth()) { - path = 'object'; - } - - return 'Expected ' + - path + ' to be a kind of ' + - j$.fnNameFor(expected.constructor) + - ', but was ' + j$.pp(actual) + '.'; - } - - function actualArrayIsLongerFormatter(actual, expected, path) { - return 'Unexpected ' + - path + (path.depth() ? ' = ' : '') + - j$.pp(actual) + - ' in array.'; - } - - function formatKeyValuePairs(obj) { - var formatted = ''; - for (var key in obj) { - formatted += '\n ' + key + ': ' + j$.pp(obj[key]); - } - return formatted; - } -}; - -getJasmineRequireObj().nothing = function() { - /** - * {@link expect} nothing explicitly. - * @function - * @name matchers#nothing - * @since 2.8.0 - * @example - * expect().nothing(); - */ - function nothing() { - return { - compare: function() { - return { - pass: true - }; - } - }; - } - - return nothing; -}; - -getJasmineRequireObj().NullDiffBuilder = function(j$) { - return function() { - return { - withPath: function(_, block) { - block(); - }, - record: function() {} - }; - }; -}; - -getJasmineRequireObj().ObjectPath = function(j$) { - function ObjectPath(components) { - this.components = components || []; - } - - ObjectPath.prototype.toString = function() { - if (this.components.length) { - return '$' + map(this.components, formatPropertyAccess).join(''); - } else { - return ''; - } - }; - - ObjectPath.prototype.add = function(component) { - return new ObjectPath(this.components.concat([component])); - }; - - ObjectPath.prototype.depth = function() { - return this.components.length; - }; - - function formatPropertyAccess(prop) { - if (typeof prop === 'number') { - return '[' + prop + ']'; - } - - if (isValidIdentifier(prop)) { - return '.' + prop; - } - - return '[\'' + prop + '\']'; - } - - function map(array, fn) { - var results = []; - for (var i = 0; i < array.length; i++) { - results.push(fn(array[i])); - } - return results; - } - - function isValidIdentifier(string) { - return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string); - } - - return ObjectPath; -}; - -getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { - var availableMatchers = [ - 'toBeResolved', - 'toBeRejected', - 'toBeResolvedTo', - 'toBeRejectedWith', - 'toBeRejectedWithError' - ], - matchers = {}; - - for (var i = 0; i < availableMatchers.length; i++) { - var name = availableMatchers[i]; - matchers[name] = jRequire[name](j$); - } - - return matchers; -}; - -getJasmineRequireObj().toBe = function(j$) { - /** - * {@link expect} the actual value to be `===` to the expected value. - * @function - * @name matchers#toBe - * @since 1.3.0 - * @param {Object} expected - The expected value to compare against. - * @example - * expect(thing).toBe(realThing); - */ - function toBe(util) { - var tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().'; - - return { - compare: function(actual, expected) { - var result = { - pass: actual === expected - }; - - if (typeof expected === 'object') { - result.message = util.buildFailureMessage('toBe', result.pass, actual, expected) + tip; - } - - return result; - } - }; - } - - return toBe; -}; - -getJasmineRequireObj().toBeCloseTo = function() { - /** - * {@link expect} the actual value to be within a specified precision of the expected value. - * @function - * @name matchers#toBeCloseTo - * @since 1.3.0 - * @param {Object} expected - The expected value to compare against. - * @param {Number} [precision=2] - The number of decimal points to check. - * @example - * expect(number).toBeCloseTo(42.2, 3); - */ - function toBeCloseTo() { - return { - compare: function(actual, expected, precision) { - if (precision !== 0) { - precision = precision || 2; - } - - if (expected === null || actual === null) { - throw new Error('Cannot use toBeCloseTo with null. Arguments evaluated to: ' + - 'expect(' + actual + ').toBeCloseTo(' + expected + ').' - ); - } - - var pow = Math.pow(10, precision + 1); - var delta = Math.abs(expected - actual); - var maxDelta = Math.pow(10, -precision) / 2; - - return { - pass: Math.round(delta * pow) <= maxDelta * pow - }; - } - }; - } - - return toBeCloseTo; -}; - -getJasmineRequireObj().toBeDefined = function() { - /** - * {@link expect} the actual value to be defined. (Not `undefined`) - * @function - * @name matchers#toBeDefined - * @since 1.3.0 - * @example - * expect(result).toBeDefined(); - */ - function toBeDefined() { - return { - compare: function(actual) { - return { - pass: (void 0 !== actual) - }; - } - }; - } - - return toBeDefined; -}; - -getJasmineRequireObj().toBeFalse = function() { - /** - * {@link expect} the actual value to be `false`. - * @function - * @name matchers#toBeFalse - * @since 3.5.0 - * @example - * expect(result).toBeFalse(); - */ - function toBeFalse() { - return { - compare: function(actual) { - return { - pass: actual === false - }; - } - }; - } - - return toBeFalse; -}; - -getJasmineRequireObj().toBeFalsy = function() { - /** - * {@link expect} the actual value to be falsy - * @function - * @name matchers#toBeFalsy - * @since 2.0.0 - * @example - * expect(result).toBeFalsy(); - */ - function toBeFalsy() { - return { - compare: function(actual) { - return { - pass: !actual - }; - } - }; - } - - return toBeFalsy; -}; - -getJasmineRequireObj().toBeGreaterThan = function() { - /** - * {@link expect} the actual value to be greater than the expected value. - * @function - * @name matchers#toBeGreaterThan - * @since 2.0.0 - * @param {Number} expected - The value to compare against. - * @example - * expect(result).toBeGreaterThan(3); - */ - function toBeGreaterThan() { - return { - compare: function(actual, expected) { - return { - pass: actual > expected - }; - } - }; - } - - return toBeGreaterThan; -}; - - -getJasmineRequireObj().toBeGreaterThanOrEqual = function() { - /** - * {@link expect} the actual value to be greater than or equal to the expected value. - * @function - * @name matchers#toBeGreaterThanOrEqual - * @since 2.0.0 - * @param {Number} expected - The expected value to compare against. - * @example - * expect(result).toBeGreaterThanOrEqual(25); - */ - function toBeGreaterThanOrEqual() { - return { - compare: function(actual, expected) { - return { - pass: actual >= expected - }; - } - }; - } - - return toBeGreaterThanOrEqual; -}; - -getJasmineRequireObj().toBeInstanceOf = function(j$) { - var usageError = j$.formatErrorMsg('', 'expect(value).toBeInstanceOf()'); - - /** - * {@link expect} the actual to be an instance of the expected class - * @function - * @name matchers#toBeInstanceOf - * @since 3.5.0 - * @param {Object} expected - The class or constructor function to check for - * @example - * expect('foo').toBeInstanceOf(String); - * expect(3).toBeInstanceOf(Number); - * expect(new Error()).toBeInstanceOf(Error); - */ - function toBeInstanceOf(util, customEqualityTesters) { - return { - compare: function(actual, expected) { - var actualType = actual && actual.constructor ? j$.fnNameFor(actual.constructor) : j$.pp(actual), - expectedType = expected ? j$.fnNameFor(expected) : j$.pp(expected), - expectedMatcher, - pass; - - try { - expectedMatcher = new j$.Any(expected); - pass = expectedMatcher.asymmetricMatch(actual); - } catch (error) { - throw new Error(usageError('Expected value is not a constructor function')); - } - - if (pass) { - return { - pass: true, - message: 'Expected instance of ' + actualType + ' not to be an instance of ' + expectedType - }; - } else { - return { - pass: false, - message: 'Expected instance of ' + actualType + ' to be an instance of ' + expectedType - }; - } - } - }; - } - - return toBeInstanceOf; -}; - -getJasmineRequireObj().toBeLessThan = function() { - /** - * {@link expect} the actual value to be less than the expected value. - * @function - * @name matchers#toBeLessThan - * @since 2.0.0 - * @param {Number} expected - The expected value to compare against. - * @example - * expect(result).toBeLessThan(0); - */ - function toBeLessThan() { - return { - - compare: function(actual, expected) { - return { - pass: actual < expected - }; - } - }; - } - - return toBeLessThan; -}; - -getJasmineRequireObj().toBeLessThanOrEqual = function() { - /** - * {@link expect} the actual value to be less than or equal to the expected value. - * @function - * @name matchers#toBeLessThanOrEqual - * @since 2.0.0 - * @param {Number} expected - The expected value to compare against. - * @example - * expect(result).toBeLessThanOrEqual(123); - */ - function toBeLessThanOrEqual() { - return { - - compare: function(actual, expected) { - return { - pass: actual <= expected - }; - } - }; - } - - return toBeLessThanOrEqual; -}; - -getJasmineRequireObj().toBeNaN = function(j$) { - /** - * {@link expect} the actual value to be `NaN` (Not a Number). - * @function - * @name matchers#toBeNaN - * @since 1.3.0 - * @example - * expect(thing).toBeNaN(); - */ - function toBeNaN() { - return { - compare: function(actual) { - var result = { - pass: (actual !== actual) - }; - - if (result.pass) { - result.message = 'Expected actual not to be NaN.'; - } else { - result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; }; - } - - return result; - } - }; - } - - return toBeNaN; -}; - -getJasmineRequireObj().toBeNegativeInfinity = function(j$) { - /** - * {@link expect} the actual value to be `-Infinity` (-infinity). - * @function - * @name matchers#toBeNegativeInfinity - * @since 2.6.0 - * @example - * expect(thing).toBeNegativeInfinity(); - */ - function toBeNegativeInfinity() { - return { - compare: function(actual) { - var result = { - pass: (actual === Number.NEGATIVE_INFINITY) - }; - - if (result.pass) { - result.message = 'Expected actual not to be -Infinity.'; - } else { - result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be -Infinity.'; }; - } - - return result; - } - }; - } - - return toBeNegativeInfinity; -}; - -getJasmineRequireObj().toBeNull = function() { - /** - * {@link expect} the actual value to be `null`. - * @function - * @name matchers#toBeNull - * @since 1.3.0 - * @example - * expect(result).toBeNull(); - */ - function toBeNull() { - return { - compare: function(actual) { - return { - pass: actual === null - }; - } - }; - } - - return toBeNull; -}; - -getJasmineRequireObj().toBePositiveInfinity = function(j$) { - /** - * {@link expect} the actual value to be `Infinity` (infinity). - * @function - * @name matchers#toBePositiveInfinity - * @since 2.6.0 - * @example - * expect(thing).toBePositiveInfinity(); - */ - function toBePositiveInfinity() { - return { - compare: function(actual) { - var result = { - pass: (actual === Number.POSITIVE_INFINITY) - }; - - if (result.pass) { - result.message = 'Expected actual not to be Infinity.'; - } else { - result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be Infinity.'; }; - } - - return result; - } - }; - } - - return toBePositiveInfinity; -}; - -getJasmineRequireObj().toBeTrue = function() { - /** - * {@link expect} the actual value to be `true`. - * @function - * @name matchers#toBeTrue - * @since 3.5.0 - * @example - * expect(result).toBeTrue(); - */ - function toBeTrue() { - return { - compare: function(actual) { - return { - pass: actual === true - }; - } - }; - } - - return toBeTrue; -}; - -getJasmineRequireObj().toBeTruthy = function() { - /** - * {@link expect} the actual value to be truthy. - * @function - * @name matchers#toBeTruthy - * @since 2.0.0 - * @example - * expect(thing).toBeTruthy(); - */ - function toBeTruthy() { - return { - compare: function(actual) { - return { - pass: !!actual - }; - } - }; - } - - return toBeTruthy; -}; - -getJasmineRequireObj().toBeUndefined = function() { - /** - * {@link expect} the actual value to be `undefined`. - * @function - * @name matchers#toBeUndefined - * @since 1.3.0 - * @example - * expect(result).toBeUndefined(): - */ - function toBeUndefined() { - return { - compare: function(actual) { - return { - pass: void 0 === actual - }; - } - }; - } - - return toBeUndefined; -}; - -getJasmineRequireObj().toContain = function() { - /** - * {@link expect} the actual value to contain a specific value. - * @function - * @name matchers#toContain - * @since 2.0.0 - * @param {Object} expected - The value to look for. - * @example - * expect(array).toContain(anElement); - * expect(string).toContain(substring); - */ - function toContain(util, customEqualityTesters) { - customEqualityTesters = customEqualityTesters || []; - - return { - compare: function(actual, expected) { - - return { - pass: util.contains(actual, expected, customEqualityTesters) - }; - } - }; - } - - return toContain; -}; - -getJasmineRequireObj().toEqual = function(j$) { - /** - * {@link expect} the actual value to be equal to the expected, using deep equality comparison. - * @function - * @name matchers#toEqual - * @since 1.3.0 - * @param {Object} expected - Expected value - * @example - * expect(bigObject).toEqual({"foo": ['bar', 'baz']}); - */ - function toEqual(util, customEqualityTesters) { - customEqualityTesters = customEqualityTesters || []; - - return { - compare: function(actual, expected) { - var result = { - pass: false - }, - diffBuilder = j$.DiffBuilder(); - - result.pass = util.equals(actual, expected, customEqualityTesters, diffBuilder); - - // TODO: only set error message if test fails - result.message = diffBuilder.getMessage(); - - return result; - } - }; - } - - return toEqual; -}; - -getJasmineRequireObj().toHaveBeenCalled = function(j$) { - - var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalled()'); - - /** - * {@link expect} the actual (a {@link Spy}) to have been called. - * @function - * @name matchers#toHaveBeenCalled - * @since 1.3.0 - * @example - * expect(mySpy).toHaveBeenCalled(); - * expect(mySpy).not.toHaveBeenCalled(); - */ - function toHaveBeenCalled() { - return { - compare: function(actual) { - var result = {}; - - if (!j$.isSpy(actual)) { - throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); - } - - if (arguments.length > 1) { - throw new Error(getErrorMsg('Does not take arguments, use toHaveBeenCalledWith')); - } - - result.pass = actual.calls.any(); - - result.message = result.pass ? - 'Expected spy ' + actual.and.identity + ' not to have been called.' : - 'Expected spy ' + actual.and.identity + ' to have been called.'; - - return result; - } - }; - } - - return toHaveBeenCalled; -}; - -getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { - - var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledBefore()'); - - /** - * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}. - * @function - * @name matchers#toHaveBeenCalledBefore - * @since 2.6.0 - * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}. - * @example - * expect(mySpy).toHaveBeenCalledBefore(otherSpy); - */ - function toHaveBeenCalledBefore() { - return { - compare: function(firstSpy, latterSpy) { - if (!j$.isSpy(firstSpy)) { - throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(firstSpy) + '.')); - } - if (!j$.isSpy(latterSpy)) { - throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(latterSpy) + '.')); - } - - var result = { pass: false }; - - if (!firstSpy.calls.count()) { - result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called.'; - return result; - } - if (!latterSpy.calls.count()) { - result.message = 'Expected spy ' + latterSpy.and.identity + ' to have been called.'; - return result; - } - - var latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder; - var first2ndSpyCall = latterSpy.calls.first().invocationOrder; - - result.pass = latest1stSpyCall < first2ndSpyCall; - - if (result.pass) { - result.message = 'Expected spy ' + firstSpy.and.identity + ' to not have been called before spy ' + latterSpy.and.identity + ', but it was'; - } else { - var first1stSpyCall = firstSpy.calls.first().invocationOrder; - var latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder; - - if(first1stSpyCall < first2ndSpyCall) { - result.message = 'Expected latest call to spy ' + firstSpy.and.identity + ' to have been called before first call to spy ' + latterSpy.and.identity + ' (no interleaved calls)'; - } else if (latest2ndSpyCall > latest1stSpyCall) { - result.message = 'Expected first call to spy ' + latterSpy.and.identity + ' to have been called after latest call to spy ' + firstSpy.and.identity + ' (no interleaved calls)'; - } else { - result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called before spy ' + latterSpy.and.identity; - } - } - - return result; - } - }; - } - - return toHaveBeenCalledBefore; -}; - -getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { - - var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledTimes()'); - - /** - * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times. - * @function - * @name matchers#toHaveBeenCalledTimes - * @since 2.4.0 - * @param {Number} expected - The number of invocations to look for. - * @example - * expect(mySpy).toHaveBeenCalledTimes(3); - */ - function toHaveBeenCalledTimes() { - return { - compare: function(actual, expected) { - if (!j$.isSpy(actual)) { - throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); - } - - var args = Array.prototype.slice.call(arguments, 0), - result = { pass: false }; - - if (!j$.isNumber_(expected)) { - throw new Error(getErrorMsg('The expected times failed is a required argument and must be a number.')); - } - - actual = args[0]; - var calls = actual.calls.count(); - var timesMessage = expected === 1 ? 'once' : expected + ' times'; - result.pass = calls === expected; - result.message = result.pass ? - 'Expected spy ' + actual.and.identity + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' : - 'Expected spy ' + actual.and.identity + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.'; - return result; - } - }; - } - - return toHaveBeenCalledTimes; -}; - -getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { - - var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledWith(...arguments)'); - - /** - * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once. - * @function - * @name matchers#toHaveBeenCalledWith - * @since 1.3.0 - * @param {...Object} - The arguments to look for - * @example - * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2); - */ - function toHaveBeenCalledWith(util, customEqualityTesters) { - return { - compare: function() { - var args = Array.prototype.slice.call(arguments, 0), - actual = args[0], - expectedArgs = args.slice(1), - result = { pass: false }; - - if (!j$.isSpy(actual)) { - throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); - } - - if (!actual.calls.any()) { - result.message = function() { - return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + - ' ' + j$.pp(expectedArgs) + - '\nbut it was never called.'; - }; - return result; - } - - if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) { - result.pass = true; - result.message = function() { - return 'Expected spy ' + actual.and.identity + ' not to have been called with:\n' + - ' ' + j$.pp(expectedArgs) + - '\nbut it was.'; - }; - } else { - result.message = function() { - var prettyPrintedCalls = actual.calls.allArgs().map(function(argsForCall) { - return ' ' + j$.pp(argsForCall); - }); - - var diffs = actual.calls.allArgs().map(function(argsForCall, callIx) { - var diffBuilder = new j$.DiffBuilder(); - util.equals(argsForCall, expectedArgs, customEqualityTesters, diffBuilder); - return 'Call ' + callIx + ':\n' + - diffBuilder.getMessage().replace(/^/mg, ' '); - }); - - return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + - ' ' + j$.pp(expectedArgs) + '\n' + '' + - 'but actual calls were:\n' + - prettyPrintedCalls.join(',\n') + '.\n\n' + - diffs.join('\n'); - }; - } - - return result; - } - }; - } - - return toHaveBeenCalledWith; -}; - -getJasmineRequireObj().toHaveClass = function(j$) { - /** - * {@link expect} the actual value to be a DOM element that has the expected class - * @function - * @name matchers#toHaveClass - * @since 3.0.0 - * @param {Object} expected - The class name to test for - * @example - * var el = document.createElement('div'); - * el.className = 'foo bar baz'; - * expect(el).toHaveClass('bar'); - */ - function toHaveClass(util, customEqualityTesters) { - return { - compare: function(actual, expected) { - if (!isElement(actual)) { - throw new Error(j$.pp(actual) + ' is not a DOM element'); - } - - return { - pass: actual.classList.contains(expected) - }; - } - }; - } - - function isElement(maybeEl) { - return maybeEl && - maybeEl.classList && - j$.isFunction_(maybeEl.classList.contains); - } - - return toHaveClass; -}; - -getJasmineRequireObj().toMatch = function(j$) { - - var getErrorMsg = j$.formatErrorMsg('', 'expect().toMatch( || )'); - - /** - * {@link expect} the actual value to match a regular expression - * @function - * @name matchers#toMatch - * @since 1.3.0 - * @param {RegExp|String} expected - Value to look for in the string. - * @example - * expect("my string").toMatch(/string$/); - * expect("other string").toMatch("her"); - */ - function toMatch() { - return { - compare: function(actual, expected) { - if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { - throw new Error(getErrorMsg('Expected is not a String or a RegExp')); - } - - var regexp = new RegExp(expected); - - return { - pass: regexp.test(actual) - }; - } - }; - } - - return toMatch; -}; - -getJasmineRequireObj().toThrow = function(j$) { - - var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrow()'); - - /** - * {@link expect} a function to `throw` something. - * @function - * @name matchers#toThrow - * @since 2.0.0 - * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked. - * @example - * expect(function() { return 'things'; }).toThrow('foo'); - * expect(function() { return 'stuff'; }).toThrow(); - */ - function toThrow(util) { - return { - compare: function(actual, expected) { - var result = { pass: false }, - threw = false, - thrown; - - if (typeof actual != 'function') { - throw new Error(getErrorMsg('Actual is not a Function')); - } - - try { - actual(); - } catch (e) { - threw = true; - thrown = e; - } - - if (!threw) { - result.message = 'Expected function to throw an exception.'; - return result; - } - - if (arguments.length == 1) { - result.pass = true; - result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; }; - - return result; - } - - if (util.equals(thrown, expected)) { - result.pass = true; - result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; }; - } else { - result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; }; - } - - return result; - } - }; - } - - return toThrow; -}; - -getJasmineRequireObj().toThrowError = function(j$) { - - var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrowError(, )'); - - /** - * {@link expect} a function to `throw` an `Error`. - * @function - * @name matchers#toThrowError - * @since 2.0.0 - * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. - * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` - * @example - * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message'); - * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/); - * expect(function() { return 'stuff'; }).toThrowError(MyCustomError); - * expect(function() { return 'other'; }).toThrowError(/foo/); - * expect(function() { return 'other'; }).toThrowError(); - */ - function toThrowError () { - return { - compare: function(actual) { - var errorMatcher = getMatcher.apply(null, arguments), - thrown; - - if (typeof actual != 'function') { - throw new Error(getErrorMsg('Actual is not a Function')); - } - - try { - actual(); - return fail('Expected function to throw an Error.'); - } catch (e) { - thrown = e; - } - - if (!j$.isError_(thrown)) { - return fail(function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; }); - } - - return errorMatcher.match(thrown); - } - }; - - function getMatcher() { - var expected, errorType; - - if (arguments[2]) { - errorType = arguments[1]; - expected = arguments[2]; - if (!isAnErrorType(errorType)) { - throw new Error(getErrorMsg('Expected error type is not an Error.')); - } - - return exactMatcher(expected, errorType); - } else if (arguments[1]) { - expected = arguments[1]; - - if (isAnErrorType(arguments[1])) { - return exactMatcher(null, arguments[1]); - } else { - return exactMatcher(arguments[1], null); - } - } else { - return anyMatcher(); - } - } - - function anyMatcher() { - return { - match: function(error) { - return pass('Expected function not to throw an Error, but it threw ' + j$.fnNameFor(error) + '.'); - } - }; - } - - function exactMatcher(expected, errorType) { - if (expected && !isStringOrRegExp(expected)) { - if (errorType) { - throw new Error(getErrorMsg('Expected error message is not a string or RegExp.')); - } else { - throw new Error(getErrorMsg('Expected is not an Error, string, or RegExp.')); - } - } - - function messageMatch(message) { - if (typeof expected == 'string') { - return expected == message; - } else { - return expected.test(message); - } - } - - var errorTypeDescription = errorType ? j$.fnNameFor(errorType) : 'an exception'; - - function thrownDescription(thrown) { - var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception', - thrownMessage = ''; - - if (expected) { - thrownMessage = ' with message ' + j$.pp(thrown.message); - } - - return thrownName + thrownMessage; - } - - function messageDescription() { - if (expected === null) { - return ''; - } else if (expected instanceof RegExp) { - return ' with a message matching ' + j$.pp(expected); - } else { - return ' with message ' + j$.pp(expected); - } - } - - function matches(error) { - return (errorType === null || error instanceof errorType) && - (expected === null || messageMatch(error.message)); - } - - return { - match: function(thrown) { - if (matches(thrown)) { - return pass(function() { - return 'Expected function not to throw ' + errorTypeDescription + messageDescription() + '.'; - }); - } else { - return fail(function() { - return 'Expected function to throw ' + errorTypeDescription + messageDescription() + - ', but it threw ' + thrownDescription(thrown) + '.'; - }); - } - } - }; - } - - function isStringOrRegExp(potential) { - return potential instanceof RegExp || (typeof potential == 'string'); - } - - function isAnErrorType(type) { - if (typeof type !== 'function') { - return false; - } - - var Surrogate = function() {}; - Surrogate.prototype = type.prototype; - return j$.isError_(new Surrogate()); - } - } - - function pass(message) { - return { - pass: true, - message: message - }; - } - - function fail(message) { - return { - pass: false, - message: message - }; - } - - return toThrowError; -}; - -getJasmineRequireObj().toThrowMatching = function(j$) { - var usageError = j$.formatErrorMsg('', 'expect(function() {}).toThrowMatching()'); - - /** - * {@link expect} a function to `throw` something matching a predicate. - * @function - * @name matchers#toThrowMatching - * @since 3.0.0 - * @param {Function} predicate - A function that takes the thrown exception as its parameter and returns true if it matches. - * @example - * expect(function() { throw new Error('nope'); }).toThrowMatching(function(thrown) { return thrown.message === 'nope'; }); - */ - function toThrowMatching() { - return { - compare: function(actual, predicate) { - var thrown; - - if (typeof actual !== 'function') { - throw new Error(usageError('Actual is not a Function')); - } - - if (typeof predicate !== 'function') { - throw new Error(usageError('Predicate is not a Function')); - } - - try { - actual(); - return fail('Expected function to throw an exception.'); - } catch (e) { - thrown = e; - } - - if (predicate(thrown)) { - return pass('Expected function not to throw an exception matching a predicate.'); - } else { - return fail(function() { - return 'Expected function to throw an exception matching a predicate, ' + - 'but it threw ' + thrownDescription(thrown) + '.'; - }); - } - } - }; - } - - function thrownDescription(thrown) { - if (thrown && thrown.constructor) { - return j$.fnNameFor(thrown.constructor) + ' with message ' + - j$.pp(thrown.message); - } else { - return j$.pp(thrown); - } - } - - function pass(message) { - return { - pass: true, - message: message - }; - } - - function fail(message) { - return { - pass: false, - message: message - }; - } - - return toThrowMatching; -}; - -getJasmineRequireObj().MockDate = function() { - function MockDate(global) { - var self = this; - var currentTime = 0; - - if (!global || !global.Date) { - self.install = function() {}; - self.tick = function() {}; - self.uninstall = function() {}; - return self; - } - - var GlobalDate = global.Date; - - self.install = function(mockDate) { - if (mockDate instanceof GlobalDate) { - currentTime = mockDate.getTime(); - } else { - currentTime = new GlobalDate().getTime(); - } - - global.Date = FakeDate; - }; - - self.tick = function(millis) { - millis = millis || 0; - currentTime = currentTime + millis; - }; - - self.uninstall = function() { - currentTime = 0; - global.Date = GlobalDate; - }; - - createDateProperties(); - - return self; - - function FakeDate() { - switch (arguments.length) { - case 0: - return new GlobalDate(currentTime); - case 1: - return new GlobalDate(arguments[0]); - case 2: - return new GlobalDate(arguments[0], arguments[1]); - case 3: - return new GlobalDate(arguments[0], arguments[1], arguments[2]); - case 4: - return new GlobalDate( - arguments[0], - arguments[1], - arguments[2], - arguments[3] - ); - case 5: - return new GlobalDate( - arguments[0], - arguments[1], - arguments[2], - arguments[3], - arguments[4] - ); - case 6: - return new GlobalDate( - arguments[0], - arguments[1], - arguments[2], - arguments[3], - arguments[4], - arguments[5] - ); - default: - return new GlobalDate( - arguments[0], - arguments[1], - arguments[2], - arguments[3], - arguments[4], - arguments[5], - arguments[6] - ); - } - } - - function createDateProperties() { - FakeDate.prototype = GlobalDate.prototype; - - FakeDate.now = function() { - if (GlobalDate.now) { - return currentTime; - } else { - throw new Error('Browser does not support Date.now()'); - } - }; - - FakeDate.toSource = GlobalDate.toSource; - FakeDate.toString = GlobalDate.toString; - FakeDate.parse = GlobalDate.parse; - FakeDate.UTC = GlobalDate.UTC; - } - } - - return MockDate; -}; - -getJasmineRequireObj().pp = function(j$) { - function PrettyPrinter() { - this.ppNestLevel_ = 0; - this.seen = []; - this.length = 0; - this.stringParts = []; - } - - function hasCustomToString(value) { - // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g. - // iframe, web worker) - try { - return ( - j$.isFunction_(value.toString) && - value.toString !== Object.prototype.toString && - value.toString() !== Object.prototype.toString.call(value) - ); - } catch (e) { - // The custom toString() threw. - return true; - } - } - - PrettyPrinter.prototype.format = function(value) { - this.ppNestLevel_++; - try { - if (j$.util.isUndefined(value)) { - this.emitScalar('undefined'); - } else if (value === null) { - this.emitScalar('null'); - } else if (value === 0 && 1 / value === -Infinity) { - this.emitScalar('-0'); - } else if (value === j$.getGlobal()) { - this.emitScalar(''); - } else if (value.jasmineToString) { - this.emitScalar(value.jasmineToString()); - } else if (typeof value === 'string') { - this.emitString(value); - } else if (j$.isSpy(value)) { - this.emitScalar('spy on ' + value.and.identity); - } else if (j$.isSpy(value.toString)) { - this.emitScalar('spy on ' + value.toString.and.identity); - } else if (value instanceof RegExp) { - this.emitScalar(value.toString()); - } else if (typeof value === 'function') { - this.emitScalar('Function'); - } else if (j$.isDomNode(value)) { - if (value.tagName) { - this.emitDomElement(value); - } else { - this.emitScalar('HTMLNode'); - } - } else if (value instanceof Date) { - this.emitScalar('Date(' + value + ')'); - } else if (j$.isSet(value)) { - this.emitSet(value); - } else if (j$.isMap(value)) { - this.emitMap(value); - } else if (j$.isTypedArray_(value)) { - this.emitTypedArray(value); - } else if ( - value.toString && - typeof value === 'object' && - !j$.isArray_(value) && - hasCustomToString(value) - ) { - try { - this.emitScalar(value.toString()); - } catch (e) { - this.emitScalar('has-invalid-toString-method'); - } - } else if (j$.util.arrayContains(this.seen, value)) { - this.emitScalar( - '' - ); - } else if (j$.isArray_(value) || j$.isA_('Object', value)) { - this.seen.push(value); - if (j$.isArray_(value)) { - this.emitArray(value); - } else { - this.emitObject(value); - } - this.seen.pop(); - } else { - this.emitScalar(value.toString()); - } - } catch (e) { - if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { - throw e; - } - } finally { - this.ppNestLevel_--; - } - }; - - PrettyPrinter.prototype.iterateObject = function(obj, fn) { - var objKeys = keys(obj, j$.isArray_(obj)); - var isGetter = function isGetter(prop) {}; - - if (obj.__lookupGetter__) { - isGetter = function isGetter(prop) { - var getter = obj.__lookupGetter__(prop); - return !j$.util.isUndefined(getter) && getter !== null; - }; - } - var length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - for (var i = 0; i < length; i++) { - var property = objKeys[i]; - fn(property, isGetter(property)); - } - - return objKeys.length > length; - }; - - PrettyPrinter.prototype.emitScalar = function(value) { - this.append(value); - }; - - PrettyPrinter.prototype.emitString = function(value) { - this.append("'" + value + "'"); - }; - - PrettyPrinter.prototype.emitArray = function(array) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Array'); - return; - } - var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - this.append('[ '); - for (var i = 0; i < length; i++) { - if (i > 0) { - this.append(', '); - } - this.format(array[i]); - } - if (array.length > length) { - this.append(', ...'); - } - - var self = this; - var first = array.length === 0; - var truncated = this.iterateObject(array, function(property, isGetter) { - if (first) { - first = false; - } else { - self.append(', '); - } - - self.formatProperty(array, property, isGetter); - }); - - if (truncated) { - this.append(', ...'); - } - - this.append(' ]'); - }; - - PrettyPrinter.prototype.emitSet = function(set) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Set'); - return; - } - this.append('Set( '); - var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - var i = 0; - set.forEach(function(value, key) { - if (i >= size) { - return; - } - if (i > 0) { - this.append(', '); - } - this.format(value); - - i++; - }, this); - if (set.size > size) { - this.append(', ...'); - } - this.append(' )'); - }; - - PrettyPrinter.prototype.emitMap = function(map) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Map'); - return; - } - this.append('Map( '); - var size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - var i = 0; - map.forEach(function(value, key) { - if (i >= size) { - return; - } - if (i > 0) { - this.append(', '); - } - this.format([key, value]); - - i++; - }, this); - if (map.size > size) { - this.append(', ...'); - } - this.append(' )'); - }; - - PrettyPrinter.prototype.emitObject = function(obj) { - var ctor = obj.constructor, - constructorName; - - constructorName = - typeof ctor === 'function' && obj instanceof ctor - ? j$.fnNameFor(obj.constructor) - : 'null'; - - this.append(constructorName); - - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - return; - } - - var self = this; - this.append('({ '); - var first = true; - - var truncated = this.iterateObject(obj, function(property, isGetter) { - if (first) { - first = false; - } else { - self.append(', '); - } - - self.formatProperty(obj, property, isGetter); - }); - - if (truncated) { - this.append(', ...'); - } - - this.append(' })'); - }; - - PrettyPrinter.prototype.emitTypedArray = function(arr) { - var constructorName = j$.fnNameFor(arr.constructor), - limitedArray = Array.prototype.slice.call( - arr, - 0, - j$.MAX_PRETTY_PRINT_ARRAY_LENGTH - ), - itemsString = Array.prototype.join.call(limitedArray, ', '); - - if (limitedArray.length !== arr.length) { - itemsString += ', ...'; - } - - this.append(constructorName + ' [ ' + itemsString + ' ]'); - }; - - PrettyPrinter.prototype.emitDomElement = function(el) { - var tagName = el.tagName.toLowerCase(), - attrs = el.attributes, - i, - len = attrs.length, - out = '<' + tagName, - attr; - - for (i = 0; i < len; i++) { - attr = attrs[i]; - out += ' ' + attr.name; - - if (attr.value !== '') { - out += '="' + attr.value + '"'; - } - } - - out += '>'; - - if (el.childElementCount !== 0 || el.textContent !== '') { - out += '...'; - } - - this.append(out); - }; - - PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { - this.append(property); - this.append(': '); - if (isGetter) { - this.append(''); - } else { - this.format(obj[property]); - } - }; - - PrettyPrinter.prototype.append = function(value) { - // This check protects us from the rare case where an object has overriden - // `toString()` with an invalid implementation (returning a non-string). - if (typeof value !== 'string') { - value = Object.prototype.toString.call(value); - } - - var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); - this.length += result.value.length; - this.stringParts.push(result.value); - - if (result.truncated) { - throw new MaxCharsReachedError(); - } - }; - - function truncate(s, maxlen) { - if (s.length <= maxlen) { - return { value: s, truncated: false }; - } - - s = s.substring(0, maxlen - 4) + ' ...'; - return { value: s, truncated: true }; - } - - function MaxCharsReachedError() { - this.message = - 'Exceeded ' + - j$.MAX_PRETTY_PRINT_CHARS + - ' characters while pretty-printing a value'; - } - - MaxCharsReachedError.prototype = new Error(); - - function keys(obj, isArray) { - var allKeys = Object.keys - ? Object.keys(obj) - : (function(o) { - var keys = []; - for (var key in o) { - if (j$.util.has(o, key)) { - keys.push(key); - } - } - return keys; - })(obj); - - if (!isArray) { - return allKeys; - } - - if (allKeys.length === 0) { - return allKeys; - } - - var extraKeys = []; - for (var i = 0; i < allKeys.length; i++) { - if (!/^[0-9]+$/.test(allKeys[i])) { - extraKeys.push(allKeys[i]); - } - } - - return extraKeys; - } - return function(value) { - var prettyPrinter = new PrettyPrinter(); - prettyPrinter.format(value); - return prettyPrinter.stringParts.join(''); - }; -}; - -getJasmineRequireObj().QueueRunner = function(j$) { - function StopExecutionError() {} - StopExecutionError.prototype = new Error(); - j$.StopExecutionError = StopExecutionError; - - function once(fn) { - var called = false; - return function(arg) { - if (!called) { - called = true; - // Direct call using single parameter, because cleanup/next does not need more - fn(arg); - } - return null; - }; - } - - function emptyFn() {} - - function QueueRunner(attrs) { - var queueableFns = attrs.queueableFns || []; - this.queueableFns = queueableFns.concat(attrs.cleanupFns || []); - this.firstCleanupIx = queueableFns.length; - this.onComplete = attrs.onComplete || emptyFn; - this.clearStack = - attrs.clearStack || - function(fn) { - fn(); - }; - this.onException = attrs.onException || emptyFn; - this.userContext = attrs.userContext || new j$.UserContext(); - this.timeout = attrs.timeout || { - setTimeout: setTimeout, - clearTimeout: clearTimeout - }; - this.fail = attrs.fail || emptyFn; - this.globalErrors = attrs.globalErrors || { - pushListener: emptyFn, - popListener: emptyFn - }; - this.completeOnFirstError = !!attrs.completeOnFirstError; - this.errored = false; - - if (typeof this.onComplete !== 'function') { - throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); - } - this.deprecated = attrs.deprecated; - } - - QueueRunner.prototype.execute = function() { - var self = this; - this.handleFinalError = function(message, source, lineno, colno, error) { - // Older browsers would send the error as the first parameter. HTML5 - // specifies the the five parameters above. The error instance should - // be preffered, otherwise the call stack would get lost. - self.onException(error || message); - }; - this.globalErrors.pushListener(this.handleFinalError); - this.run(0); - }; - - QueueRunner.prototype.skipToCleanup = function(lastRanIndex) { - if (lastRanIndex < this.firstCleanupIx) { - this.run(this.firstCleanupIx); - } else { - this.run(lastRanIndex + 1); - } - }; - - QueueRunner.prototype.clearTimeout = function(timeoutId) { - Function.prototype.apply.apply(this.timeout.clearTimeout, [ - j$.getGlobal(), - [timeoutId] - ]); - }; - - QueueRunner.prototype.setTimeout = function(fn, timeout) { - return Function.prototype.apply.apply(this.timeout.setTimeout, [ - j$.getGlobal(), - [fn, timeout] - ]); - }; - - QueueRunner.prototype.attempt = function attempt(iterativeIndex) { - var self = this, - completedSynchronously = true, - handleError = function handleError(error) { - onException(error); - next(error); - }, - cleanup = once(function cleanup() { - if (timeoutId !== void 0) { - self.clearTimeout(timeoutId); - } - self.globalErrors.popListener(handleError); - }), - next = once(function next(err) { - cleanup(); - - if (j$.isError_(err)) { - if (!(err instanceof StopExecutionError) && !err.jasmineMessage) { - self.fail(err); - } - self.errored = errored = true; - } - - function runNext() { - if (self.completeOnFirstError && errored) { - self.skipToCleanup(iterativeIndex); - } else { - self.run(iterativeIndex + 1); - } - } - - if (completedSynchronously) { - self.setTimeout(runNext); - } else { - runNext(); - } - }), - errored = false, - queueableFn = self.queueableFns[iterativeIndex], - timeoutId; - - next.fail = function nextFail() { - self.fail.apply(null, arguments); - self.errored = errored = true; - next(); - }; - - self.globalErrors.pushListener(handleError); - - if (queueableFn.timeout !== undefined) { - var timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; - timeoutId = self.setTimeout(function() { - var error = new Error( - 'Timeout - Async function did not complete within ' + - timeoutInterval + - 'ms ' + - (queueableFn.timeout - ? '(custom timeout)' - : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') - ); - onException(error); - next(); - }, timeoutInterval); - } - - try { - if (queueableFn.fn.length === 0) { - var maybeThenable = queueableFn.fn.call(self.userContext); - - if (maybeThenable && j$.isFunction_(maybeThenable.then)) { - maybeThenable.then(next, onPromiseRejection); - completedSynchronously = false; - return { completedSynchronously: false }; - } - } else { - queueableFn.fn.call(self.userContext, next); - completedSynchronously = false; - return { completedSynchronously: false }; - } - } catch (e) { - onException(e); - self.errored = errored = true; - } - - cleanup(); - return { completedSynchronously: true, errored: errored }; - - function onException(e) { - self.onException(e); - self.errored = errored = true; - } - - function onPromiseRejection(e) { - onException(e); - next(); - } - }; - - QueueRunner.prototype.run = function(recursiveIndex) { - var length = this.queueableFns.length, - self = this, - iterativeIndex; - - for ( - iterativeIndex = recursiveIndex; - iterativeIndex < length; - iterativeIndex++ - ) { - var result = this.attempt(iterativeIndex); - - if (!result.completedSynchronously) { - return; - } - - self.errored = self.errored || result.errored; - - if (this.completeOnFirstError && result.errored) { - this.skipToCleanup(iterativeIndex); - return; - } - } - - this.clearStack(function() { - self.globalErrors.popListener(self.handleFinalError); - self.onComplete(self.errored && new StopExecutionError()); - }); - }; - - return QueueRunner; -}; - -getJasmineRequireObj().ReportDispatcher = function(j$) { - function ReportDispatcher(methods, queueRunnerFactory) { - var dispatchedMethods = methods || []; - - for (var i = 0; i < dispatchedMethods.length; i++) { - var method = dispatchedMethods[i]; - this[method] = (function(m) { - return function() { - dispatch(m, arguments); - }; - })(method); - } - - var reporters = []; - var fallbackReporter = null; - - this.addReporter = function(reporter) { - reporters.push(reporter); - }; - - this.provideFallbackReporter = function(reporter) { - fallbackReporter = reporter; - }; - - this.clearReporters = function() { - reporters = []; - }; - - return this; - - function dispatch(method, args) { - if (reporters.length === 0 && fallbackReporter !== null) { - reporters.push(fallbackReporter); - } - var onComplete = args[args.length - 1]; - args = j$.util.argsToArray(args).splice(0, args.length - 1); - var fns = []; - for (var i = 0; i < reporters.length; i++) { - var reporter = reporters[i]; - addFn(fns, reporter, method, args); - } - - queueRunnerFactory({ - queueableFns: fns, - onComplete: onComplete, - isReporter: true - }); - } - - function addFn(fns, reporter, method, args) { - var fn = reporter[method]; - if (!fn) { - return; - } - - var thisArgs = j$.util.cloneArgs(args); - if (fn.length <= 1) { - fns.push({ - fn: function() { - return fn.apply(reporter, thisArgs); - } - }); - } else { - fns.push({ - fn: function(done) { - return fn.apply(reporter, thisArgs.concat([done])); - } - }); - } - } - } - - return ReportDispatcher; -}; - -getJasmineRequireObj().interface = function(jasmine, env) { - var jasmineInterface = { - /** - * Callback passed to parts of the Jasmine base interface. - * - * By default Jasmine assumes this function completes synchronously. - * If you have code that you need to test asynchronously, you can declare that you receive a `done` callback, return a Promise, or use the `async` keyword if it is supported in your environment. - * @callback implementationCallback - * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. - * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. - */ - - /** - * Create a group of specs (often called a suite). - * - * Calls to `describe` can be nested within other calls to compose your suite as a tree. - * @name describe - * @since 1.3.0 - * @function - * @global - * @param {String} description Textual description of the group - * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs - */ - describe: function(description, specDefinitions) { - return env.describe(description, specDefinitions); - }, - - /** - * A temporarily disabled [`describe`]{@link describe} - * - * Specs within an `xdescribe` will be marked pending and not executed - * @name xdescribe - * @since 1.3.0 - * @function - * @global - * @param {String} description Textual description of the group - * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs - */ - xdescribe: function(description, specDefinitions) { - return env.xdescribe(description, specDefinitions); - }, - - /** - * A focused [`describe`]{@link describe} - * - * If suites or specs are focused, only those that are focused will be executed - * @see fit - * @name fdescribe - * @since 2.1.0 - * @function - * @global - * @param {String} description Textual description of the group - * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs - */ - fdescribe: function(description, specDefinitions) { - return env.fdescribe(description, specDefinitions); - }, - - /** - * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code. - * - * A spec whose expectations all succeed will be passing and a spec with any failures will fail. - * @name it - * @since 1.3.0 - * @function - * @global - * @param {String} description Textual description of what this spec is checking - * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. - * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. - * @see async - */ - it: function() { - return env.it.apply(env, arguments); - }, - - /** - * A temporarily disabled [`it`]{@link it} - * - * The spec will report as `pending` and will not be executed. - * @name xit - * @since 1.3.0 - * @function - * @global - * @param {String} description Textual description of what this spec is checking. - * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. - */ - xit: function() { - return env.xit.apply(env, arguments); - }, - - /** - * A focused [`it`]{@link it} - * - * If suites or specs are focused, only those that are focused will be executed. - * @name fit - * @since 2.1.0 - * @function - * @global - * @param {String} description Textual description of what this spec is checking. - * @param {implementationCallback} testFunction Function that contains the code of your test. - * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. - * @see async - */ - fit: function() { - return env.fit.apply(env, arguments); - }, - - /** - * Run some shared setup before each of the specs in the {@link describe} in which it is called. - * @name beforeEach - * @since 1.3.0 - * @function - * @global - * @param {implementationCallback} [function] Function that contains the code to setup your specs. - * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. - * @see async - */ - beforeEach: function() { - return env.beforeEach.apply(env, arguments); - }, - - /** - * Run some shared teardown after each of the specs in the {@link describe} in which it is called. - * @name afterEach - * @since 1.3.0 - * @function - * @global - * @param {implementationCallback} [function] Function that contains the code to teardown your specs. - * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. - * @see async - */ - afterEach: function() { - return env.afterEach.apply(env, arguments); - }, - - /** - * Run some shared setup once before all of the specs in the {@link describe} are run. - * - * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. - * @name beforeAll - * @since 2.1.0 - * @function - * @global - * @param {implementationCallback} [function] Function that contains the code to setup your specs. - * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. - * @see async - */ - beforeAll: function() { - return env.beforeAll.apply(env, arguments); - }, - - /** - * Run some shared teardown once after all of the specs in the {@link describe} are run. - * - * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. - * @name afterAll - * @since 2.1.0 - * @function - * @global - * @param {implementationCallback} [function] Function that contains the code to teardown your specs. - * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. - * @see async - */ - afterAll: function() { - return env.afterAll.apply(env, arguments); - }, - - /** - * Create an expectation for a spec. - * @name expect - * @since 1.3.0 - * @function - * @global - * @param {Object} actual - Actual computed value to test expectations against. - * @return {matchers} - */ - expect: function(actual) { - return env.expect(actual); - }, - - /** - * Create an asynchronous expectation for a spec. Note that the matchers - * that are provided by an asynchronous expectation all return promises - * which must be either returned from the spec or waited for using `await` - * in order for Jasmine to associate them with the correct spec. - * @name expectAsync - * @since 3.3.0 - * @function - * @global - * @param {Object} actual - Actual computed value to test expectations against. - * @return {async-matchers} - * @example - * await expectAsync(somePromise).toBeResolved(); - * @example - * return expectAsync(somePromise).toBeResolved(); - */ - expectAsync: function(actual) { - return env.expectAsync(actual); - }, - - /** - * Mark a spec as pending, expectation results will be ignored. - * @name pending - * @since 2.0.0 - * @function - * @global - * @param {String} [message] - Reason the spec is pending. - */ - pending: function() { - return env.pending.apply(env, arguments); - }, - - /** - * Explicitly mark a spec as failed. - * @name fail - * @since 2.1.0 - * @function - * @global - * @param {String|Error} [error] - Reason for the failure. - */ - fail: function() { - return env.fail.apply(env, arguments); - }, - - /** - * Install a spy onto an existing object. - * @name spyOn - * @since 1.3.0 - * @function - * @global - * @param {Object} obj - The object upon which to install the {@link Spy}. - * @param {String} methodName - The name of the method to replace with a {@link Spy}. - * @returns {Spy} - */ - spyOn: function(obj, methodName) { - return env.spyOn(obj, methodName); - }, - - /** - * Install a spy on a property installed with `Object.defineProperty` onto an existing object. - * @name spyOnProperty - * @since 2.6.0 - * @function - * @global - * @param {Object} obj - The object upon which to install the {@link Spy} - * @param {String} propertyName - The name of the property to replace with a {@link Spy}. - * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. - * @returns {Spy} - */ - spyOnProperty: function(obj, methodName, accessType) { - return env.spyOnProperty(obj, methodName, accessType); - }, - - /** - * Installs spies on all writable and configurable properties of an object. - * @name spyOnAllFunctions - * @since 3.2.1 - * @function - * @global - * @param {Object} obj - The object upon which to install the {@link Spy}s - * @returns {Object} the spied object - */ - spyOnAllFunctions: function(obj) { - return env.spyOnAllFunctions(obj); - }, - - jsApiReporter: new jasmine.JsApiReporter({ - timer: new jasmine.Timer() - }), - - /** - * @namespace jasmine - */ - jasmine: jasmine - }; - - /** - * Add a custom equality tester for the current scope of specs. - * - * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. - * @name jasmine.addCustomEqualityTester - * @since 2.0.0 - * @function - * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise. - * @see custom_equality - */ - jasmine.addCustomEqualityTester = function(tester) { - env.addCustomEqualityTester(tester); - }; - - /** - * Add custom matchers for the current scope of specs. - * - * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. - * @name jasmine.addMatchers - * @since 2.0.0 - * @function - * @param {Object} matchers - Keys from this object will be the new matcher names. - * @see custom_matcher - */ - jasmine.addMatchers = function(matchers) { - return env.addMatchers(matchers); - }; - - /** - * Add custom async matchers for the current scope of specs. - * - * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. - * @name jasmine.addAsyncMatchers - * @since 3.5.0 - * @function - * @param {Object} matchers - Keys from this object will be the new async matcher names. - * @see custom_matcher - */ - jasmine.addAsyncMatchers = function(matchers) { - return env.addAsyncMatchers(matchers); - }; - - /** - * Get the currently booted mock {Clock} for this Jasmine environment. - * @name jasmine.clock - * @since 2.0.0 - * @function - * @returns {Clock} - */ - jasmine.clock = function() { - return env.clock; - }; - - /** - * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. - * @name jasmine.createSpy - * @since 1.3.0 - * @function - * @param {String} [name] - Name to give the spy. This will be displayed in failure messages. - * @param {Function} [originalFn] - Function to act as the real implementation. - * @return {Spy} - */ - jasmine.createSpy = function(name, originalFn) { - return env.createSpy(name, originalFn); - }; - - /** - * Create an object with multiple {@link Spy}s as its members. - * @name jasmine.createSpyObj - * @since 1.3.0 - * @function - * @param {String} [baseName] - Base name for the spies in the object. - * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}. - * @param {String[]|Object} [propertyNames] - Array of property names to create spies for, or Object whose keys will be propertynames and values the {@link Spy#and#returnValue|returnValue}. - * @return {Object} - */ - jasmine.createSpyObj = function(baseName, methodNames, propertyNames) { - return env.createSpyObj(baseName, methodNames, propertyNames); - }; - - /** - * Add a custom spy strategy for the current scope of specs. - * - * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. - * @name jasmine.addSpyStrategy - * @since 3.5.0 - * @function - * @param {String} name - The name of the strategy (i.e. what you call from `and`) - * @param {Function} factory - Factory function that returns the plan to be executed. - */ - jasmine.addSpyStrategy = function(name, factory) { - return env.addSpyStrategy(name, factory); - }; - - /** - * Set the default spy strategy for the current scope of specs. - * - * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. - * @name jasmine.setDefaultSpyStrategy - * @function - * @param {Function} defaultStrategyFn - a function that assigns a strategy - * @example - * beforeEach(function() { - * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); - * }); - */ - jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { - return env.setDefaultSpyStrategy(defaultStrategyFn); - }; - - return jasmineInterface; -}; - -getJasmineRequireObj().Spy = function(j$) { - var nextOrder = (function() { - var order = 0; - - return function() { - return order++; - }; - })(); - - /** - * _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj} - * @constructor - * @name Spy - */ - function Spy( - name, - originalFn, - customStrategies, - defaultStrategyFn, - getPromise - ) { - var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, - wrapper = makeFunc(numArgs, function() { - return spy.apply(this, Array.prototype.slice.call(arguments)); - }), - strategyDispatcher = new SpyStrategyDispatcher({ - name: name, - fn: originalFn, - getSpy: function() { - return wrapper; - }, - customStrategies: customStrategies, - getPromise: getPromise - }), - callTracker = new j$.CallTracker(), - spy = function() { - /** - * @name Spy.callData - * @property {object} object - `this` context for the invocation. - * @property {number} invocationOrder - Order of the invocation. - * @property {Array} args - The arguments passed for this invocation. - */ - var callData = { - object: this, - invocationOrder: nextOrder(), - args: Array.prototype.slice.apply(arguments) - }; - - callTracker.track(callData); - var returnValue = strategyDispatcher.exec(this, arguments); - callData.returnValue = returnValue; - - return returnValue; - }; - - function makeFunc(length, fn) { - switch (length) { - case 1: - return function(a) { - return fn.apply(this, arguments); - }; - case 2: - return function(a, b) { - return fn.apply(this, arguments); - }; - case 3: - return function(a, b, c) { - return fn.apply(this, arguments); - }; - case 4: - return function(a, b, c, d) { - return fn.apply(this, arguments); - }; - case 5: - return function(a, b, c, d, e) { - return fn.apply(this, arguments); - }; - case 6: - return function(a, b, c, d, e, f) { - return fn.apply(this, arguments); - }; - case 7: - return function(a, b, c, d, e, f, g) { - return fn.apply(this, arguments); - }; - case 8: - return function(a, b, c, d, e, f, g, h) { - return fn.apply(this, arguments); - }; - case 9: - return function(a, b, c, d, e, f, g, h, i) { - return fn.apply(this, arguments); - }; - default: - return function() { - return fn.apply(this, arguments); - }; - } - } - - for (var prop in originalFn) { - if (prop === 'and' || prop === 'calls') { - throw new Error( - "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon" - ); - } - - wrapper[prop] = originalFn[prop]; - } - - /** - * @member {SpyStrategy} - Accesses the default strategy for the spy. This strategy will be used - * whenever the spy is called with arguments that don't match any strategy - * created with {@link Spy#withArgs}. - * @name Spy#and - * @since 2.0.0 - * @example - * spyOn(someObj, 'func').and.returnValue(42); - */ - wrapper.and = strategyDispatcher.and; - /** - * Specifies a strategy to be used for calls to the spy that have the - * specified arguments. - * @name Spy#withArgs - * @since 3.0.0 - * @function - * @param {...*} args - The arguments to match - * @type {SpyStrategy} - * @example - * spyOn(someObj, 'func').withArgs(1, 2, 3).and.returnValue(42); - * someObj.func(1, 2, 3); // returns 42 - */ - wrapper.withArgs = function() { - return strategyDispatcher.withArgs.apply(strategyDispatcher, arguments); - }; - wrapper.calls = callTracker; - - if (defaultStrategyFn) { - defaultStrategyFn(wrapper.and); - } - - return wrapper; - } - - function SpyStrategyDispatcher(strategyArgs) { - var baseStrategy = new j$.SpyStrategy(strategyArgs); - var argsStrategies = new StrategyDict(function() { - return new j$.SpyStrategy(strategyArgs); - }); - - this.and = baseStrategy; - - this.exec = function(spy, args) { - var strategy = argsStrategies.get(args); - - if (!strategy) { - if (argsStrategies.any() && !baseStrategy.isConfigured()) { - throw new Error( - "Spy '" + - strategyArgs.name + - "' received a call with arguments " + - j$.pp(Array.prototype.slice.call(args)) + - ' but all configured strategies specify other arguments.' - ); - } else { - strategy = baseStrategy; - } - } - - return strategy.exec(spy, args); - }; - - this.withArgs = function() { - return { and: argsStrategies.getOrCreate(arguments) }; - }; - } - - function StrategyDict(strategyFactory) { - this.strategies = []; - this.strategyFactory = strategyFactory; - } - - StrategyDict.prototype.any = function() { - return this.strategies.length > 0; - }; - - StrategyDict.prototype.getOrCreate = function(args) { - var strategy = this.get(args); - - if (!strategy) { - strategy = this.strategyFactory(); - this.strategies.push({ - args: args, - strategy: strategy - }); - } - - return strategy; - }; - - StrategyDict.prototype.get = function(args) { - var i; - - for (i = 0; i < this.strategies.length; i++) { - if (j$.matchersUtil.equals(args, this.strategies[i].args)) { - return this.strategies[i].strategy; - } - } - }; - - return Spy; -}; - -getJasmineRequireObj().SpyFactory = function(j$) { - function SpyFactory(getCustomStrategies, getDefaultStrategyFn, getPromise) { - var self = this; - - this.createSpy = function(name, originalFn) { - return j$.Spy( - name, - originalFn, - getCustomStrategies(), - getDefaultStrategyFn(), - getPromise - ); - }; - - this.createSpyObj = function(baseName, methodNames, propertyNames) { - var baseNameIsCollection = - j$.isObject_(baseName) || j$.isArray_(baseName); - - if (baseNameIsCollection) { - propertyNames = methodNames; - methodNames = baseName; - baseName = 'unknown'; - } - - var obj = {}; - var spy, descriptor; - - var methods = normalizeKeyValues(methodNames); - for (var i = 0; i < methods.length; i++) { - spy = obj[methods[i][0]] = self.createSpy( - baseName + '.' + methods[i][0] - ); - if (methods[i].length > 1) { - spy.and.returnValue(methods[i][1]); - } - } - - var properties = normalizeKeyValues(propertyNames); - for (var i = 0; i < properties.length; i++) { - descriptor = { - get: self.createSpy(baseName + '.' + properties[i][0] + '.get'), - set: self.createSpy(baseName + '.' + properties[i][0] + '.set') - }; - if (properties[i].length > 1) { - descriptor.get.and.returnValue(properties[i][1]); - descriptor.set.and.returnValue(properties[i][1]); - } - Object.defineProperty(obj, properties[i][0], descriptor); - } - - if (methods.length === 0 && properties.length === 0) { - throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; - } - - return obj; - }; - } - - function normalizeKeyValues(object) { - var result = []; - if (j$.isArray_(object)) { - for (var i = 0; i < object.length; i++) { - result.push([object[i]]); - } - } else if (j$.isObject_(object)) { - for (var key in object) { - if (object.hasOwnProperty(key)) { - result.push([key, object[key]]); - } - } - } - return result; - } - - return SpyFactory; -}; - -getJasmineRequireObj().SpyRegistry = function(j$) { - var spyOnMsg = j$.formatErrorMsg('', 'spyOn(, )'); - var spyOnPropertyMsg = j$.formatErrorMsg( - '', - 'spyOnProperty(, , [accessType])' - ); - - function SpyRegistry(options) { - options = options || {}; - var global = options.global || j$.getGlobal(); - var createSpy = options.createSpy; - var currentSpies = - options.currentSpies || - function() { - return []; - }; - - this.allowRespy = function(allow) { - this.respy = allow; - }; - - this.spyOn = function(obj, methodName) { - var getErrorMsg = spyOnMsg; - - if (j$.util.isUndefined(obj) || obj === null) { - throw new Error( - getErrorMsg( - 'could not find an object to spy upon for ' + methodName + '()' - ) - ); - } - - if (j$.util.isUndefined(methodName) || methodName === null) { - throw new Error(getErrorMsg('No method name supplied')); - } - - if (j$.util.isUndefined(obj[methodName])) { - throw new Error(getErrorMsg(methodName + '() method does not exist')); - } - - if (obj[methodName] && j$.isSpy(obj[methodName])) { - if (this.respy) { - return obj[methodName]; - } else { - throw new Error( - getErrorMsg(methodName + ' has already been spied upon') - ); - } - } - - var descriptor = Object.getOwnPropertyDescriptor(obj, methodName); - - if (descriptor && !(descriptor.writable || descriptor.set)) { - throw new Error( - getErrorMsg(methodName + ' is not declared writable or has no setter') - ); - } - - var originalMethod = obj[methodName], - spiedMethod = createSpy(methodName, originalMethod), - restoreStrategy; - - if ( - Object.prototype.hasOwnProperty.call(obj, methodName) || - (obj === global && methodName === 'onerror') - ) { - restoreStrategy = function() { - obj[methodName] = originalMethod; - }; - } else { - restoreStrategy = function() { - if (!delete obj[methodName]) { - obj[methodName] = originalMethod; - } - }; - } - - currentSpies().push({ - restoreObjectToOriginalState: restoreStrategy - }); - - obj[methodName] = spiedMethod; - - return spiedMethod; - }; - - this.spyOnProperty = function(obj, propertyName, accessType) { - var getErrorMsg = spyOnPropertyMsg; - - accessType = accessType || 'get'; - - if (j$.util.isUndefined(obj)) { - throw new Error( - getErrorMsg( - 'spyOn could not find an object to spy upon for ' + - propertyName + - '' - ) - ); - } - - if (j$.util.isUndefined(propertyName)) { - throw new Error(getErrorMsg('No property name supplied')); - } - - var descriptor = j$.util.getPropertyDescriptor(obj, propertyName); - - if (!descriptor) { - throw new Error(getErrorMsg(propertyName + ' property does not exist')); - } - - if (!descriptor.configurable) { - throw new Error( - getErrorMsg(propertyName + ' is not declared configurable') - ); - } - - if (!descriptor[accessType]) { - throw new Error( - getErrorMsg( - 'Property ' + - propertyName + - ' does not have access type ' + - accessType - ) - ); - } - - if (j$.isSpy(descriptor[accessType])) { - if (this.respy) { - return descriptor[accessType]; - } else { - throw new Error( - getErrorMsg( - propertyName + '#' + accessType + ' has already been spied upon' - ) - ); - } - } - - var originalDescriptor = j$.util.clone(descriptor), - spy = createSpy(propertyName, descriptor[accessType]), - restoreStrategy; - - if (Object.prototype.hasOwnProperty.call(obj, propertyName)) { - restoreStrategy = function() { - Object.defineProperty(obj, propertyName, originalDescriptor); - }; - } else { - restoreStrategy = function() { - delete obj[propertyName]; - }; - } - - currentSpies().push({ - restoreObjectToOriginalState: restoreStrategy - }); - - descriptor[accessType] = spy; - - Object.defineProperty(obj, propertyName, descriptor); - - return spy; - }; - - this.spyOnAllFunctions = function(obj) { - if (j$.util.isUndefined(obj)) { - throw new Error( - 'spyOnAllFunctions could not find an object to spy upon' - ); - } - - var pointer = obj, - props = [], - prop, - descriptor; - - while (pointer) { - for (prop in pointer) { - if ( - Object.prototype.hasOwnProperty.call(pointer, prop) && - pointer[prop] instanceof Function - ) { - descriptor = Object.getOwnPropertyDescriptor(pointer, prop); - if ( - (descriptor.writable || descriptor.set) && - descriptor.configurable - ) { - props.push(prop); - } - } - } - pointer = Object.getPrototypeOf(pointer); - } - - for (var i = 0; i < props.length; i++) { - this.spyOn(obj, props[i]); - } - - return obj; - }; - - this.clearSpies = function() { - var spies = currentSpies(); - for (var i = spies.length - 1; i >= 0; i--) { - var spyEntry = spies[i]; - spyEntry.restoreObjectToOriginalState(); - } - }; - } - - return SpyRegistry; -}; - -getJasmineRequireObj().SpyStrategy = function(j$) { - /** - * @interface SpyStrategy - */ - function SpyStrategy(options) { - options = options || {}; - - var self = this; - - /** - * Get the identifying information for the spy. - * @name SpyStrategy#identity - * @since 3.0.0 - * @member - * @type {String} - */ - this.identity = options.name || 'unknown'; - this.originalFn = options.fn || function() {}; - this.getSpy = options.getSpy || function() {}; - this.plan = this._defaultPlan = function() {}; - - var k, - cs = options.customStrategies || {}; - for (k in cs) { - if (j$.util.has(cs, k) && !this[k]) { - this[k] = createCustomPlan(cs[k]); - } - } - - var getPromise = - typeof options.getPromise === 'function' - ? options.getPromise - : function() {}; - - var requirePromise = function(name) { - var Promise = getPromise(); - - if (!Promise) { - throw new Error( - name + - ' requires global Promise, or `Promise` configured with `jasmine.getEnv().configure()`' - ); - } - - return Promise; - }; - - /** - * Tell the spy to return a promise resolving to the specified value when invoked. - * @name SpyStrategy#resolveTo - * @since 3.5.0 - * @function - * @param {*} value The value to return. - */ - this.resolveTo = function(value) { - var Promise = requirePromise('resolveTo'); - self.plan = function() { - return Promise.resolve(value); - }; - return self.getSpy(); - }; - - /** - * Tell the spy to return a promise rejecting with the specified value when invoked. - * @name SpyStrategy#rejectWith - * @since 3.5.0 - * @function - * @param {*} value The value to return. - */ - this.rejectWith = function(value) { - var Promise = requirePromise('rejectWith'); - - self.plan = function() { - return Promise.reject(value); - }; - return self.getSpy(); - }; - } - - function createCustomPlan(factory) { - return function() { - var plan = factory.apply(null, arguments); - - if (!j$.isFunction_(plan)) { - throw new Error('Spy strategy must return a function'); - } - - this.plan = plan; - return this.getSpy(); - }; - } - - /** - * Execute the current spy strategy. - * @name SpyStrategy#exec - * @since 2.0.0 - * @function - */ - SpyStrategy.prototype.exec = function(context, args) { - return this.plan.apply(context, args); - }; - - /** - * Tell the spy to call through to the real implementation when invoked. - * @name SpyStrategy#callThrough - * @since 2.0.0 - * @function - */ - SpyStrategy.prototype.callThrough = function() { - this.plan = this.originalFn; - return this.getSpy(); - }; - - /** - * Tell the spy to return the value when invoked. - * @name SpyStrategy#returnValue - * @since 2.0.0 - * @function - * @param {*} value The value to return. - */ - SpyStrategy.prototype.returnValue = function(value) { - this.plan = function() { - return value; - }; - return this.getSpy(); - }; - - /** - * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked. - * @name SpyStrategy#returnValues - * @since 2.1.0 - * @function - * @param {...*} values - Values to be returned on subsequent calls to the spy. - */ - SpyStrategy.prototype.returnValues = function() { - var values = Array.prototype.slice.call(arguments); - this.plan = function() { - return values.shift(); - }; - return this.getSpy(); - }; - - /** - * Tell the spy to throw an error when invoked. - * @name SpyStrategy#throwError - * @since 2.0.0 - * @function - * @param {Error|String} something Thing to throw - */ - SpyStrategy.prototype.throwError = function(something) { - var error = something instanceof Error ? something : new Error(something); - this.plan = function() { - throw error; - }; - return this.getSpy(); - }; - - /** - * Tell the spy to call a fake implementation when invoked. - * @name SpyStrategy#callFake - * @since 2.0.0 - * @function - * @param {Function} fn The function to invoke with the passed parameters. - */ - SpyStrategy.prototype.callFake = function(fn) { - if (!(j$.isFunction_(fn) || j$.isAsyncFunction_(fn))) { - throw new Error( - 'Argument passed to callFake should be a function, got ' + fn - ); - } - this.plan = fn; - return this.getSpy(); - }; - - /** - * Tell the spy to do nothing when invoked. This is the default. - * @name SpyStrategy#stub - * @since 2.0.0 - * @function - */ - SpyStrategy.prototype.stub = function(fn) { - this.plan = function() {}; - return this.getSpy(); - }; - - SpyStrategy.prototype.isConfigured = function() { - return this.plan !== this._defaultPlan; - }; - - return SpyStrategy; -}; - -getJasmineRequireObj().StackTrace = function(j$) { - function StackTrace(error) { - var lines = error.stack.split('\n').filter(function(line) { - return line !== ''; - }); - - var extractResult = extractMessage(error.message, lines); - - if (extractResult) { - this.message = extractResult.message; - lines = extractResult.remainder; - } - - var parseResult = tryParseFrames(lines); - this.frames = parseResult.frames; - this.style = parseResult.style; - } - - var framePatterns = [ - // PhantomJS on Linux, Node, Chrome, IE, Edge - // e.g. " at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)" - // Note that the "function name" can include a surprisingly large set of - // characters, including angle brackets and square brackets. - { - re: /^\s*at ([^\)]+) \(([^\)]+)\)$/, - fnIx: 1, - fileLineColIx: 2, - style: 'v8' - }, - - // NodeJS alternate form, often mixed in with the Chrome style - // e.g. " at /some/path:4320:20 - { re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' }, - - // PhantomJS on OS X, Safari, Firefox - // e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27" - // or "http://localhost:8888/__jasmine__/jasmine.js:4320:27" - { - re: /^(([^@\s]+)@)?([^\s]+)$/, - fnIx: 2, - fileLineColIx: 3, - style: 'webkit' - } - ]; - - // regexes should capture the function name (if any) as group 1 - // and the file, line, and column as group 2. - function tryParseFrames(lines) { - var style = null; - var frames = lines.map(function(line) { - var convertedLine = first(framePatterns, function(pattern) { - var overallMatch = line.match(pattern.re), - fileLineColMatch; - if (!overallMatch) { - return null; - } - - fileLineColMatch = overallMatch[pattern.fileLineColIx].match( - /^(.*):(\d+):\d+$/ - ); - if (!fileLineColMatch) { - return null; - } - - style = style || pattern.style; - return { - raw: line, - file: fileLineColMatch[1], - line: parseInt(fileLineColMatch[2], 10), - func: overallMatch[pattern.fnIx] - }; - }); - - return convertedLine || { raw: line }; - }); - - return { - style: style, - frames: frames - }; - } - - function first(items, fn) { - var i, result; - - for (i = 0; i < items.length; i++) { - result = fn(items[i]); - - if (result) { - return result; - } - } - } - - function extractMessage(message, stackLines) { - var len = messagePrefixLength(message, stackLines); - - if (len > 0) { - return { - message: stackLines.slice(0, len).join('\n'), - remainder: stackLines.slice(len) - }; - } - } - - function messagePrefixLength(message, stackLines) { - if (!stackLines[0].match(/^Error/)) { - return 0; - } - - var messageLines = message.split('\n'); - var i; - - for (i = 1; i < messageLines.length; i++) { - if (messageLines[i] !== stackLines[i]) { - return 0; - } - } - - return messageLines.length; - } - - return StackTrace; -}; - -getJasmineRequireObj().Suite = function(j$) { - function Suite(attrs) { - this.env = attrs.env; - this.id = attrs.id; - this.parentSuite = attrs.parentSuite; - this.description = attrs.description; - this.expectationFactory = attrs.expectationFactory; - this.asyncExpectationFactory = attrs.asyncExpectationFactory; - this.expectationResultFactory = attrs.expectationResultFactory; - this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; - - this.beforeFns = []; - this.afterFns = []; - this.beforeAllFns = []; - this.afterAllFns = []; - - this.timer = attrs.timer || j$.noopTimer; - - this.children = []; - - /** - * @typedef SuiteResult - * @property {Int} id - The unique id of this suite. - * @property {String} description - The description text passed to the {@link describe} that made this suite. - * @property {String} fullName - The full description including all ancestors of this suite. - * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. - * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. - * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. - * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. - */ - this.result = { - id: this.id, - description: this.description, - fullName: this.getFullName(), - failedExpectations: [], - deprecationWarnings: [], - duration: null - }; - } - - Suite.prototype.expect = function(actual) { - return this.expectationFactory(actual, this); - }; - - Suite.prototype.expectAsync = function(actual) { - return this.asyncExpectationFactory(actual, this); - }; - - Suite.prototype.getFullName = function() { - var fullName = []; - for ( - var parentSuite = this; - parentSuite; - parentSuite = parentSuite.parentSuite - ) { - if (parentSuite.parentSuite) { - fullName.unshift(parentSuite.description); - } - } - return fullName.join(' '); - }; - - Suite.prototype.pend = function() { - this.markedPending = true; - }; - - Suite.prototype.beforeEach = function(fn) { - this.beforeFns.unshift(fn); - }; - - Suite.prototype.beforeAll = function(fn) { - this.beforeAllFns.push(fn); - }; - - Suite.prototype.afterEach = function(fn) { - this.afterFns.unshift(fn); - }; - - Suite.prototype.afterAll = function(fn) { - this.afterAllFns.unshift(fn); - }; - - Suite.prototype.startTimer = function() { - this.timer.start(); - }; - - Suite.prototype.endTimer = function() { - this.result.duration = this.timer.elapsed(); - }; - - function removeFns(queueableFns) { - for (var i = 0; i < queueableFns.length; i++) { - queueableFns[i].fn = null; - } - } - - Suite.prototype.cleanupBeforeAfter = function() { - removeFns(this.beforeAllFns); - removeFns(this.afterAllFns); - removeFns(this.beforeFns); - removeFns(this.afterFns); - }; - - Suite.prototype.addChild = function(child) { - this.children.push(child); - }; - - Suite.prototype.status = function() { - if (this.markedPending) { - return 'pending'; - } - - if (this.result.failedExpectations.length > 0) { - return 'failed'; - } else { - return 'passed'; - } - }; - - Suite.prototype.canBeReentered = function() { - return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; - }; - - Suite.prototype.getResult = function() { - this.result.status = this.status(); - return this.result; - }; - - Suite.prototype.sharedUserContext = function() { - if (!this.sharedContext) { - this.sharedContext = this.parentSuite - ? this.parentSuite.clonedSharedUserContext() - : new j$.UserContext(); - } - - return this.sharedContext; - }; - - Suite.prototype.clonedSharedUserContext = function() { - return j$.UserContext.fromExisting(this.sharedUserContext()); - }; - - Suite.prototype.onException = function() { - if (arguments[0] instanceof j$.errors.ExpectationFailed) { - return; - } - - var data = { - matcherName: '', - passed: false, - expected: '', - actual: '', - error: arguments[0] - }; - var failedExpectation = this.expectationResultFactory(data); - - if (!this.parentSuite) { - failedExpectation.globalErrorType = 'afterAll'; - } - - this.result.failedExpectations.push(failedExpectation); - }; - - Suite.prototype.addExpectationResult = function() { - if (isFailure(arguments)) { - var data = arguments[1]; - this.result.failedExpectations.push(this.expectationResultFactory(data)); - if (this.throwOnExpectationFailure) { - throw new j$.errors.ExpectationFailed(); - } - } - }; - - Suite.prototype.addDeprecationWarning = function(deprecation) { - if (typeof deprecation === 'string') { - deprecation = { message: deprecation }; - } - this.result.deprecationWarnings.push( - this.expectationResultFactory(deprecation) - ); - }; - - function isFailure(args) { - return !args[0]; - } - - return Suite; -}; - -if (typeof window == void 0 && typeof exports == 'object') { - /* globals exports */ - exports.Suite = jasmineRequire.Suite; -} - -getJasmineRequireObj().Timer = function() { - var defaultNow = (function(Date) { - return function() { - return new Date().getTime(); - }; - })(Date); - - function Timer(options) { - options = options || {}; - - var now = options.now || defaultNow, - startTime; - - this.start = function() { - startTime = now(); - }; - - this.elapsed = function() { - return now() - startTime; - }; - } - - return Timer; -}; - -getJasmineRequireObj().noopTimer = function() { - return { - start: function() {}, - elapsed: function() { - return 0; - } - }; -}; - -getJasmineRequireObj().TreeProcessor = function() { - function TreeProcessor(attrs) { - var tree = attrs.tree, - runnableIds = attrs.runnableIds, - queueRunnerFactory = attrs.queueRunnerFactory, - nodeStart = attrs.nodeStart || function() {}, - nodeComplete = attrs.nodeComplete || function() {}, - failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations, - orderChildren = - attrs.orderChildren || - function(node) { - return node.children; - }, - excludeNode = - attrs.excludeNode || - function(node) { - return false; - }, - stats = { valid: true }, - processed = false, - defaultMin = Infinity, - defaultMax = 1 - Infinity; - - this.processTree = function() { - processNode(tree, true); - processed = true; - return stats; - }; - - this.execute = function(done) { - if (!processed) { - this.processTree(); - } - - if (!stats.valid) { - throw 'invalid order'; - } - - var childFns = wrapChildren(tree, 0); - - queueRunnerFactory({ - queueableFns: childFns, - userContext: tree.sharedUserContext(), - onException: function() { - tree.onException.apply(tree, arguments); - }, - onComplete: done - }); - }; - - function runnableIndex(id) { - for (var i = 0; i < runnableIds.length; i++) { - if (runnableIds[i] === id) { - return i; - } - } - } - - function processNode(node, parentExcluded) { - var executableIndex = runnableIndex(node.id); - - if (executableIndex !== undefined) { - parentExcluded = false; - } - - if (!node.children) { - var excluded = parentExcluded || excludeNode(node); - stats[node.id] = { - excluded: excluded, - willExecute: !excluded && !node.markedPending, - segments: [ - { - index: 0, - owner: node, - nodes: [node], - min: startingMin(executableIndex), - max: startingMax(executableIndex) - } - ] - }; - } else { - var hasExecutableChild = false; - - var orderedChildren = orderChildren(node); - - for (var i = 0; i < orderedChildren.length; i++) { - var child = orderedChildren[i]; - - processNode(child, parentExcluded); - - if (!stats.valid) { - return; - } - - var childStats = stats[child.id]; - - hasExecutableChild = hasExecutableChild || childStats.willExecute; - } - - stats[node.id] = { - excluded: parentExcluded, - willExecute: hasExecutableChild - }; - - segmentChildren(node, orderedChildren, stats[node.id], executableIndex); - - if (!node.canBeReentered() && stats[node.id].segments.length > 1) { - stats = { valid: false }; - } - } - } - - function startingMin(executableIndex) { - return executableIndex === undefined ? defaultMin : executableIndex; - } - - function startingMax(executableIndex) { - return executableIndex === undefined ? defaultMax : executableIndex; - } - - function segmentChildren( - node, - orderedChildren, - nodeStats, - executableIndex - ) { - var currentSegment = { - index: 0, - owner: node, - nodes: [], - min: startingMin(executableIndex), - max: startingMax(executableIndex) - }, - result = [currentSegment], - lastMax = defaultMax, - orderedChildSegments = orderChildSegments(orderedChildren); - - function isSegmentBoundary(minIndex) { - return ( - lastMax !== defaultMax && - minIndex !== defaultMin && - lastMax < minIndex - 1 - ); - } - - for (var i = 0; i < orderedChildSegments.length; i++) { - var childSegment = orderedChildSegments[i], - maxIndex = childSegment.max, - minIndex = childSegment.min; - - if (isSegmentBoundary(minIndex)) { - currentSegment = { - index: result.length, - owner: node, - nodes: [], - min: defaultMin, - max: defaultMax - }; - result.push(currentSegment); - } - - currentSegment.nodes.push(childSegment); - currentSegment.min = Math.min(currentSegment.min, minIndex); - currentSegment.max = Math.max(currentSegment.max, maxIndex); - lastMax = maxIndex; - } - - nodeStats.segments = result; - } - - function orderChildSegments(children) { - var specifiedOrder = [], - unspecifiedOrder = []; - - for (var i = 0; i < children.length; i++) { - var child = children[i], - segments = stats[child.id].segments; - - for (var j = 0; j < segments.length; j++) { - var seg = segments[j]; - - if (seg.min === defaultMin) { - unspecifiedOrder.push(seg); - } else { - specifiedOrder.push(seg); - } - } - } - - specifiedOrder.sort(function(a, b) { - return a.min - b.min; - }); - - return specifiedOrder.concat(unspecifiedOrder); - } - - function executeNode(node, segmentNumber) { - if (node.children) { - return { - fn: function(done) { - var onStart = { - fn: function(next) { - nodeStart(node, next); - } - }; - - queueRunnerFactory({ - onComplete: function() { - var args = Array.prototype.slice.call(arguments, [0]); - node.cleanupBeforeAfter(); - nodeComplete(node, node.getResult(), function() { - done.apply(undefined, args); - }); - }, - queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), - userContext: node.sharedUserContext(), - onException: function() { - node.onException.apply(node, arguments); - } - }); - } - }; - } else { - return { - fn: function(done) { - node.execute( - done, - stats[node.id].excluded, - failSpecWithNoExpectations - ); - } - }; - } - } - - function wrapChildren(node, segmentNumber) { - var result = [], - segmentChildren = stats[node.id].segments[segmentNumber].nodes; - - for (var i = 0; i < segmentChildren.length; i++) { - result.push( - executeNode(segmentChildren[i].owner, segmentChildren[i].index) - ); - } - - if (!stats[node.id].willExecute) { - return result; - } - - return node.beforeAllFns.concat(result).concat(node.afterAllFns); - } - } - - return TreeProcessor; -}; - -getJasmineRequireObj().UserContext = function(j$) { - function UserContext() {} - - UserContext.fromExisting = function(oldContext) { - var context = new UserContext(); - - for (var prop in oldContext) { - if (oldContext.hasOwnProperty(prop)) { - context[prop] = oldContext[prop]; - } - } - - return context; - }; - - return UserContext; -}; - -getJasmineRequireObj().version = function() { - return '3.5.0'; -}; diff --git a/lib/jasmine-3.5.0/jasmine_favicon.png b/lib/jasmine-3.5.0/jasmine_favicon.png deleted file mode 100644 index 3b84583be4b9d5ae9cd5cae07b2dbaa5ebb0ad1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1486 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0VDb;}332@o1PuQh8X8uGu4-^- zn3*=SA+%wV=cI;&hMB$%-Lc(MLmN8%IwwUpbcA;F2Q;)twND9bn-tpC9oR4-vb8I; zp+Bg#FSKP+P(yD-%f!%zo}iZgh=%Ua=KkP@iNVbiLYsSn8~VeW`+}P$1~&ACHcbd_ z=nZb_32o{NZkZ6&1k^hrxT!a&r8lIdAIJ@9nh?|iRMsET+#A%`AKKg-(%2K)*cZ|~ zA-J&*$PEUH08MM`4Q=iVY3vVfhN$TUscGs5sR8P31X|G_+SnTcv<0XVC=Rr!u|K4# zHyCJU6UYRR;%2BtKv^I=q#2|DECn_k@wXU|=@ zeD&J(8#iy=zH|5fgNKiwJbm`!<*V0k-oF3v@zdvTKYsrD^Y>rGdNDU(R>|^oaSW-5 z%f0Y2x+hSk{p0&HA;E60Oq1S3r7U;(X3MH#?W9nxXzk)srlC4P;cZYe&v*G}mgi%< ze(bTl{@&_)cX8b-kvX%ff9D22e*M~7edm|;|Jb8m2JBzkzKiSBKR0n*?XSyM6fxZY zJ4bb;vGKRiKP(ztq3sJx9iq$Re`-EEQU0KE$sdyqayzp6H-7x+oGKZ_6;hQ_Y|p&y z7o`N7g@ z8Yl1HKL;mFo4BdxljF1}KbfMR6nNdLyPnEap1MqBdI?{g$L58$P8+wftLX5R70)7n`#pwItnbma$w{%`M(`wpsu2rJ60rdGCc> z+~lm)viMYM`=uXwK_$`#_UtRZMO=2-IV)l2vc~)doyPvg-RmxtACQ#v{7}5$&isV+ zPws~n?@yNGy#H1=vTI?;CAl9~9#VgqJ+7`4y~R+Wyj0U+){?lm9r6mtf0gl_VmUW^ z%eje%?jM%^eLuUg#ybXaQM{yQy&8UgiQ`HQ+=iTpjfd0iJ@ ssyFi&dF4%9dEry;pNN)Q>$jg_dr3r;PHA*CFc&d+y85}Sb4q9e0J812W&i*H diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..13566cd --- /dev/null +++ b/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "Werewolf Utility", + "short_name": "Werewolf", + "icons": [ + { + "src": "/client/favicon_package/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/client/favicon_package/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#000000", + "background_color": "#000000", + "start_url": "/?source=pwa", + "display": "fullscreen", + "display_override": ["fullscreen", "standalone", "minimal-ui"], + + "description": "A utility to deal Werewolf cards and run games in any setting, on any device." +} diff --git a/package.json b/package.json index 990b59f..3b48b62 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,10 @@ "description": "", "main": "index.js", "scripts": { - "test": "jasmine" + "start": "node server/main.js", + "test": "jasmine && node browsertest.js openBrowser socket", + "test:unit": "jasmine", + "test:e2e": "node browsertest.js" }, "author": "", "license": "ISC", diff --git a/server-helper.js b/server-helper.js deleted file mode 100644 index 2b7a0db..0000000 --- a/server-helper.js +++ /dev/null @@ -1,206 +0,0 @@ -const debugMode = Array.from(process.argv.map( (arg)=>arg.trim().toLowerCase() )).includes("debug"); -const LOGGER = require("./javascript/modules/logger")(debugMode); - -module.exports = class { - - constructor(CronJob) { - // TODO: do better than a plain object - this.activeGames = {}; - this.timers = {}; - - // cron job for periodically clearing finished games - const scope = this; - this.job = new CronJob('0 0 */2 * * *', function() { - console.log(scope.activeGames); - for (const key in scope.activeGames) { - if (scope.activeGames.hasOwnProperty(key) && (Math.abs((new Date()) - (new Date(scope.activeGames[key].startTime))) / 36e5) >= 2) { - delete scope.activeGames[key]; - } - } - console.log("Games pruned at: " + (new Date().toDateString()) + " " + (new Date()).toTimeString()); - }); - console.log("cron job created"); - this.job.start(); - } - - killPlayer(id, code) { - let game = this.findGame(code); - if (game) { - let player = game.players.find((player) => player.id === id); - if (player) { - player.dead = true; - player.deadAt = new Date().toJSON(); - game.killedPlayer = player.name; - game.lastKilled = player.id; - game.killedRole = player.card.role; - game.message = game.reveals - ? player.name + ", a " + player.card.role + ", was killed!" - : player.name + " has died!"; - console.log(game.message); - if (player.card.isTypeOfWerewolf && game.hasDreamWolf) { - this.activateDreamWolvesIfNeeded(game); - } - const winCheck = module.exports.teamWon(game); - if (winCheck === "wolf") { - console.log("wolves won the game!"); - game.winningTeam = "wolf"; - game.status = "ended"; - } - if (winCheck === "village") { - console.log("village won the game!"); - game.winningTeam = "village"; - game.status = "ended"; - } - } - } - } - - endGameDueToTimeExpired(code) { - if (this.timers[code]) { - clearInterval(this.timers[code]); - } - let game = this.findGame(code); - if (game) { - LOGGER.debug("Game " + code + " has ended due to expired timer."); - game.winningTeam = "wolf"; - game.status = "ended"; - } - } - - clearGameTimer(code) { - if (this.timers[code]) { - clearInterval(this.timers[code]); - LOGGER.debug("game paused and timer cleared for " + code); - } - } - - resumeGame(code) { - let game = this.findGame(code); - if (game) { - game.paused = false; - let newTime = new Date(game.endTime).getTime() + (new Date().getTime() - new Date(game.pauseTime).getTime()); - let newDate = new Date(game.endTime); - newDate.setTime(newTime); - game.endTime = newDate.toJSON(); - LOGGER.debug("Game " + code + " timer has been unpaused, starting clock anew..."); - this.startGameClock(code, newDate - Date.now()); - } - } - - pauseGame(code) { - let game = this.findGame(code); - if (game) { - game.pauseTime = (new Date()).toJSON(); - game.paused = true; - this.clearGameTimer(code); - } - } - - startGameClock(code, time) { - LOGGER.debug("timer started for game " + code); - let moduleScope = this; - if (this.timers[code]) { - clearInterval(this.timers[code]); - } - this.timers[code] = setInterval(function() { - moduleScope.endGameDueToTimeExpired(code) - }, time); - } - - startGame(gameData) { - let game = this.findGame(gameData.code); - if (game) { - LOGGER.debug("game " + gameData.code + " started"); - game.status = "started"; - game.players = gameData.players; - if (game.time) { - let d = new Date(); - d.setMinutes(d.getMinutes() + parseInt(game.time)); - game.endTime = d.toJSON(); - this.startGameClock(gameData.code, game.time * 60 * 1000); // provided time is in minutes - } - } - } - - requestState(data, socket) { - const game = this.findGame(data.code); - if (game && Object.keys(socket.rooms).includes(data.code) === false) { - socket.join(data.code, function() { - socket.emit('state', game); - }); - } else { - if (game) { - socket.emit('state', game); - } - } - } - - joinGame(playerInfo, socket) { - - const game = this.findGame(playerInfo.code); - if (game && game.players.length < game.size && !game.players.find((player) => player.id === playerInfo.id)) { - game.players.push({name: playerInfo.name, id: playerInfo.id}); - console.log(playerInfo.name + " joined the game!"); - socket.emit('success'); - } else { - if (game && game.players.length === game.size) { - socket.emit("joinError", "This game is full - sorry!"); - } else { - socket.emit("joinError", "No game found"); - } - } - } - - newGame(game, onSuccess) { - this.activeGames[game.accessCode] = game; - this.activeGames[game.accessCode].startTime = (new Date()).toJSON(); - console.log("Game created at " + (new Date().toDateString()) + " " + (new Date()).toTimeString()); - onSuccess(); - } - - findGame(code) { - return this.activeGames[Object.keys(this.activeGames).find((key) => key === code)]; - } - - // If there are multiple dream wolves, convert them all. - activateDreamWolvesIfNeeded(game) { - game.players.forEach((player) => { - if (!player.dead && player.card.role === "Dream Wolf") { - player.card.isTypeOfWerewolf = true; - console.log("player " + player.name + " was converted to a wolf!"); - } - }) - } - - static teamWon(game) { - let wolvesAlive = 0; - let villagersAlive = 0; - let totalAlive = 0; - let hunterAlive = false; - for (const player of game.players) { - if (!player.card.isTypeOfWerewolf && !player.dead) { - villagersAlive ++; - } - if (player.card.isTypeOfWerewolf && !player.dead) { - wolvesAlive ++; - } - if (player.card.role === "Hunter" && !player.dead) { - hunterAlive = true; - } - if (!player.dead) { - totalAlive ++; - } - } - if (wolvesAlive === 0) { - return "village" - } - if ((wolvesAlive >= villagersAlive) && (totalAlive !== 2)) { - return "wolf"; - } - if (totalAlive === 2) { - return hunterAlive ? "village" : "wolf" - } - return false; - } -}; - diff --git a/server.js b/server.js deleted file mode 100644 index ae6f96f..0000000 --- a/server.js +++ /dev/null @@ -1,79 +0,0 @@ -// Dependencies -const express = require('express'); -const http = require('http'); -const socketIO = require('socket.io'); -const app = express(); -const server = http.Server(app); -const io = socketIO(server); -const ServerHelper = require('./server-helper.js'); -const secure = require('express-force-https'); -app.use(secure); - -// Link websocket interaction functions, separated to aid testing -const CronJob = require('cron').CronJob; -const serverHelper = new ServerHelper(CronJob); - -const debugMode = Array.from(process.argv.map( (arg)=>arg.trim().toLowerCase() )).includes("debug"); -const LOGGER = require("./javascript/modules/logger")(debugMode); - -app.set('port', 5000); - -app.use('/javascript', express.static(__dirname + '/javascript')); // Routing -app.use('/assets', express.static(__dirname + '/assets')); // Routing -app.use('/stylesheets', express.static(__dirname + '/stylesheets')); // Routing -app.use('/node_modules/socket.io-client', express.static(__dirname + '/node_modules/socket.io-client')); // Routing -app.get('', function(request, response) { - response.sendFile(__dirname + '/views/index.html'); -}); - -app.get('/learn', function(request, response) { - response.sendFile(__dirname + '/views/learn.html'); -}); - -app.get('/faq', function(request, response) { - response.sendFile(__dirname + '/views/faq.html'); -}); - -app.get('/create', function(request, response) { - response.sendFile(__dirname + '/views/create_game.html'); -}); - -app.get('/join', function(request, response) { - response.sendFile(__dirname + '/views/join_game.html'); -}); - -app.get('/:code', function(request, response) { - response.sendFile(__dirname + '/views/game.html'); -}); - -// Starts the server. -server.listen(process.env.PORT || 5000, function() { - console.log('Starting server on port 5000'); -}); - -// Add the WebSocket handlers -io.on('connection', function(socket) { - socket.on('newGame', function(game, onSuccess) { - serverHelper.newGame(game, onSuccess); - }); - socket.on('joinGame', function(playerInfo) { - serverHelper.joinGame(playerInfo, socket); - }); - // send the game state to the client that requested it - socket.on('requestState', function(data) { - serverHelper.requestState(data, socket); - }); - socket.on('startGame', function(gameData) { - serverHelper.startGame(gameData); - }); - socket.on('pauseGame', function(code) { - serverHelper.pauseGame(code); - }); - socket.on('resumeGame', function(code) { - serverHelper.resumeGame(code); - }); - socket.on('killPlayer', function(id, code) { - serverHelper.killPlayer(id, code); - }); -}); - diff --git a/server/main.js b/server/main.js new file mode 100644 index 0000000..4a494b6 --- /dev/null +++ b/server/main.js @@ -0,0 +1,94 @@ +const express = require('express'); +const http = require('http'); +const https = require('https'); +const path = require('path'); +const fs = require('fs'); +const socketIO = require('socket.io'); +const app = express(); +let main; +const bodyParser = require('body-parser'); +// const GameManager = require('./modules/managers/GameManager.js'); +// const QueueManager = require('./modules/managers/QueueManager'); + +app.use(bodyParser.json()); // to support JSON-encoded bodies +app.use(bodyParser.urlencoded({ // to support URL-encoded bodies + extended: true +})); + +const debugMode = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())).includes('debug'); +const localServer = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())).includes('local'); +const useHttps = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())).includes('https'); +const port = process.env.PORT || Array + .from(process.argv.map((arg) => { + return arg.trim().toLowerCase(); + })) + .filter((arg) => { + return /port=\d+/.test(arg); + }) + .map((arg) => { + return /port=(\d+)/.exec(arg)[1]; + })[0] || 5000; +const logger = require('./modules/Logger')(debugMode); + +if (localServer) { + logger.log('starting main in LOCAL mode.'); + if (useHttps && fs.existsSync(path.join(__dirname, './certs/localhost-key.pem')) && fs.existsSync(path.join(__dirname, './certs/localhost.pem'))) { + const key = fs.readFileSync(path.join(__dirname, './certs/localhost-key.pem'), 'utf-8'); + const cert = fs.readFileSync(path.join(__dirname, './certs/localhost.pem'), 'utf-8'); + logger.log('local certs detected. Using HTTPS.'); + main = https.createServer({ key, cert }, app); + logger.log(`navigate to https://localhost:${port}`); + } else { + logger.log('https not specified or no local certs detected. Using HTTP.'); + main = http.createServer(app); + logger.log(`navigate to http://localhost:${port}`); + } +} else { + logger.log('starting main in PRODUCTION mode. WARNING: This should not be used for local development.'); + main = http.createServer(app); + const secure = require('express-force-https'); + app.use(secure); +} + +const io = socketIO(main); + +app.set('port', port); + +/* Instantiate the singleton game manager */ +//const gameManager = new GameManager(logger).getInstance(); + +/* Instantiate the singleton queue manager */ +//const queueManager = new QueueManager(matchmaking, logger).getInstance(); + + +/* api endpoints */ +// const games = require('./api/GamesAPI'); +// const words = require('./api/WordsAPI'); +// app.use('/api/games', games); +// app.use('/api/words', words); + +/* serve all the app's pages */ +app.use('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, '../manifest.json')); +}); + +app.use('/favicon.ico', (req, res) => { + res.sendFile(path.join(__dirname, '../client/favicon_package/favicon.ico')); +}); + +const router = require('./routes/router'); +const faviconRouter = require('./routes/favicon-router'); +const staticRouter = require('./routes/static-router'); + +app.use('', router); +app.use('', staticRouter); +app.use('', faviconRouter); + +app.use(function (req, res) { + res.sendFile(path.join(__dirname, '../client/views/404.html')); +}); + +// Starts the main. +main.listen(port, function () { + logger.log(`Starting server on port ${port} http://localhost:${port}` ); +}); diff --git a/javascript/modules/logger.js b/server/modules/Logger.js similarity index 76% rename from javascript/modules/logger.js rename to server/modules/Logger.js index c49a1f4..62a30ba 100644 --- a/javascript/modules/logger.js +++ b/server/modules/Logger.js @@ -1,17 +1,17 @@ -module.exports = function(debugMode = false){ +module.exports = function (debugMode = false) { return { - log(message = "") { + log (message = '') { const now = new Date(); console.log('LOG ', now.toGMTString(), ': ', message); }, - debug(message = "") { + debug (message = '') { if (!debugMode) return; const now = new Date(); console.debug('DEBUG ', now.toGMTString(), ': ', message); }, - error(message = "") { + error (message = '') { if (!debugMode) return; const now = new Date(); console.error('ERROR ', now.toGMTString(), ': ', message); diff --git a/server/routes/favicon-router.js b/server/routes/favicon-router.js new file mode 100644 index 0000000..bb101f0 --- /dev/null +++ b/server/routes/favicon-router.js @@ -0,0 +1,18 @@ +const express = require('express'); +const faviconRouter = express.Router(); +const path = require('path'); +const checkIfFileExists = require("./util"); + +faviconRouter.use('/client/favicon_package/*', (req, res) => { + let filePath = path.join(__dirname, ('../../' + req.baseUrl)); + let extension = path.extname(filePath); + checkIfFileExists(filePath).then((fileExists) => { + if (fileExists && (extension === '.png' || extension === '.ico' || extension === '.svg' || extension === 'xml')) { + res.sendFile(filePath); + } else { + res.sendStatus(404); + } + }); +}); + +module.exports = faviconRouter; diff --git a/server/routes/router.js b/server/routes/router.js new file mode 100644 index 0000000..e523e23 --- /dev/null +++ b/server/routes/router.js @@ -0,0 +1,13 @@ +const express = require('express'); +const router = express.Router(); +const path = require('path'); + +router.get('/', function (request, response) { + response.sendFile(path.join(__dirname, '../../client/views/home.html')); +}); + +router.get('/create', function (request, response) { + response.sendFile(path.join(__dirname, '../../client/views/create.html')); +}); + +module.exports = router; diff --git a/server/routes/static-router.js b/server/routes/static-router.js new file mode 100644 index 0000000..1a006ca --- /dev/null +++ b/server/routes/static-router.js @@ -0,0 +1,66 @@ +const express = require('express'); +const staticRouter = express.Router(); +const path = require('path'); +const checkIfFileExists = require("./util"); + +staticRouter.use('/styles/*', (req, res) => { + let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); + let extension = path.extname(filePath); + checkIfFileExists(filePath).then((fileExists) => { + if (fileExists && (extension === '.css')) { + res.sendFile(filePath); + } else { + res.sendStatus(404); + } + }); +}); + +staticRouter.use('/client/webfonts/*', (req, res) => { + let filePath = path.join(__dirname, ('../' + req.baseUrl)); + let extension = path.extname(filePath); + checkIfFileExists(filePath).then((fileExists) => { + if (fileExists && (extension === '.ttf' || extension === '.woff2')) { + res.sendFile(filePath); + } else { + res.sendStatus(404); + } + }); +}); + +staticRouter.use('/client/images/*', (req, res) => { + let filePath = path.join(__dirname, ('../' + req.baseUrl)); + let extension = path.extname(filePath); + checkIfFileExists(filePath).then((fileExists) => { + if (fileExists && (extension === '.svg' || extension === '.png' || extension === '.jpg')) { + res.sendFile(filePath); + } else { + res.sendStatus(404); + } + }); +}); + +staticRouter.use('/scripts/*', (req, res) => { + let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); + let extension = path.extname(filePath); + checkIfFileExists(filePath).then((fileExists) => { + if (fileExists && (extension === '.js')) { + res.sendFile(filePath); + } else { + res.sendStatus(404); + } + }); +}); + +staticRouter.use('/views/*', (req, res) => { + let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); + let extension = path.extname(filePath); + checkIfFileExists(filePath).then((fileExists) => { + if (fileExists && (extension === '.html')) { + res.sendFile(filePath); + } else { + res.sendFile('../views/404.html'); + } + }); +}); + +module.exports = staticRouter; diff --git a/server/routes/util.js b/server/routes/util.js new file mode 100644 index 0000000..56b38c7 --- /dev/null +++ b/server/routes/util.js @@ -0,0 +1,9 @@ +const fs = require('fs'); + +function checkIfFileExists(file) { + return fs.promises.access(file, fs.constants.F_OK) + .then(() => true) + .catch((e) => { console.error(e); return false }); +} + +module.exports = checkIfFileExists; diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json deleted file mode 100644 index f004bf2..0000000 --- a/spec/support/jasmine.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "spec_dir": "spec/unit", - "spec_files": [ - "**/*[sS]pec.js" - ], - "helpers": [ - "helpers/**/*.js" - ], - "stopSpecOnExpectationFailure": false, - "random": true -} \ No newline at end of file diff --git a/spec/unit/ServerSpec.js b/spec/unit/ServerSpec.js deleted file mode 100644 index d81bf95..0000000 --- a/spec/unit/ServerSpec.js +++ /dev/null @@ -1,65 +0,0 @@ -const ServerHelper = require('../../server-helper.js'); -const CronJob = require('cron').CronJob; - -describe('server helper', function() { - - let serverHelper; - - beforeEach(function(){ - serverHelper = new ServerHelper(CronJob); - }); - - it('should be instantiated with a cron job', function() { - - expect(serverHelper.job).toBeDefined(); - expect(serverHelper.job instanceof CronJob).toBeTrue(); - - }); - - it('should find a specific game by code in activeGames', function() { - - const expectedGame = {"startTime": 12345}; - serverHelper.activeGames = { - "somegame": {"startTime": 24156}, - "expected": expectedGame, - "filler": {"eh": "i dunno"}, - "wrong": {"this is": -Infinity} - }; - - expect(serverHelper.findGame("expected")).toBe(expectedGame); - - }); - - it('should create a new game', function(){ - - const spy = jasmine.createSpy("spy"); - const game = {"accessCode": "werewolf"}; - - serverHelper.newGame(game,spy); - - expect(serverHelper.findGame("werewolf")).toBeDefined(); - expect(serverHelper.findGame("werewolf").accessCode).toEqual("werewolf"); - expect(spy).toHaveBeenCalled(); - }); - - xdescribe('should handle players joining', function() { - - const socket = { - "emit": (value) => { - return value; - } - }; - - it('successful adds a player to game', function() { - - }); - - it('rejects a player when the game is full', function() { - - }); - - it('rejects a player when game is not found', function() { - - }); - }); -}); \ No newline at end of file diff --git a/stylesheets/styles.css b/stylesheets/styles.css deleted file mode 100644 index 88fa8ab..0000000 --- a/stylesheets/styles.css +++ /dev/null @@ -1,1918 +0,0 @@ -@media(max-width: 750px) { - .app-header { - font-size: 45px; - margin: 2em 0 0.5em 0; - } - - .app-header-secondary { - font-size: 35px; - margin: 0.3em 0; - } - - - #game-container #card-container { - min-width: 20em; - } - - h3 { - font-size: 20px; - } - - textarea { - font-size: 14px; - } - - #game-container { - flex-direction: column; - } - - #app-content { - width: 92%; - } - - .card { - padding: 0.5em; - width: 8em; - height: 11.5em; - font-size: 0.9em; - } - - .custom-role-edit { - font-size: 15px; - } - - .compact-card, .compact-card-header { - width: 9em; - } - - #card-select-header #role-view-changer img { - width: 20px; - } - - #role-view-changer div { - font-size: 14px; - } - - .card-header > p { - right: 7px; - top: 19px; - } - - .card-image { - top: 25%; - left: 12%;; - } - - .disclaimer, .custom-card h1 { - font-size: 15px; - } - - .modal { - width: 92%; - } - - .modal-content { - width: 90%; - } - - .modal-body { - padding: 1em; - } - - .modal-header { - padding: 0 1em; - } - - .modal-role { - margin-right: 2em; - } - - #learn-container, #faq-container { - margin: 3em 1em; - } - - .app-title img { - width: 100%; - } - - #card-select-header, #game-start { - align-items: center; - justify-content: center; - } - - #join-game-container { - display: flex; - flex-direction: column - } - - #join-game-container input[type=text] { - width: 10em; - height: 1.3em; - font-size: 1em; - } - - #join-game-container label { - font-size: 1.3em; - } - - #join-game-container h2 { - font-size: 2.5em; - margin-bottom: 1em; - } - - #join-game-container button, #join-game-container a { - width: 45%; - max-width: 15em; - margin-right: 0.5em; - } - - #join-game-container a button { - width: 100%; - } - - #overlay { - justify-content: center; - align-items: center; - } - - #killed-name { - padding-top: 0; - margin: 0 0.5em; - font-size: 2em; - } - - #game-container .alive-player .tooltiptext { - width: 12em; - right: 45%; - bottom: 80%; - font-size: 12px; - } - -} - -@media(min-width: 750.01px) { - .app-header { - font-size: 70px; - margin: 0.5em 0; - } - - #card-select-header #role-view-changer img { - width: 25px; - } - - .card-header > p { - right: 17px; - top: 30px; - } - - textarea { - font-size: 1.1em; - } - - #game-container #card-container { - min-width: 25em; - } - - .app-header-secondary { - font-size: 70px; - margin: 0.3em 0; - } - - .card { - width: 7em; - height: 10.5em; - padding: 1em; - font-size: 1.1em; - margin: 0.5em; - } - - .compact-card, .compact-card-header { - width: 11em; - } - - .custom-role-edit { - font-size: 19px; - } - - .disclaimer, .custom-card h1 { - font-size: 18px; - } - - h3 { - font-size: 30px; - } - - #app-content { - width: 80%; - } - - #learn-container, #faq-container { - margin: 3em; - } - - .modal-body { - padding: 2em 4em; - } - - .modal { - width: 92%; - } - - .modal-content { - width: 90%; - } - - #custom-card-modal .modal-content, #edit-custom-roles-modal .modal-content { - width: 50%; - } - - .modal-header { - padding: 0 3em; - } - - .modal-role { - margin-right: 3em; - } - - .app-title img { - width: 35em; - } - - #main-buttons a, #main-buttons button { - max-width: 15em; - } - - #card-select { - justify-content: flex-start; - } - - .card-image { - top: 29%; - left: 20%; - } - - #join-game-container input[type=text] { - width: 17em; - height: 1.7em; - font-size: 1.1em; - } - - #join-game-container label { - font-size: 1.3em; - } - - #join-game-container h2 { - font-size: 4em; - } - - #join-game-container button, #join-game-container a button { - width: 15em; - padding: 1.5em; - margin-right: 1em; - } - - #killed-name { - padding-top: 2em; - font-size: 2.5em; - margin: 0; - } - - #overlay { - justify-content: flex-start; - align-items: center; - } - - #game-container .alive-player .tooltiptext { - width: 15em; - font-size: 14px; - right: 106%; -/* bottom: -36%;*/ - } - -} - -@media(max-width: 850px) { - #card-select-header span:nth-child(2) { - flex-direction: column; - } - - #card-select-header span:nth-child(2) > div { - margin: 0.5em 0; - } -} - -@media(max-width: 1225px) and (min-width: 750.01px) { - #custom-card-modal .modal-content, #edit-custom-roles-modal .modal-content { - width: 75%; - } -} - -@font-face { - font-family: 'diavlo'; - src: url("../assets/fonts/Diavlo_LIGHT_II_37.otf") format("opentype"); -} - -@font-face { - font-family: 'diavlo-bold'; - src: url("../assets/fonts/Diavlo_BOOK_II_37.otf") format("opentype"); -} - -@font-face { - font-family: 'sitewide-sans-serif'; - src: url("../assets/fonts/manrope-light.otf") format("opentype"); -} - -html, body { - margin: 0 auto; - height: 100%; - color: #bfb8b8; - font-family: 'sitewide-sans-serif', sans-serif; - background-color: #23282b !important; -} - -@keyframes fadein { - from { - opacity: 0; - - } - to { - opacity: 1; - } -} - -@keyframes slide-fade { - from { - opacity: 0; - transform: translateX(80px) - - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes slide-fade-remove { - from { - opacity: 1; - transform: translateX(0px) - - } - to { - opacity: 0; - transform: translateX(-80px); - } -} - -#app-content { - text-align: center; - margin: 0 auto; - position: relative; -} - -#landing-container { - width: 100%; - height: 100%; - position: relative; - text-align: center; - background: linear-gradient(0deg, rgba(35,40,43,0.7959558823529411) 18%, rgba(131,131,131,0.6222864145658263) 100%); -} - -.app-title .app-header { - width: 6em; - display: flex; - font-family: diavlo-bold, sans-serif; -} - -.app-title { - display: flex; - align-items: center; - flex-direction: column; - line-height: 0.95; -} - -#footer { - position: absolute; - bottom: 0; - width: 100%; - margin-bottom: 1.5em; - font-size: 0.75em; - text-align: center; - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; -} - -.bmc-button span:nth-child(1) { - font-size: 20px; -} - -.bmc-button { - line-height: 35px !important; - height:40px !important; - text-decoration: none !important; - display:inline-flex !important; - align-items: center !important; - margin-bottom: 3em !important; - color:#ffffff !important; - background-color:#FF5F5F !important; - border-radius: 5px !important; - border: 1px solid transparent !important; - padding: 7px 15px 7px 10px !important; - font-size: 15px !important; - box-shadow: 0px 1px 2px rgba(190, 190, 190, 0.5) !important; - -webkit-box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important; - font-family: sitewide-sans-serif, sans-serif !important; - -webkit-box-sizing: border-box !important; - box-sizing: border-box !important; -} - -.bmc-button:hover, .bmc-button:active, .bmc-button:focus { - -webkit-box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important; - text-decoration: none !important;box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important; - opacity: 0.85 !important;color:#ffffff !important; -} - -#footer div { - display: flex; - align-items: center; -} - -#footer p { - margin: 0 0 0 1em; - color: lightgray; -} - -#footer a { - color: #d13d3d; - text-decoration: #d13d3d; - cursor: pointer; - margin: 0 0 0 1em; -} - -#footer a:hover { - color: #333243; -} - -.app-header, .app-header-secondary { - font-family: 'diavlo', sans-serif; - filter: drop-shadow(2px 2px 4px black); - color: #992626; -} - -h3 { - color: gray; -} - -button { - font-family: 'sitewide-sans-serif', sans-serif; -} - -.error { - border: 1px solid #bd2a2a !important; - background-color: rgba(189, 42, 42, 0.1) !important; -} - -#join-error, #name-error, #size-error, #some-error { - color: #bd2a2a; - font-size: 0.9em; - height: 2em; - margin: 0; - font-weight: bold; -} - -#join-game-container span { - display: flex; - flex-direction: column; -} - -#join-game-container div { - width: 100%; - display: flex; - justify-content: center; -} - -#join-game-container form { - margin-bottom: 2em; -} - -#join-game-container input[type=text], #create-game-container input[type=text] { - margin-right: 0.5em; - margin-bottom: 0; -} - -.app-btn { - background-color: #992626; - box-shadow: 2px 3px 8px rgba(0, 0, 0, 0.4); - color: #bfb8b8; - border: none; - width: 10em; - padding: 1em; - border-radius: 3px; - margin-bottom: 1em; -} - -#create-game-container .app-btn { - display: flex; - align-items: center; - justify-content: center; -} - -.app-btn img, .app-btn-secondary img { - width: 15px; -} - -img.custom-role-button { - width: 25px; -} - -.custom-role-edit { - display: flex; - width: 100%; - flex-direction: column; - justify-content: space-around; - align-items: center; - background-color: black; - border-radius: 5px; - margin: 0.3em; -} - -.edit-role-form { - background-color: #1a1a1a; - padding: 1em; -} - -.custom-role-edit > div:nth-child(1) { - display: flex; - width: 100%; - justify-content: space-around; -} - -.custom-role-edit > div:nth-child(1) div { - display: flex; - align-items: center; -} - -.custom-role-edit > div:nth-child(2) { - display: flex; - width: 100%; -} - -.custom-role-edit p { - text-overflow: ellipsis; - overflow-x: hidden; - white-space: nowrap; - max-width: 7em; -} - -.custom-role-edit div > img { - margin: 0 1em; - cursor: pointer; - user-select: none; -} - -.custom-role-edit div > img:hover { - filter: brightness(70%); -} - -#import-file-input { - display: none; -} - -.card, .compact-card { - text-align: center; - cursor: pointer; - position: relative; - margin: 0.3em; - background-color: #393a40; - color: gray; - box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4); - border-radius: 3px; - user-select: none; -} - -.compact-card { - display: flex; - height: max-content; - position: relative; -} - -.compact-card h1 { - display: flex; - align-items: center; - font-size: 14px; - margin: 0 10px 0 10px; -} - -.compact-card .card-role { - color: #bfb8b8; - margin: 0; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - max-width: 8em; -} - -.compact-card-right p { - font-size: 40px; - margin: 0 10px 0 0; - display: flex; - justify-content: flex-end; -} - -.compact-card-left p { - font-size: 40px; - margin: 0 0 0 10px; - display: flex; - justify-content: flex-start; -} - -.compact-card-left, .compact-card-right { - width: 50%; -} - -.compact-card .card-quantity { - text-align: center; - margin: 0; -} - -.compact-card-header { - position: absolute; - flex-direction: column; - align-items: center; - justify-content: center; - display: flex; - top: 0; - right: 0; - pointer-events: none; - text-align: center; -} - -.card-werewolf { - border: 1px solid red; -} - -.custom-card { - border: 1px dashed whitesmoke; - background-color: transparent; -} - -.custom-card div { - font-size: 40px; - color: whitesmoke; - margin-right: 5px; -} - -.custom-card h1 { - color: whitesmoke; -} - -.card:hover, .compact-card:hover { - background-color: #55565c; -} - -.card-top { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: space-between; - height: 50%; -} - -.card-bottom { - display: flex; - display: -webkit-flex; - justify-content: flex-end; - height: 50%; - align-items: flex-end; -} - -.card-top p { - margin: 0; - font-size: 2.5em; - pointer-events: none; - line-height: 1; - color: #bfb8b8; -} - -.card-top p.card-role { - font-size: 1em; - height: 1.2em; - pointer-events: none; - max-width: 7em; - white-space: nowrap; - overflow-x: hidden; - text-overflow: ellipsis; - overflow-y: visible; -} - -.card-header { - display: flex; - width: 100%; - justify-content: space-between; - align-items: flex-start; - pointer-events: none; -} - -.card-header > p { - position: absolute; -} - -.card-bottom p { - pointer-events: none; - line-height: 1; - font-size: 3em; - margin: 0; - color: #bfb8b8; -} - -.card-image { - position: absolute; - pointer-events: none; -} - -.card-image-custom { - width: 50px; - height: 50px; - padding: 25px; -} - -.card-quantity { - pointer-events: none; - font-weight: bold; - text-align: left; - color: #bd2a2a; - font-size: 1.5em; - margin: 0 0 0 0.2em; - width: 1.5em; -} - -.card-role { - color: #bfb8b8; - margin: 0; -} - -#card-select { - margin-bottom: 2em; -} - -#card-select-good, #card-select-evil { - display: flex; - display: -webkit-flex; - -webkit-flex-wrap: wrap; -} - -#card-select > h2:nth-child(1) { - color: #4a6bff; -} - -#card-select > h2:nth-child(3) { - color: #bd2a2a; -} - - -.app-btn-secondary { - display: flex; - justify-content: space-around; - align-items: center; - background-color: lightgray; - border-radius: 3px; - color: black; - border: none; - width: 10em; - padding: 0.5em; - margin-right: 1em; -} - -@keyframes press-down { - from { box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.4); } - to { box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5); } -} - -.app-btn:hover, .app-btn:focus { - cursor: pointer; - background-color: #333243 !important; -} - -#main-buttons .app-btn:hover, #main-buttons .app-btn:focus { - box-shadow: none !important; - cursor: pointer; - background-color: #333243; - animation: press-down 0.2s; - animation-fill-mode: forwards; - animation-direction: normal; -} - -.app-btn-secondary:hover, .app-btn-secondary:focus { - cursor: pointer; - filter: brightness(70%); -} - -#main-buttons { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - max-width: 18em; - margin: 0 auto; - overflow: hidden; -} - -#main-buttons button { - width: 100%; - padding: 1em; - font-size: 0.9em; - box-shadow: 2px 3px 8px rgba(0, 0, 0, 0.4); - background-color: #992626; - color: #bfb8b8; -} - -#main-buttons a { - width: 100%; -} - -#game-mode-select { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - position: absolute; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -#landing-container .slide-in { - animation: slide-fade 0.25s; - animation-delay: 0.125s; - animation-fill-mode: both; -} - -#landing-container .slide-out { - animation: slide-fade-remove 0.25s; -} - - -#game-mode-select .game-mode { - width: 7em; - background-color: transparent; - color: whitesmoke; - border: 1px solid whitesmoke; - font-size: 30px; - font-weight: bold; - padding: 5px; - margin-bottom: 0.5em; - border-radius: 5px; -} - -#game-mode-select .game-mode:hover { - cursor: pointer; - background-color: #494f52; -} - -#game-mode-select span { - font-size: 50px; - font-weight: bold; - cursor: pointer; - margin-bottom: 0.2em; - text-align: left; - width: 100%; -} - -#game-mode-select span:hover { - color: #333243; -} - -#card-select-header { - display: flex; - width: 100%; - justify-content: center; - align-items: flex-start; - margin-bottom: 0.5em; - flex-direction: column; -} - -#card-select-header a, #landing-container #game-mode-select a:nth-child(2) { - text-decoration: underline; - color: #b8c4ff; -} - -#landing-container #game-mode-select a:nth-child(2) { - text-decoration: underline; - color: #b8c4ff; - text-align: left; - width: 100%; - margin-bottom: 1em; -} - -#card-select-header span > div { - display: flex; - align-items: center; -} - -#card-select-header #role-view-changer img { - margin-left: 15px; -} - -#role-view-changer div { - display: flex; - justify-content: space-around; - width: max-content; - margin: 0 15px 0 0; - padding: 5px; - border-radius: 3px; -} - -.selected { - background: #494f52; -} - -#role-view-changer div:hover { - cursor: pointer; - background-color: #494f52; -} - -#role-view-changer { - margin: 15px 0 0 0; -} - -#role-view-changer p { - margin: 0; -} - -#card-select-header h3 { - margin: 0; - font-size: 1.2em; - color: #bfb8b8; -} - -#create-game-container button { - padding: 0.8em; - height: 3em; - width: max-content; -} - -#edit-role-btn > img { - margin-left: 0.5em; -} - -#import-role-btn > img { - margin-left: 0.5em; -} - -#game-start a button { - width: 100%; -} - -#game-start { - display: flex; - width: 100%; -} - -#game-start a { - margin-right: 0.3em; -} - -#game-start > button { - background-color: #8EA604; - color: black; - margin-left: 0.3em; - width: 12em; -} - -#game-start a button { - margin-right: 0.3em; - margin-left: 0; - width: 12em; -} - -#card-select-header button:nth-child(1) { - margin-right: 0.3em; -} - -#card-select-header button:nth-child(2) { - margin-left: 0.3em; -} - -#card-select-header span { - display: flex; - align-items: center; -} - -#reset-btn { - /*background-color: transparent;*/ - /*color: #bd2a2a;*/ - /*border-radius: 5px;*/ - /*border: 1px solid #bd2a2a;*/ - /*width: 8em;*/ - /*padding: 0.5em;*/ - /*margin: 0 1em 0 0;*/ - /*cursor: pointer;*/ -} - -#create-game-container, #join-game-container { - text-align: left; - margin: 1em 0.5em 2em 0.5em; -} - -#game-size { - border-radius: 5px; - padding: 0.3em 0.5em; - margin-right: 0.5em !important; -} - -#create-game-container { - display: flex; - flex-direction: column; - align-items: flex-start; - animation: slide-fade 0.5s; -} - -#create-game-container #werewolf-key { - width: 25px; - height: 25px; - border: 1px solid red; - border-radius: 3px; - margin-right: 10px; -} - - -#join-game-container { - display: flex; - flex-direction: column; - align-items: center; - animation: slide-fade 0.5s; -} - -a { - text-decoration: none; - color: inherit; -} - -input[type=text] { - background-color: transparent; - border: 1px solid #464552; - caret-color: gray; - margin: 0.5em 0 1em 0; - color: gray; - padding: 0.9em; - height: 1.2em; - width: 12em; - border-radius: 5px; - font-size: 1.1em; -} - -textarea { - resize: none; - background-color: transparent; - border: 1px solid #464552; - caret-color: gray; - font-family: inherit; - margin: 0.5em 0 1em 0; - color: gray; - padding: 0.9em; - border-radius: 5px; -} - -.checkbox label::before{ - content: ""; - display: inline-block; - height: 30px; - left: -42px; - width: 30px; - border: 1px solid whitesmoke; - border-radius: 3px; -} - -.checkbox input[type=checkbox] { - opacity: 0; -} - -.checkbox label::after { - display: inline-block; - height: 8px; - width: 16px; - left: -35px; - top: 8px; - border-left: 3px solid; - border-bottom: 3px solid; - transform: rotate(-55deg); -} - -.checkbox { - display: flex; - width: 100%; - margin: 1em 0; -} - -.checkbox label { - position: relative; - margin: 1em 0 1em 1.5em; -} - -.checkbox label::before, -.checkbox label::after { - position: absolute; -} - -.checkbox input[type="checkbox"] + label::after { - content: none; -} - -.checkbox input[type="checkbox"]:checked + label::after { - content: ""; -} - -.checkbox input[type="checkbox"]:focus + label::before { - outline: rgb(59, 153, 252) auto 5px; -} - -.checkbox label:hover::before { - border: 1px solid #333243 !important; - cursor: pointer; -} - -.checkbox label:hover { - cursor: pointer; -} - - -.disclaimer { - color: #bd2a2a; -} - -select { - border-radius: 5px; - padding: 10px 20px; - font-size: 20px; - font-family: inherit; - background-color: transparent; - color: whitesmoke; -} - -select option { - background: rgb(35,40,43); -} - -input[type=number] { - background-color: transparent; - border: 1px solid #464552; - border-radius: 5px; - caret-color: gray; - margin: 0.5em 0 1em 0; - color: gray; - padding: 0.9em; - height: 1.2em; - width: 5em; - font-size: 1.1em; -} - -input[type=text]:hover, input[type=number]:hover { - background-color: rgba(51, 50, 67, 0.35); -} - -label { - margin-bottom: 1em; - font-size: 1em; - display: flex; - flex-direction: column; -} - -#message-box { - height: 2em; - max-width: 25em; - margin: 1em auto; - border-bottom: 1px solid #992626; - padding: 0.5em; -} - -.import-failure { - display: flex; - width: 100%; - flex-direction: row; - justify-content: space-between; - background-color: black; - border-radius: 5px; - margin: 0.3em; -} - -.import-failure-label { - display: flex; - flex-direction: row; - width: 33%; -} - -.import-failure-label p { - text-overflow: ellipsis; - overflow-x: hidden; - white-space: nowrap; - max-width: 20em; -} - -.import-failure-data { - display: flex; - flex: 1 1 auto; - flex-direction: row; - justify-content: center; -} - -.import-failure-data ul { - max-width: 40em; -} - -.triangle { - width: 0; - height: 0; - border-style: solid; - border-width: 8px; - float: left; - position: relative; - z-index: 1; - } - -div.import-failure.warning .triangle { - border-color: #dbec3b transparent transparent #dbec3b; -} - -div.import-failure.error .triangle { - border-color: #c7360a transparent transparent #c7360a; -} - -/* lobby */ - -#lobby-container { - padding: 1em; - max-width: 26em; - margin: 0 auto; -} - -.lobby-player { - color: #bfb8b8; - border-radius: 3px; - font-size: 1em; - background-color: rgba(51, 50, 67, 0.35); - padding: 0.3em; - margin: 0.5em 0; - text-align: left; - box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.3); -} - -#game-code { - font-family: "Courier New", serif; -} - -.highlighted { - border: 2px solid #bd2a2a; -} - -.disabled { - opacity: 0.5; - color: gray; - background-color: white; - pointer-events: none; - border: none; -} - -.hidden { - display: none !important; -} - -#lobby-subheader { - text-align: left; - margin-bottom: 2em; -} - -#join-count, #deck-size { - font-size: 1.5em; -} - -.lobby-player p { - margin: 0; -} - -#launch-btn { - margin-top: 2em; -} - -/* GAME */ - -@keyframes flip-up { - 0% { - transform: rotateY(0deg); - } - 100% { - transform: rotateY(-180deg); - } -} - -@keyframes flip-slide { - 0% { - transform: rotateY(0deg); - transform: translateX(-100px); - } - 100% { - transform: rotateY(-90deg); - transform: translateX(0px); - } -} - -@keyframes flip-down { - 0% { - transform: rotateY(0deg); - } - 100% { - transform: rotateY(180deg); - } -} - - -#game-card { - background-color: transparent; - display: flex; - flex-direction: column; - cursor: pointer; - justify-content: space-between; - max-width: 17em; - border-radius: 3px; - height: 23em; - margin: 0 auto 2em auto; - width: 100%; - box-shadow: 0 13px 17px rgba(0,0,0,0.6); - perspective: 1000px; - transform-style: preserve-3d; -} - -#game-card img { - width: 215px; - top: 35px; - position: absolute; - z-index: 4; - user-drag: none; - user-select: none; - -moz-user-select: none; - -webkit-user-drag: none; - -webkit-user-select: none; - -ms-user-select: none; -} - -.placeholder { - width: 150px; - height: 150px; -} - -.village h2 { - color: #4a6bff; -} - -.wolf h2 { - color: #bd2a2a; -} - -.flip-up { - animation: flip-up 0.7s; - animation-fill-mode: forwards; - animation-direction: reverse; -} - -.flip-down { - animation: flip-down 0.7s; - animation-fill-mode: forwards; -} - -.flip-slide{ - animation: flip-slide 1s; - animation-fill-mode: forwards; -} - - -#game-card h2 { - font-size: 1.7em; - font-family: 'diavlo', sans-serif; - margin: 0.3em 0 0.4em 0; -} - -#game-card p { - padding: 0.5em; - font-size: 0.8em; - color: #464552; - max-height: 68px; - overflow: auto; -} - -.game-container { - margin-top: 1em; -} - -/* This container is needed to position the front and back side */ -.game-card-inner { - position: relative; - width: 95%; - height: 97%; - margin: auto auto; - border-radius: 5px; - text-align: center; - border: 3px solid #9c9c9c; -} - -/* Position the front and back side */ -.game-card-front, .game-card-back { - background-color: #dedede; - position: absolute; - display: flex; - border-radius: 3px; - flex-direction: column; - justify-content: space-between; - align-items: center; - width: 100%; - height: 100%; - transform: rotateX(0deg); - -webkit-perspective: 0; - -webkit-backface-visibility: hidden; - -webkit-transform: translate3d(0,0,0); - -moz-backface-visibility: hidden; - -moz-transform: translate3d(0, 0, 0); - -moz-perspective: 0; - visibility:visible; - backface-visibility: hidden; -} - -.game-card-back { - transform: rotateY(180deg); -} - -.game-card-front p:first-of-type { - margin: 0.7em; - border: 1px solid gray; - border-radius: 3px; -} - -.killed-btn { - border-radius: 5px; - width: 13em; - font-size: 1em; - margin: 0 -} - -#players-remaining { - font-size: 1.5em; - color: #bfb8b8; - width: 6em; - margin-bottom: 0.5em; -} - -#clock { - font-size: 1.5em; - width: 3.8em; - margin: 0 0.5em 0.5em 0; -} - -#flip-instruction { - color: gray; - margin: 0; -} - -#game-header { - display: flex; - margin: 0 auto; - max-width: 35em; - justify-content: center; -} - -#game-container { - display: flex; - justify-content: center; - align-items: center; -} - -#game-container .killed-player, #game-container .alive-player { - background-color: black; - border-radius: 5px; - padding: 5px; - display: flex; - align-items: center; - justify-content: space-between; - box-shadow: 3px 10px 10px rgba(0,0,0,0.6); - margin: 0.3em; - position: relative; -} - -#game-container .killed-player p, #game-container .alive-player p { - margin: 0; -} - -#play-pause { - width: 2.5em; - height: 2.5em; - cursor: pointer; -} - -#play-pause:hover { - opacity: 0.5; -} - -#play-pause:hover { -} - -#play-pause:active, #play-pause:focus { - opacity: 0.4; - transform: scale(0.90); -} - -/* end splash */ - -#end-container { - margin: 0 auto; - display: inline-block; - text-align: left; - width: 100%; -} - -#end-container #roster { - width: 100%; - flex-wrap: wrap; - display: flex; -} - -#end-container .winner-header { - display: flex; - align-items: center; - margin: 0; - font-size: 30px; - margin: 10px 0 30px 0; -} - -#end-container .evil-subheader { - font-family: 'diavlo', sans-serif; - color: #bd2a2a; - margin: 0 0.3em; - font-size: 1.5em; -} - -#end-container .roster-list-item { - background-color: #40464a; - padding: 10px; - border-radius: 5px; - display: flex; - align-items: center; - font-size: 20px; - width: 33%; - min-width: 13em; - margin: 0.3em; -} - -#end-container .roster-header { - font-size: 25px; - margin-bottom: 1em; - -} - -#end-container .roster-list-item img { - margin-right: 0.5em; -} - -#end-container .evil-header { - display: flex; - align-items: center; - font-size: 20px; - margin-bottom: 2em; -} - - -#end-container p { - font-weight: bold; - margin: 0 0.3em 0 0; - font-size: 70px; -} - -#end-container button { - margin-top: 3em; -} - -#end-container .winner-village { - font-family: 'diavlo', sans-serif; - color: #4a6bff; -} - -#end-container .winner-wolf { - font-family: 'diavlo', sans-serif; - color: #bd2a2a; -} - -#learn-container h2, #faq-container h2 { - font-family: 'diavlo', sans-serif; - color: #bd2a2a; - font-size: 40px; -} - -.faq-question { - max-width: 60em; -} - -#learn-container button, #faq-container button { - margin-top: 1em; -} - -#roles { - display: flex; - flex-wrap: wrap; -} - -.modal-role { - width: 22em; - position: relative; - border-bottom: 1px solid #494f52; - padding-bottom: 0.5em; -} - -.modal-role div { - display: flex; - justify-content: center; - align-items: center; -} - -.modal-role div div { - display: flex; - flex-direction: column; - align-items: flex-start; -} - -.modal-role p:first-of-type { - margin: 0; - color: #bfb8b8; -} - -.modal-role h2 { - margin: 0; -} - -.modal { - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - -webkit-overflow-scrolling: touch; - background-color: rgb(0,0,0); - background-color: rgba(0,0,0,0.4); -} - -.modal-header { - border-radius: 5px; - display: flex; - justify-content: flex-end; - background-color: #23282b; - color: #bfb8b8; -} - -.modal-header h2 { - margin-top: 2em; - margin-bottom: 0; - color: #bfb8b8; -} - -.modal-footer { - padding: 1em; - background-color: #23282b; - color: #bfb8b8; -} - -.modal-content { - position: relative; - color: #bfb8b8; - background-color: #23282b; - margin: 1em auto; - padding: 0; - border: none; - box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19); - animation-name: animatetop; - animation-duration: 0.4s -} - -.role-wolf { - color: #bd2a2a; - font-family: 'diavlo', sans-serif; -} - -.role-village { - color: #4a6bff; - font-family: 'diavlo', sans-serif; -} - -.quantity-village, .alive-player-village, .dead-player-village { - color: #4a6bff; -} - -.quantity-wolf, .alive-player-evil, .dead-player-evil { - color: #bd2a2a; -} - -.dead-player-no-reveals { - color: whitesmoke !important; -} - -.alive-player-wolf, .dead-player-wolf { - border: 2px solid #bd2a2a; -} - -#overlay { - position: fixed; - user-select: none; - display: none; - flex-direction: column; - width: 100%; - height: 100%; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0,0,0,0.9); - z-index: 3; -} - -#killed-name { - display: none; - color: #bfb8b8; -} - -#killed-role { - display: none; - margin: 0 auto; -} - -.killed-role-custom { - width: 190px; - padding: 95px; -} - -.killed-role-hidden { - padding-top: 3em; -} - -@keyframes slide-fade-in-top { - 0% { - opacity: 0; - transform: translateY(-150px) - - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes slide-fade-out-top { - 0% { - opacity: 1; - transform: translateY(0px) - - } - 100% { - opacity: 0; - transform: translateY(-150px); - } -} - -@keyframes slide-fade-in-bottom { - 0% { - opacity: 0; - transform: translateY(150px) - - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes slide-fade-out-bottom { - 0% { - opacity: 1; - transform: translateY(0); - } - 100% { - opacity: 0; - transform: translateY(150px) - - } -} - -@keyframes fade-overlay-in { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -@keyframes fade-overlay-out { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } -} - -.animate-overlay-in { - animation: fade-overlay-in 5s; - animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); - animation-fill-mode: forwards; - -webkit-animation: fade-overlay-in 5s; - -webkit-animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); - -webkit-animation-fill-mode: forwards; -} - -.animate-overlay-out { - animation: fade-overlay-out 1s; - animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); - animation-fill-mode: forwards; - -webkit-animation: fade-overlay-out 1s; - -webkit-animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); - -webkit-animation-fill-mode: forwards; -} - -.animate-name-in { - animation: slide-fade-in-top 5s; - animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); - animation-fill-mode: forwards; - -webkit-animation: slide-fade-in-top 5s; - -webkit-animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); - -webkit-animation-fill-mode: forwards; -} - -.animate-name-out { - animation: slide-fade-out-top 1s; - animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); - animation-fill-mode: forwards; - -webkit-animation: slide-fade-out-top 1s; - -webkit-animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); - -webkit-animation-fill-mode: forwards; -} - -.animate-role-in { - animation: slide-fade-in-bottom 5s; - animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); - animation-fill-mode: forwards; - -webkit-animation: slide-fade-in-bottom 5s; - -webkit-animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); - -webkit-animation-fill-mode: forwards; -} - -.animate-role-out { - animation: slide-fade-out-bottom 1s; - animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); - animation-fill-mode: forwards; - -webkit-animation: slide-fade-out-bottom 1s; - -webkit-animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); - -webkit-animation-fill-mode: forwards; -} - -@keyframes animatetop { - from {top: -300px; opacity: 0} - to {top: 0; opacity: 1} -} - -.close { - margin-top: 0.2em; - color: #bfb8b8; - float: right; - font-size: 46px; - height: 1em; - display: flex; - align-items: center; - font-weight: bold; -} - -.close:hover, -.close:focus { -color: #333243; -text-decoration: none; -cursor: pointer; -} - -/*Tooltip styles. Taken from w3schools*/ -#game-container .alive-player .tooltiptext { - visibility: hidden; - background-color: black; - color: #fff; - text-align: left; - border-radius: 6px; - padding: 5px; - position: absolute; - z-index: 1; -} - -#game-container .alive-player img { - width: 25px; - height: 25px; -} - -#game-container .alive-player:hover .tooltiptext, #game-container .alive-player:focus .tooltiptext { - visibility: visible; - opacity: 1; -} diff --git a/views/create_game.html b/views/create_game.html deleted file mode 100644 index 344201f..0000000 --- a/views/create_game.html +++ /dev/null @@ -1,165 +0,0 @@ - - - - Werewolf - - - - - - - - - - - - -
    -
    -

    Create A Game

    - - -
    - - - - - -
    -

    0 Players

    - - -
    -
    -
    -
    = Werewolf role (counts for parity)
    -
    -
    - - -
    -

    Condensed View

    - -
    -
    -
    -
    -

    Good Roles

    -
    -

    Evil Roles

    -
    -
    -
    - - - - -
    -

    -
    -
    - - - diff --git a/views/faq.html b/views/faq.html deleted file mode 100644 index a063c55..0000000 --- a/views/faq.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - Title - - - - - - -
    - -

    FAQ

    -
    -

    What is Parity?

    -

    Parity means equal, and in the context of the game, this refers to an equal number of wolves and villagers. - This is a victory condition for the wolves, because if at any time during the day parity is reached, it is certain that the wolves - will be able to kill all the remaining villagers. Thus, if a standard or custom role is marked as a Werewolf, it will be considered - when the game determines if parity has been reached. -

    -
    -
    -

    What does it mean when a game is "Reveal" or "No-Reveal"?

    -

    If you are playing a game with "reveals," players' roles will be revealed when they die. Otherwise, they remain a mystery.

    -
    -
    - - - diff --git a/views/game.html b/views/game.html deleted file mode 100644 index 03de719..0000000 --- a/views/game.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - Werewolf - - - - - - - -
    -
    -

    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - diff --git a/views/index.html b/views/index.html deleted file mode 100644 index 4340696..0000000 --- a/views/index.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - Werewolf - - - - - - - - - - - diff --git a/views/join_game.html b/views/join_game.html deleted file mode 100644 index 8b99d5d..0000000 --- a/views/join_game.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - Werewolf - - - - - - - -
    -
    -

    Join a Game

    -
    - - -
    -
    - - - - -
    -
    -
    - - - diff --git a/views/learn.html b/views/learn.html deleted file mode 100644 index e5d0b54..0000000 --- a/views/learn.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - Title - - - - - - -
    - -

    Introduction

    -

    This is a social strategy game involving deception, deduction, cooperation, and any number of clever tactics. - There are two teams - the village and the werewolves. The village has the objective of finding and killing all - the wolves, and the wolves are trying to eat all the villagers. The game is divided into two phases: day and - night. At night is when the werewolves operate, deciding together which villager to kill off. The daytime is when - the village is active, deciding which among them seem evil and killing them to end the day. During the day, everyone - is disguised as a villager - even those that are actually wolves. -

    -

    Setup

    -

    At least 5 players are needed for a sensible game. Players can decide on which cards should go in the deck to create - a balanced experience. For example, a 7 player game might involve 2 werewolves, 3 villagers, a hunter, and a seer. - For larger games, this can be a bit trickier, but the goal is to create a game that isn't too easy for the wolves - or the villagers. Once the deck is chosen, the deck is dealt, and players see only their card. -

    -

    Gameplay

    -

    Play begins with the Night One. Everyone "goes to sleep," closing their eyes and creating some sort of white - noise (commonly a patting of the hand on the thigh). At this point, the moderator will ask the Werewolves to wake - up and see each other. If you do not have a designated moderator, choose someone arbitrarily for the first night, - and then the first player to die can moderate the rest of the game.

    - - First, The Werewolves will wake up and see the other wolves, giving them the knowledge that will guide the game. - Then, the werewolves go back to sleep. If you are playing with a Minion, next the Werewolves will raise their - hands (but not awake), and the Minion will awake to spot the wolves. You can also play with a "double-blind" - minion, who does not know who the wolves are, but is still playing on the same team as the wolves. This is all - that needs to happen on the first night.

    - - At this point, everyone wakes up, and Day One begins. This is an open debate between everyone in the circle about - who they should kill in suspicion of being a wolf. This can take any amount of time, but watch out, because the - wolves win if time expires! You should have some system for exhibiting votes to kill another player. If a player - receives a majority vote, they should press the "I'm dead" button on their screen, and everyone else will have - that player's role revealed to them. At this point, everyone immediately goes to sleep, and the next night begins.

    - - On every night after the first, wolves will, in silence, agree on someone in the circle (other than themselves) - to eat during the night. After this concludes, and everyone wakes up, the player that was killed will be revealed - by the moderator, and they will reveal their role. Then, of course, Day Two begins.

    - - The game continues in this alternating fashion until an endgame is reached. If a day ends with the same number of - wolves as villagers, the wolves win (as they can kill a villager at night, and then have the majority to kill the - remaining villager during the day). If the village manages to kill every wolf, then they win. In the scenario - where there is one villager and one wolf remaining, if the remaining villager is a Hunter, then the village wins. - There are several "power cards" such as the Hunter, which can help the village or the wolves. If you are a power - role, you can read the description on your card to find out what your special ability is. -

    - -
    - - From 226b0c25e20d84b6e55b308048ae9c220a8fb9b4 Mon Sep 17 00:00:00 2001 From: Alec Date: Mon, 8 Nov 2021 23:38:41 -0500 Subject: [PATCH 02/60] new create page logic --- README.md | 2 +- client/config/customCards.js | 12 ++ client/config/{cards.js => defaultCards.js} | 9 +- client/modules/DeckStateManager.js | 48 ++++++++ client/modules/ModalManager.js | 30 +++++ client/modules/Toast.js | 35 ++++++ client/scripts/create.js | 119 +++++++++++++++++- client/styles/GLOBAL.css | 63 +++++++++- client/styles/create.css | 128 ++++++++++++++++++++ client/styles/modal.css | 42 +++++++ client/views/create.html | 41 ++++++- client/views/home.html | 8 +- client/webfonts/Diavlo_LIGHT_II_37.woff2 | Bin 0 -> 17216 bytes client/webfonts/OFL.txt | 94 ++++++++++++++ client/webfonts/SignikaNegative-Light.woff2 | Bin 0 -> 14112 bytes server/routes/static-router.js | 37 ++++++ 16 files changed, 656 insertions(+), 12 deletions(-) create mode 100644 client/config/customCards.js rename client/config/{cards.js => defaultCards.js} (80%) create mode 100644 client/modules/DeckStateManager.js create mode 100644 client/modules/ModalManager.js create mode 100644 client/modules/Toast.js create mode 100644 client/styles/modal.css create mode 100644 client/webfonts/Diavlo_LIGHT_II_37.woff2 create mode 100644 client/webfonts/OFL.txt create mode 100644 client/webfonts/SignikaNegative-Light.woff2 diff --git a/README.md b/README.md index 8e86ca0..953a56f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This is a Javascript application running on a node express main. I am using the All pixel art is my own (for better or for worse). -This is meant to facilitate the game in a face-to-face social setting and provide utility/convenience - not control all aspects of the game flow. The app allows players to create or join a game lobby where state is synchronized. The creator of the game can build a deck from either the standard set of provided cards, or from any number of custom cards the user creates. Once the game begins, this deck will be randomly dealt to all participants. +This is meant to facilitate the game in a face-to-face social setting and provide utility/convenience - not control all aspects of the game flow. The app allows players to create or join a game lobby where state is synchronized. The creator of the game can build a deck from either the standard set of provided defaultCards, or from any number of custom defaultCards the user creates. Once the game begins, this deck will be randomly dealt to all participants. Players will see their card (which can be flipped up and down), an optional timer, and a button to say that they have been killed off. If a player presses the button, they will be removed from the game, and their role revealed to other players. The game will continue until the end of the game is detected, or the timer expires. diff --git a/client/config/customCards.js b/client/config/customCards.js new file mode 100644 index 0000000..e6b46cb --- /dev/null +++ b/client/config/customCards.js @@ -0,0 +1,12 @@ +export const customCards = [ + { + role: "Santa", + team: "evil", + description: "hohoho", + }, + { + role: "Mason", + team: "good", + description: "you are a mason", + }, +]; diff --git a/client/config/cards.js b/client/config/defaultCards.js similarity index 80% rename from client/config/cards.js rename to client/config/defaultCards.js index 9229e93..b24fd81 100644 --- a/client/config/cards.js +++ b/client/config/defaultCards.js @@ -1,4 +1,4 @@ -export const cards = [ +export const defaultCards = [ { role: "Villager", team: "good", @@ -15,10 +15,15 @@ export const cards = [ description: "If a Werewolf dies, you become a Werewolf. You do not wake up with the Werewolves until this happens. You count for parity only after converting to a wolf.", }, { - role: "Minion", + role: "Knowing Minion", team: "evil", description: "You are an evil villager - you know who the wolves are, and you want them to win.", }, + { + role: "Double-Blind Minion", + team: "evil", + description: "You are an evil villager. You don't know who the wolves are, but you want them to win.", + }, { role: "Seer", team: "good", diff --git a/client/modules/DeckStateManager.js b/client/modules/DeckStateManager.js new file mode 100644 index 0000000..398c72a --- /dev/null +++ b/client/modules/DeckStateManager.js @@ -0,0 +1,48 @@ +export class DeckStateManager { + constructor() { + this.deck = null; + this.customRoleOptions = null; + } + + addToDeck(role) { + let option = this.customRoleOptions.find((option) => option.role === role) + let existingCard = this.deck.find((card) => card.role === role) + if (option && !existingCard) { + option.quantity = 0; + this.deck.push(option); + this.customRoleOptions.splice(this.customRoleOptions.indexOf(option), 1); + } + } + + addToCustomRoleOptions(role) { + this.customRoleOptions.push(role); + } + + addCopyOfCard(role) { + let existingCard = this.deck.find((card) => card.role === role) + if (existingCard) { + existingCard.quantity += 1; + } + } + + removeCopyOfCard(role) { + let existingCard = this.deck.find((card) => card.role === role) + if (existingCard && existingCard.quantity > 0) { + existingCard.quantity -= 1; + } + } + + getCurrentDeck() { return this.deck } + + getCard(role) { return this.deck.find((card) => card.role === role) } + + getCurrentCustomRoleOptions() { return this.customRoleOptions } + + getDeckSize() { + let total = 0; + for (let role of this.deck) { + total += role.quantity; + } + return total; + } +} diff --git a/client/modules/ModalManager.js b/client/modules/ModalManager.js new file mode 100644 index 0000000..142245d --- /dev/null +++ b/client/modules/ModalManager.js @@ -0,0 +1,30 @@ +export const ModalManager = { + displayModal: displayModal +} + +function displayModal(modalId, backgroundId, closeButtonId) { + const modal = document.getElementById(modalId); + const modalOverlay = document.getElementById(backgroundId); + const closeBtn = document.getElementById(closeButtonId); + let closeModalHandler; + if (modal && modalOverlay && closeBtn) { + modal.style.display = 'flex'; + modalOverlay.style.display = 'flex'; + modalOverlay.removeEventListener("click", closeModalHandler); + modalOverlay.addEventListener("click", closeModalHandler = function(e) { + e.preventDefault(); + dispelModal(modalId, backgroundId); + }); + closeBtn.removeEventListener("click", closeModalHandler); + closeBtn.addEventListener("click", closeModalHandler); + } +} + +function dispelModal(modalId, backgroundId) { + const modal = document.getElementById(modalId); + const modalOverlay = document.getElementById(backgroundId); + if (modal && modalOverlay) { + modal.style.display = 'none'; + modalOverlay.style.display = 'none'; + } +} diff --git a/client/modules/Toast.js b/client/modules/Toast.js new file mode 100644 index 0000000..c5b5691 --- /dev/null +++ b/client/modules/Toast.js @@ -0,0 +1,35 @@ +export const toast = (message, type, positionAtTop = true) => { + if (message && type) { + buildAndInsertMessageElement(message, type, positionAtTop); + } +}; + +function buildAndInsertMessageElement (message, type, positionAtTop) { + cancelCurrentMessage(); + let backgroundColor; + const position = positionAtTop ? 'top:4rem;' : 'bottom: 15px;'; + switch (type) { + case 'warning': + backgroundColor = '#fff5b1'; + break; + case 'error': + backgroundColor = '#fdaeb7'; + break; + case 'success': + backgroundColor = '#bef5cb'; + break; + } + const messageEl = document.createElement("div"); + messageEl.setAttribute("id", "current-info-message"); + messageEl.setAttribute("style", 'background-color:' + backgroundColor + ';' + position) + messageEl.setAttribute("class", 'info-message'); + messageEl.innerText = message; + document.body.prepend(messageEl); +} + +function cancelCurrentMessage () { + const currentMessage = document.getElementById('current-info-message'); + if (currentMessage !== null) { + currentMessage.remove(); + } +} diff --git a/client/scripts/create.js b/client/scripts/create.js index d00368b..c17f190 100644 --- a/client/scripts/create.js +++ b/client/scripts/create.js @@ -1,3 +1,120 @@ -export const create = () => { +import { toast } from "../modules/Toast.js"; +import { ModalManager } from "../modules/ModalManager.js"; +import { defaultCards } from "../config/defaultCards.js"; +import { customCards } from "../config/customCards.js"; +import { DeckStateManager } from "../modules/DeckStateManager.js"; +export const create = () => { + let deckManager = new DeckStateManager(); + loadDefaultCards(deckManager); + loadCustomCards(deckManager); + document.getElementById("game-form").onsubmit = (e) => { + e.preventDefault(); + } + document.getElementById("custom-role-btn").addEventListener( + "click", () => { + ModalManager.displayModal( + "add-role-modal", + "add-role-modal-background", + "close-modal-button" + ) + } + ) +} + +// Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. +function loadDefaultCards(deckManager) { + defaultCards.sort((a, b) => { + return a.role.localeCompare(b.role); + }); + let deck = []; + for (let i = 0; i < defaultCards.length; i ++) { // each dropdown should include every + let card = defaultCards[i]; + card.quantity = 0; + let cardEl = constructCompactDeckBuilderElement(defaultCards[i], deckManager); + document.getElementById("deck").appendChild(cardEl); + deck.push(card); + } + deckManager.deck = deck; +} + +/* Display a dropdown containing all the custom roles. Adding one will add it to the game deck and +create a widget for it */ +function loadCustomCards(deckManager) { + let form = document.getElementById("add-card-to-deck-form"); + customCards.sort((a, b) => { + return a.role.localeCompare(b.role); + }); + let selectEl = document.createElement("select"); + selectEl.setAttribute("id", "deck-select"); + addOptionsToList(customCards, selectEl); + form.appendChild(selectEl); + let submitBtn = document.createElement("input"); + submitBtn.setAttribute("type", "submit"); + submitBtn.setAttribute("value", "Add Role to Deck"); + submitBtn.addEventListener('click', (e) => { + e.preventDefault(); + if (selectEl.selectedIndex > 0) { + deckManager.addToDeck(selectEl.value); + let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(selectEl.value), deckManager); + updateCustomRoleOptionsList(deckManager, selectEl); + document.getElementById("deck").appendChild(cardEl); + } + }) + form.appendChild(submitBtn); + + + deckManager.customRoleOptions = customCards; +} + +function updateCustomRoleOptionsList(deckManager, selectEl) { + document.querySelectorAll('#deck-select option').forEach(e => e.remove()); + addOptionsToList(deckManager.customRoleOptions, selectEl); +} + +function addOptionsToList(options, selectEl) { + let noneSelected = document.createElement("option"); + noneSelected.innerText = "None selected" + noneSelected.disabled = true; + noneSelected.selected = true; + selectEl.appendChild(noneSelected); + for (let i = 0; i < options.length; i ++) { // each dropdown should include every + let optionEl = document.createElement("option"); + optionEl.setAttribute("value", customCards[i].role); + optionEl.innerText = customCards[i].role; + selectEl.appendChild(optionEl); + } +} + +function constructCompactDeckBuilderElement(card, deckManager) { + const cardContainer = document.createElement("div"); + + cardContainer.setAttribute("class", "compact-card"); + + cardContainer.setAttribute("id", "card-" + card.role); + cardContainer.setAttribute("id", "card-" + card.role); + + cardContainer.innerHTML = + "
    " + + "

    -

    " + + "
    " + + "
    " + + "

    " + card.role + "

    " + + "
    0
    " + + "
    " + + "
    " + + "

    +

    " + + "
    "; + + cardContainer.querySelector('.compact-card-right').addEventListener('click', () => { + deckManager.addCopyOfCard(card.role); + cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; + document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; + }); + cardContainer.querySelector('.compact-card-left').addEventListener('click', () => { + deckManager.removeCopyOfCard(card.role); + cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; + document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; + }); + return cardContainer; } diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css index c1ffd0f..af97402 100644 --- a/client/styles/GLOBAL.css +++ b/client/styles/GLOBAL.css @@ -11,7 +11,68 @@ th, thead, tr, tt, u, ul, var { border: 0; background: transparent; } +@font-face { + font-family: 'diavlo'; + src: url("../webfonts/Diavlo_LIGHT_II_37.woff2") format("woff2"); +} + +@font-face { + font-family: 'signika-negative'; + src: url("../webfonts/SignikaNegative-Light.woff2") format("woff2"); +} html { - font-family: sans-serif; + font-family: 'signika-negative', sans-serif !important; + background-color: #23282b !important; +} + +body { + display: flex; + flex-direction: column; + align-items: flex-start; + width: 95%; + margin: 0 auto; + max-width: 75em; +} + +h1 { + font-family: 'diavlo', sans-serif; + color: #ab2626; + filter: drop-shadow(2px 2px 4px black); + font-size: 40px; + margin: 0.5em 0; +} + +h3 { + color: #d7d7d7; + font-family: 'signika-negative', sans-serif; + font-weight: normal; + font-size: 18px; + margin: 0.5em 0; +} + +label { + color: #d7d7d7; + font-family: 'signika-negative', sans-serif; + font-size: 18px; + font-weight: normal; +} + +button, input[type="submit"] { + font-family: 'signika-negative', sans-serif !important; + padding: 10px; + background-color: #1f1f1f; + border: none; + border-radius: 3px; + color: white; + font-size: 18px; + cursor: pointer; +} + +button:hover, input[type="submit"]:hover { + background-color: #4f4f4f; +} + +input { + padding: 10px; } diff --git a/client/styles/create.css b/client/styles/create.css index e69de29..ce3e0ee 100644 --- a/client/styles/create.css +++ b/client/styles/create.css @@ -0,0 +1,128 @@ +.compact-card { + text-align: center; + cursor: pointer; + position: relative; + margin: 0.3em; + background-color: #393a40; + color: gray; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4); + border-radius: 3px; + user-select: none; + max-width: 15em; + min-width: 12em; + display: flex; + height: max-content; +} + +.compact-card h1 { + display: flex; + align-items: center; + font-size: 14px; + margin: 0 10px 0 10px; +} + +.compact-card .card-role { + color: #bfb8b8; + margin: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + max-width: 8em; +} + +.compact-card-right p { + font-size: 40px; + margin: 0 10px 0 0; + display: flex; + justify-content: flex-end; +} + +.compact-card-left p { + font-size: 40px; + margin: 0 0 0 10px; + display: flex; + justify-content: flex-start; +} + +.compact-card-left, .compact-card-right { + width: 50%; +} + +.compact-card .card-quantity { + text-align: center; + margin: 0; +} + +.compact-card-header { + position: absolute; + left: 0; + right: 0; + margin: auto; + flex-direction: column; + align-items: center; + justify-content: center; + display: flex; + top: 0; + pointer-events: none; + text-align: center; +} + +#deck-container, #deck, #custom-roles-container { + width: fit-content; +} + +#deck-container, #custom-roles-container { + margin: 0.5em 0; +} + +#deck { + display: flex; + flex-wrap: wrap; +} + +form { + width: 100%; + margin: 1em 0; +} + +select { + padding: 10px; + font-size: 18px; + font-family: 'signika-negative', sans-serif; +} + +#deck-container, #custom-roles-container { + border: 1px solid #3d4448; + padding: 10px; + border-radius: 3px; +} + +#game-form > div { + display: flex; + flex-direction: column; + border: 1px solid #3d4448; + padding: 10px; + border-radius: 3px; + width: fit-content; +} + +#game-form > div > label { + display: flex; +} + +#game-time label, #game-time input { + margin-right: 10px; +} + +label[for="game-time"], label[for="add-card-to-deck-form"], label[for="deck"] { + color: #0075F2; + font-size: 30px; + border-radius: 3px; + margin-bottom: 10px; +} + +#create-game{ + color: #45a445; + font-size: 30px; + margin-top: 2em; +} diff --git a/client/styles/modal.css b/client/styles/modal.css new file mode 100644 index 0000000..50ddde9 --- /dev/null +++ b/client/styles/modal.css @@ -0,0 +1,42 @@ +.modal { + border-radius: 2px; + text-align: center; + position: fixed; + width: 100%; + height: 100%; + z-index: 100; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #f7f7f7; + align-items: center; + justify-content: center; + max-width: 17em; + max-height: 20em; + font-family: sans-serif; + font-size: 22px; + flex-direction: column; + padding: 1em; +} + +.modal-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: calc(100% + 100px); + background-color: rgba(0, 0, 0, 0.55); + z-index: 50; + cursor: pointer; +} + +.modal > form > div { + display: flex; + flex-direction: column; + margin-bottom: 1em; +} + +.modal > form > div > label { + display: flex; + font-size: 16px; +} diff --git a/client/views/create.html b/client/views/create.html index 5198cf1..b0bc825 100644 --- a/client/views/create.html +++ b/client/views/create.html @@ -15,16 +15,49 @@ + + +

    Create A Game

    - Creating a game gives you the moderator role. You will not be dealt a card. You will know everyone's role and can - remove any player from the game. You can also play/pause the optional timer and, if desired, delegate your moderator - role to any other player. + Creating a game gives you the moderator role with certain special permissions. You will not be dealt a card.

    +
    + +
    + +
    +
    + +
    +
    - +
    + +
    + + + + +
    +
    +
    + + + +

    Create A Game

    @@ -51,10 +67,10 @@
    + min="0" max="5" /> + min="1" max="60" />
    diff --git a/client/views/home.html b/client/views/home.html index 68fc0ba..6881505 100644 --- a/client/views/home.html +++ b/client/views/home.html @@ -20,9 +20,24 @@ + +

    This is a tool to run games of Werewolf when not in-person, or when you don't possess a deck of cards. Create a game and moderate, or join one and have a role dealt to your device.

    +
    + +
    +
    + + +
    +
    + + +
    +
    +
    + + + + + diff --git a/client/views/home.html b/client/views/home.html index 6881505..b6a810c 100644 --- a/client/views/home.html +++ b/client/views/home.html @@ -32,10 +32,7 @@ -
    - - -
    + + + + -

    404

    + + +

    404

    +

    The game or other resource that you are looking for could not be found, or you don't have permission to access it. + If this error is unexpected, the application may have restarted.

    +
    + diff --git a/client/views/create.html b/client/views/create.html index 251eb12..1250225 100644 --- a/client/views/create.html +++ b/client/views/create.html @@ -26,6 +26,7 @@
    diff --git a/client/views/game.html b/client/views/game.html index 78e0b17..cae06da 100644 --- a/client/views/game.html +++ b/client/views/game.html @@ -18,6 +18,11 @@ + +
    - diff --git a/server/main.js b/server/main.js index 5bf5dc8..5ae182f 100644 --- a/server/main.js +++ b/server/main.js @@ -43,9 +43,9 @@ logger.log('LOG LEVEL IS: ' + logLevel) if (localServer) { environment = globals.ENVIRONMENT.LOCAL; logger.log('starting main in LOCAL mode.'); - if (useHttps && fs.existsSync(path.join(__dirname, './certs/localhost-key.pem')) && fs.existsSync(path.join(__dirname, './certs/localhost.pem'))) { - const key = fs.readFileSync(path.join(__dirname, './certs/localhost-key.pem'), 'utf-8'); - const cert = fs.readFileSync(path.join(__dirname, './certs/localhost.pem'), 'utf-8'); + if (useHttps && fs.existsSync(path.join(__dirname, '../client/certs/localhost-key.pem')) && fs.existsSync(path.join(__dirname, '../client/certs/localhost.pem'))) { + const key = fs.readFileSync(path.join(__dirname, '../client/certs/localhost-key.pem'), 'utf-8'); + const cert = fs.readFileSync(path.join(__dirname, '../client/certs/localhost.pem'), 'utf-8'); logger.log('local certs detected. Using HTTPS.'); main = https.createServer({ key, cert }, app); logger.log(`navigate to https://localhost:${port}`); diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 97244bf..bc0b0d5 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -17,6 +17,7 @@ class GameManager { addGameSocketHandlers = (namespace, socket) => { this.namespace = namespace; socket.on(globals.CLIENT_COMMANDS.FETCH_GAME_STATE, (accessCode, personId, ackFn) => { + this.logger.trace('request for game state for accessCode ' + accessCode + ', person ' + personId); handleRequestForGameState( this.namespace, this.logger, From 5c869182a2080e8dafef86d2f2e4aaa7ef250cea Mon Sep 17 00:00:00 2001 From: Alec Date: Tue, 30 Nov 2021 02:50:00 -0500 Subject: [PATCH 14/60] playing and pausing the timer --- .gitignore | 2 +- client/config/globals.js | 4 +- client/modules/GameStateRenderer.js | 12 ++-- client/modules/Templates.js | 21 ++++++- client/scripts/game.js | 34 +++++++++-- server/config/globals.js | 8 ++- server/model/Game.js | 3 +- server/modules/ActiveGameRunner.js | 19 +++++- server/modules/GameManager.js | 35 ++++++++++++ server/modules/GameProcess.js | 37 ++++++++---- server/modules/GameStateCurator.js | 5 +- server/modules/ServerTimer.js | 89 ++++++++++++++++++----------- 12 files changed, 209 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 984aaaa..6af4543 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .idea node_modules/* -./client/certs/ +client/certs/ .vscode/launch.json package-lock.json diff --git a/client/config/globals.js b/client/config/globals.js index 6a16d74..9133683 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -7,7 +7,9 @@ export const globals = { COMMANDS: { FETCH_GAME_STATE: 'fetchGameState', GET_ENVIRONMENT: 'getEnvironment', - START_GAME: 'startGame' + START_GAME: 'startGame', + PAUSE_TIMER: 'pauseTimer', + RESUME_TIMER: 'resumeTimer' }, STATUS: { LOBBY: "lobby", diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js index caa2860..95af8c0 100644 --- a/client/modules/GameStateRenderer.js +++ b/client/modules/GameStateRenderer.js @@ -68,10 +68,10 @@ export class GameStateRenderer { } renderGameHeader() { - let title = document.createElement("h1"); - title.innerText = "Game"; - document.querySelector('#game-title h1')?.remove(); - document.getElementById("game-title").appendChild(title); + // let title = document.createElement("h1"); + // title.innerText = "Game"; + // document.querySelector('#game-title h1')?.remove(); + // document.getElementById("game-title").appendChild(title); } renderPlayerRole() { @@ -99,6 +99,10 @@ export class GameStateRenderer { document.getElementById("game-role").style.display = 'none'; }); } + + renderModeratorView() { + + } } function renderClient(client, container) { diff --git a/client/modules/Templates.js b/client/modules/Templates.js index bc9d995..87f44b0 100644 --- a/client/modules/Templates.js +++ b/client/modules/Templates.js @@ -45,5 +45,24 @@ export const templates = { "
    " + "

    Click to reveal your role

    " + "

    (click again to hide)

    " + - "
    " + "
    ", + MODERATOR_GAME_VIEW: + "
    " + + "

    Moderator

    " + + "
    " + + "
    " + + "" + + "
    " + + "
    " + + "
    " + + "" + + "" + + "
    " + + "
    " + + "" + + "
    " + + "
    " + + "
    " + + "
    " + + "" } diff --git a/client/scripts/game.js b/client/scripts/game.js index 67bd9ad..a739c5b 100644 --- a/client/scripts/game.js +++ b/client/scripts/game.js @@ -27,13 +27,12 @@ function prepareGamePage(environment, socket, reconnect=false) { } else { toast('You are connected.', 'success', true); console.log(gameState); - gameState.accessCode = accessCode; userId = gameState.client.id; UserUtility.setAnonymousUserId(userId, environment); let gameStateRenderer = new GameStateRenderer(gameState); const timerWorker = new Worker('../modules/Timer.js'); setClientSocketHandlers(gameStateRenderer, socket, timerWorker); - processGameState(gameState, userId, socket, gameStateRenderer, timerWorker, reconnect); // this socket is initialized via a script tag in the game page HTML. + processGameState(gameState, userId, socket, gameStateRenderer, timerWorker); } }); } else { @@ -54,15 +53,28 @@ function processGameState (gameState, userId, socket, gameStateRenderer, timerWo || gameState.userType === globals.USER_TYPES.TEMPORARY_MODERATOR ) ) { - displayStartGamePromptForModerators(gameStateRenderer); + displayStartGamePromptForModerators(gameStateRenderer, socket); } break; case globals.STATUS.IN_PROGRESS: document.querySelector("#start-game-prompt")?.remove(); gameStateRenderer.gameState = gameState; - document.getElementById("game-state-container").innerHTML = templates.GAME; gameStateRenderer.renderGameHeader(); - gameStateRenderer.renderPlayerRole(); + if (gameState.userType === globals.USER_TYPES.PLAYER || gameState.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + document.getElementById("game-state-container").innerHTML = templates.GAME; + gameStateRenderer.renderPlayerRole(); + } else if (gameState.userType === globals.USER_TYPES.MODERATOR) { + document.getElementById("game-state-container").innerHTML = templates.MODERATOR_GAME_VIEW; + gameStateRenderer.renderModeratorView(); + console.log(gameState); + console.log(gameState.accessCode); + document.getElementById("pause-button").addEventListener('click', () => { + socket.emit(globals.COMMANDS.PAUSE_TIMER, gameState.accessCode); + }); + document.getElementById("play-button").addEventListener('click', () => { + socket.emit(globals.COMMANDS.RESUME_TIMER, gameState.accessCode); + }) + } break; default: break; @@ -110,6 +122,18 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker) { ) }); } + + if(!socket.hasListeners(globals.COMMANDS.PAUSE_TIMER)) { + socket.on(globals.COMMANDS.PAUSE_TIMER, (timeRemaining) => { + console.log(timeRemaining); + }); + } + + if(!socket.hasListeners(globals.COMMANDS.RESUME_TIMER)) { + socket.on(globals.COMMANDS.RESUME_TIMER, (timeRemaining) => { + console.log(timeRemaining); + }); + } } function displayStartGamePromptForModerators(gameStateRenderer, socket) { diff --git a/server/config/globals.js b/server/config/globals.js index 947f9bb..30d8721 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -5,7 +5,9 @@ const globals = { CLIENT_COMMANDS: { FETCH_GAME_STATE: 'fetchGameState', GET_ENVIRONMENT: 'getEnvironment', - START_GAME: 'startGame' + START_GAME: 'startGame', + PAUSE_TIMER: 'pauseTimer', + RESUME_TIMER: 'resumeTimer' }, STATUS: { LOBBY: "lobby", @@ -39,7 +41,9 @@ const globals = { GAME_PROCESS_COMMANDS: { END_GAME: "endGame", START_GAME: "startGame", - START_TIMER: "startTimer" + START_TIMER: "startTimer", + PAUSE_TIMER: "pauseTimer", + RESUME_TIMER: "resumeTimer" } }; diff --git a/server/model/Game.js b/server/model/Game.js index 7c71fa7..9768b1f 100644 --- a/server/model/Game.js +++ b/server/model/Game.js @@ -1,6 +1,6 @@ class Game { constructor(accessCode, status, people, deck, hasTimer, moderator, timerParams=null) { - this.accessCode = accessCode + this.accessCode = accessCode; this.status = status; this.moderator = moderator; this.people = people; @@ -8,6 +8,7 @@ class Game { this.hasTimer = hasTimer; this.timerParams = timerParams; this.isFull = false; + this.timeRemaining = null; } } diff --git a/server/modules/ActiveGameRunner.js b/server/modules/ActiveGameRunner.js index dc5adb1..23c2d91 100644 --- a/server/modules/ActiveGameRunner.js +++ b/server/modules/ActiveGameRunner.js @@ -5,6 +5,7 @@ const globals = require('../config/globals'); class ActiveGameRunner { constructor (logger) { this.activeGames = {}; + this.timerThreads = {}; this.logger = logger; } @@ -14,6 +15,7 @@ class ActiveGameRunner { runGame = (game, namespace) => { this.logger.debug('running game ' + game.accessCode); const gameProcess = fork(path.join(__dirname, '/GameProcess.js')); + this.timerThreads[game.accessCode] = gameProcess; gameProcess.on('message', (msg) => { switch (msg.command) { case globals.GAME_PROCESS_COMMANDS.END_GAME: @@ -21,11 +23,26 @@ class ActiveGameRunner { this.logger.debug('PARENT: END GAME'); namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.END_GAME, game.accessCode); break; + case globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER: + game.timerParams.paused = true; + this.logger.trace(msg); + game.timeRemaining = msg.timeRemaining; + this.logger.debug('PARENT: PAUSE TIMER'); + namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, game.timeRemaining); + break; + case globals.GAME_PROCESS_COMMANDS.RESUME_TIMER: + game.timerParams.paused = false; + this.logger.trace(msg); + game.timeRemaining = msg.timeRemaining; + this.logger.debug('PARENT: RESUME TIMER'); + namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, game.timeRemaining); + break; } }); gameProcess.on('exit', () => { - this.logger.debug('Game ' + game.accessCode + ' has ended. Elapsed: ' + (new Date() - game.startTime) + 'ms'); + this.logger.debug('Game ' + game.accessCode + ' has ended.'); + delete this.timerThreads[game.accessCode]; }); gameProcess.send({ command: globals.GAME_PROCESS_COMMANDS.START_TIMER, diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index bc0b0d5..190b8e3 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -43,6 +43,38 @@ class GameManager { } } }); + + socket.on(globals.CLIENT_COMMANDS.PAUSE_TIMER, (accessCode) => { + this.logger.trace(accessCode); + let game = this.activeGameRunner.activeGames[accessCode]; + if (game) { + let thread = this.activeGameRunner.timerThreads[accessCode]; + if (thread) { + this.logger.debug('Timer thread found for game ' + accessCode); + thread.send({ + command: globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, + accessCode: game.accessCode, + logLevel: this.logger.logLevel + }); + } + } + }) + + socket.on(globals.CLIENT_COMMANDS.RESUME_TIMER, (accessCode) => { + this.logger.trace(accessCode); + let game = this.activeGameRunner.activeGames[accessCode]; + if (game) { + let thread = this.activeGameRunner.timerThreads[accessCode]; + if (thread) { + this.logger.debug('Timer thread found for game ' + accessCode); + thread.send({ + command: globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, + accessCode: game.accessCode, + logLevel: this.logger.logLevel + }); + } + } + }) } @@ -54,6 +86,9 @@ class GameManager { } else { const newAccessCode = this.generateAccessCode(); let moderator = initializeModerator(gameParams.moderatorName, gameParams.hasDedicatedModerator); + if (gameParams.timerParams !== null) { + gameParams.timerParams.paused = false; + } this.activeGameRunner.activeGames[newAccessCode] = new Game( newAccessCode, globals.STATUS.LOBBY, diff --git a/server/modules/GameProcess.js b/server/modules/GameProcess.js index c170a72..e4cb8dd 100644 --- a/server/modules/GameProcess.js +++ b/server/modules/GameProcess.js @@ -1,26 +1,43 @@ const globals = require('../config/globals.js'); const ServerTimer = require('./ServerTimer.js'); +let timer; + process.on('message', (msg) => { const logger = require('./Logger')(msg.logLevel); switch (msg.command) { case globals.GAME_PROCESS_COMMANDS.START_TIMER: logger.debug('CHILD PROCESS ' + msg.accessCode + ': START TIMER'); - runGameTimer(msg.hours, msg.minutes, logger).then(() => { + timer = new ServerTimer( + msg.hours, + msg.minutes, + globals.CLOCK_TICK_INTERVAL_MILLIS, + logger + ); + timer.runTimer().then(() => { logger.debug('Timer finished for ' + msg.accessCode); process.send({ command: globals.GAME_PROCESS_COMMANDS.END_GAME }); process.exit(0); }); + + break; + case globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER: + timer.stopTimer(); + logger.debug('CHILD PROCESS ' + msg.accessCode + ': PAUSE TIMER'); + process.send({ command: globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, timeRemaining: timer.currentTimeInMillis}); + + break; + + case globals.GAME_PROCESS_COMMANDS.RESUME_TIMER: + timer.resumeTimer().then(() => { + logger.debug('Timer finished for ' + msg.accessCode); + process.send({ command: globals.GAME_PROCESS_COMMANDS.END_GAME }); + process.exit(0); + }); + logger.debug('CHILD PROCESS ' + msg.accessCode + ': RESUME TIMER'); + process.send({ command: globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, timeRemaining: timer.currentTimeInMillis}); + break; } }); -function runGameTimer (hours, minutes, logger) { - const cycleTimer = new ServerTimer( - hours, - minutes, - globals.CLOCK_TICK_INTERVAL_MILLIS, - logger - ); - return cycleTimer.runTimer(); -} diff --git a/server/modules/GameStateCurator.js b/server/modules/GameStateCurator.js index 9ed1320..9680be0 100644 --- a/server/modules/GameStateCurator.js +++ b/server/modules/GameStateCurator.js @@ -19,6 +19,7 @@ function getGameStateBasedOnPermissions(game, person) { switch (person.userType) { case globals.USER_TYPES.PLAYER: return { + accessCode: game.accessCode, status: game.status, moderator: mapPerson(game.moderator), userType: globals.USER_TYPES.PLAYER, @@ -30,10 +31,11 @@ function getGameStateBasedOnPermissions(game, person) { }) .map((filteredPerson) => ({ name: filteredPerson.name, userType: filteredPerson.userType })), timerParams: game.timerParams, - isFull: game.isFull + isFull: game.isFull, } case globals.USER_TYPES.MODERATOR: return { + accessCode: game.accessCode, status: game.status, moderator: mapPerson(game.moderator), userType: globals.USER_TYPES.MODERATOR, @@ -45,6 +47,7 @@ function getGameStateBasedOnPermissions(game, person) { } case globals.USER_TYPES.TEMPORARY_MODERATOR: return { + accessCode: game.accessCode, status: game.status, moderator: mapPerson(game.moderator), userType: globals.USER_TYPES.TEMPORARY_MODERATOR, diff --git a/server/modules/ServerTimer.js b/server/modules/ServerTimer.js index 8cb439b..48379c7 100644 --- a/server/modules/ServerTimer.js +++ b/server/modules/ServerTimer.js @@ -1,60 +1,83 @@ -/* ALL TIMES ARE IN MILLIS */ -function stepFn (expected, interval, start, totalTime, ticking, timesUpResolver, logger) { +function stepFn (serverTimerInstance, expected) { const now = Date.now(); - if (now - start >= totalTime) { - clearTimeout(ticking); - logger.debug('ELAPSED: ' + (now - start) + 'ms (~' + (Math.abs(totalTime - (now - start)) / totalTime).toFixed(3) + '% error).'); - timesUpResolver(); // this is a reference to the callback defined in the construction of the promise in runTimer() + serverTimerInstance.currentTimeInMillis = serverTimerInstance.totalTime - (now - serverTimerInstance.start); + if (now - serverTimerInstance.start >= serverTimerInstance.totalTime) { + clearTimeout(serverTimerInstance.ticking); + serverTimerInstance.logger.debug( + 'ELAPSED: ' + (now - serverTimerInstance.start) + 'ms (~' + + (Math.abs(serverTimerInstance.totalTime - (now - serverTimerInstance.start)) / serverTimerInstance.totalTime).toFixed(3) + '% error).' + ); + serverTimerInstance.timesUpResolver(); // this is a reference to the callback defined in the construction of the promise in runTimer() return; } const delta = now - expected; - expected += interval; - ticking = setTimeout(function () { + expected += serverTimerInstance.interval; + serverTimerInstance.ticking = setTimeout(function () { stepFn( - expected, - interval, - start, - totalTime, - ticking, - timesUpResolver, - logger + serverTimerInstance, + expected ); - }, Math.max(0, interval - delta)); // take into account drift + }, Math.max(0, serverTimerInstance.interval - delta)); // take into account drift } class ServerTimer { + constructor (hours, minutes, tickInterval, logger) { this.hours = hours; this.minutes = minutes; this.tickInterval = tickInterval; this.logger = logger; + this.currentTimeInMillis = null; + this.ticking = null; + this.timesUpPromise = null; + this.timesUpResolver = null; + this.start = null; + this.totalTime = null; } runTimer () { - const interval = this.tickInterval; - const totalTime = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); - const logger = this.logger; - logger.debug('STARTING TIMER FOR ' + totalTime + 'ms'); - const start = Date.now(); + this.totalTime = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); + this.logger.debug('STARTING TIMER FOR ' + this.totalTime + 'ms'); + this.start = Date.now(); const expected = Date.now() + this.tickInterval; - let timesUpResolver; - const timesUpPromise = new Promise((resolve) => { - timesUpResolver = resolve; + this.timesUpPromise = new Promise((resolve) => { + this.timesUpResolver = resolve; }); - const ticking = setTimeout(function () { + const instance = this; + this.ticking = setTimeout(function () { stepFn( - expected, - interval, - start, - totalTime, - ticking, - timesUpResolver, - logger + instance, + expected ); }, this.tickInterval); - return timesUpPromise; + return this.timesUpPromise; + } + + stopTimer() { + clearTimeout(this.ticking); + let now = Date.now(); + this.logger.debug( + 'ELAPSED (PAUSE): ' + (now - this.start) + 'ms (~' + + (Math.abs(this.totalTime - (now - this.start)) / this.totalTime).toFixed(3) + '% error).' + ); + } + + resumeTimer() { + this.logger.debug('RESUMING TIMER FOR ' + this.currentTimeInMillis + 'ms'); + this.start = Date.now(); + this.totalTime = this.currentTimeInMillis; + const expected = Date.now() + this.tickInterval; + const instance = this; + this.ticking = setTimeout(function () { + stepFn( + instance, + expected + ); + }, this.tickInterval); + + return this.timesUpPromise; } } From e21ad8493f65aea02e79838127905c412bce9276 Mon Sep 17 00:00:00 2001 From: Alec Date: Tue, 30 Nov 2021 19:24:08 -0500 Subject: [PATCH 15/60] get timer on connect, patch play/pause functionality --- client/config/globals.js | 5 +- client/modules/GameTimerManager.js | 116 +++++++++++++++++++++++++++++ client/modules/Timer.js | 58 ++++++++------- client/scripts/create.js | 5 +- client/scripts/game.js | 44 ++++------- client/styles/game.css | 12 +++ client/views/404.html | 2 +- server/config/globals.js | 5 +- server/modules/ActiveGameRunner.js | 18 +++-- server/modules/GameManager.js | 8 +- server/modules/GameProcess.js | 10 +++ server/modules/GameStateCurator.js | 21 +++++- server/modules/ServerTimer.js | 4 +- 13 files changed, 230 insertions(+), 78 deletions(-) create mode 100644 client/modules/GameTimerManager.js diff --git a/client/config/globals.js b/client/config/globals.js index 9133683..db15ade 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -1,6 +1,6 @@ export const globals = { USER_SIGNATURE_LENGTH: 25, - CLOCK_TICK_INTERVAL_MILLIS: 100, + CLOCK_TICK_INTERVAL_MILLIS: 10, ACCESS_CODE_LENGTH: 6, PLAYER_ID_COOKIE_KEY: 'play-werewolf-anon-id', ACCESS_CODE_CHAR_POOL: 'abcdefghijklmnopqrstuvwxyz0123456789', @@ -9,7 +9,8 @@ export const globals = { GET_ENVIRONMENT: 'getEnvironment', START_GAME: 'startGame', PAUSE_TIMER: 'pauseTimer', - RESUME_TIMER: 'resumeTimer' + RESUME_TIMER: 'resumeTimer', + GET_TIME_REMAINING: 'getTimeRemaining' }, STATUS: { LOBBY: "lobby", diff --git a/client/modules/GameTimerManager.js b/client/modules/GameTimerManager.js new file mode 100644 index 0000000..9fac66f --- /dev/null +++ b/client/modules/GameTimerManager.js @@ -0,0 +1,116 @@ +import {globals} from "../config/globals.js"; + +export class GameTimerManager { + constructor() { + + } + + startGameTimer (hours, minutes, tickRate, soundManager, timerWorker) { + if (window.Worker) { + timerWorker.onmessage = function (e) { + if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds > 0) { + document.getElementById('game-timer').innerText = e.data.displayTime; + } + }; + const totalTime = convertFromHoursToMilliseconds(hours) + convertFromMinutesToMilliseconds(minutes); + timerWorker.postMessage({ totalTime: totalTime, tickInterval: tickRate }); + } + } + + resumeGameTimer(totalTime, tickRate, soundManager, timerWorker) { + if (window.Worker) { + let timer = document.getElementById('game-timer'); + timer.classList.remove('paused'); + timer.innerText = totalTime < 60000 + ? returnHumanReadableTime(totalTime, true) + : returnHumanReadableTime(totalTime); + timerWorker.onmessage = function (e) { + if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds > 0) { + timer.innerText = e.data.displayTime; + } + }; + timerWorker.postMessage({ totalTime: totalTime, tickInterval: tickRate }); + } + } + + pauseGameTimer(timerWorker, timeRemaining) { + if (window.Worker) { + timerWorker.postMessage('stop'); + let timer = document.getElementById('game-timer'); + timer.innerText = timeRemaining < 60000 + ? returnHumanReadableTime(timeRemaining, true) + : returnHumanReadableTime(timeRemaining); + timer.classList.add('paused'); + } + } + + displayPausedTime(time) { + let timer = document.getElementById('game-timer'); + timer.innerText = time < 60000 + ? returnHumanReadableTime(time, true) + : returnHumanReadableTime(time); + timer.classList.add('paused'); + } + + attachTimerSocketListeners(socket, timerWorker, gameStateRenderer) { + if (!socket.hasListeners(globals.EVENTS.START_TIMER)) { + socket.on(globals.EVENTS.START_TIMER, () => { + this.startGameTimer( + gameStateRenderer.gameState.timerParams.hours, + gameStateRenderer.gameState.timerParams.minutes, + globals.CLOCK_TICK_INTERVAL_MILLIS, + null, + timerWorker + ) + }); + } + + if(!socket.hasListeners(globals.COMMANDS.PAUSE_TIMER)) { + socket.on(globals.COMMANDS.PAUSE_TIMER, (timeRemaining) => { + this.pauseGameTimer(timerWorker, timeRemaining) + }); + } + + if(!socket.hasListeners(globals.COMMANDS.RESUME_TIMER)) { + socket.on(globals.COMMANDS.RESUME_TIMER, (timeRemaining) => { + this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker); + }); + } + + if(!socket.hasListeners(globals.COMMANDS.GET_TIME_REMAINING)) { + socket.on(globals.COMMANDS.GET_TIME_REMAINING, (timeRemaining, paused) => { + console.log('received time remaining from server'); + if (paused) { + this.displayPausedTime(timeRemaining); + } else { + this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker); + } + }); + } + } +} + + +function convertFromMinutesToMilliseconds(minutes) { + return minutes * 60 * 1000; +} + +function convertFromHoursToMilliseconds(hours) { + return hours * 60 * 60 * 1000; +} + +function returnHumanReadableTime(milliseconds, tenthsOfSeconds=false) { + + let tenths = Math.floor((milliseconds / 100) % 10); + let seconds = Math.floor((milliseconds / 1000) % 60); + let minutes = Math.floor((milliseconds / (1000 * 60)) % 60); + let hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); + + hours = hours < 10 ? "0" + hours : hours; + minutes = minutes < 10 ? "0" + minutes : minutes; + seconds = seconds < 10 ? "0" + seconds : seconds; + + return tenthsOfSeconds + ? hours + ":" + minutes + ":" + seconds + '.' + tenths + : hours + ":" + minutes + ":" + seconds; +} diff --git a/client/modules/Timer.js b/client/modules/Timer.js index d2fe55d..67926be 100644 --- a/client/modules/Timer.js +++ b/client/modules/Timer.js @@ -10,18 +10,20 @@ See: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API const messageParameters = { STOP: 'stop', TICK_INTERVAL: 'tickInterval', - HOURS: 'hours', - MINUTES: 'minutes' + TOTAL_TIME: 'totalTime' }; +let timer; + onmessage = function (e) { if (typeof e.data === 'object' - && e.data.hasOwnProperty(messageParameters.HOURS) - && e.data.hasOwnProperty(messageParameters.MINUTES) + && e.data.hasOwnProperty(messageParameters.TOTAL_TIME) && e.data.hasOwnProperty(messageParameters.TICK_INTERVAL) ) { - const timer = new Singleton(e.data.hours, e.data.minutes, e.data.tickInterval); + timer = new Singleton(e.data.totalTime, e.data.tickInterval); timer.startTimer(); + } else if (e.data === 'stop') { + timer.stopTimer(); } }; @@ -32,9 +34,12 @@ function stepFn (expected, interval, start, totalTime) { } const delta = now - expected; expected += interval; + let displayTime = (totalTime - (expected - start)) < 60000 + ? returnHumanReadableTime(totalTime - (expected - start), true) + : returnHumanReadableTime(totalTime - (expected - start)); postMessage({ timeRemainingInMilliseconds: totalTime - (expected - start), - displayTime: returnHumanReadableTime(totalTime - (expected - start)) + displayTime: displayTime }); Singleton.setNewTimeoutReference(setTimeout(() => { stepFn(expected, interval, start, totalTime); @@ -43,19 +48,18 @@ function stepFn (expected, interval, start, totalTime) { } class Timer { - constructor (hours, minutes, tickInterval) { + constructor (totalTime, tickInterval) { this.timeoutId = undefined; - this.hours = hours; - this.minutes = minutes; + this.totalTime = totalTime; this.tickInterval = tickInterval; } startTimer () { - if (!isNaN(this.hours) && !isNaN(this.minutes) && !isNaN(this.tickInterval)) { + if (!isNaN(this.tickInterval)) { const interval = this.tickInterval; - const totalTime = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); const start = Date.now(); const expected = Date.now() + this.tickInterval; + const totalTime = this.totalTime; if (this.timeoutId) { clearTimeout(this.timeoutId); } @@ -64,23 +68,28 @@ class Timer { }, this.tickInterval); } } + + stopTimer() { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + } } class Singleton { - constructor (hours, minutes, tickInterval) { + constructor (totalTime, tickInterval) { if (!Singleton.instance) { - Singleton.instance = new Timer(hours, minutes, tickInterval); + Singleton.instance = new Timer(totalTime, tickInterval); } else { // This allows the same timer to be configured to run for different intervals / at a different granularity. - Singleton.setNewTimerParameters(hours, minutes, tickInterval); + Singleton.setNewTimerParameters(totalTime, tickInterval); } return Singleton.instance; } - static setNewTimerParameters (hours, minutes, tickInterval) { + static setNewTimerParameters (totalTime, tickInterval) { if (Singleton.instance) { - Singleton.instance.hours = hours; - Singleton.instance.minutes = minutes; + Singleton.instance.totalTime = totalTime; Singleton.instance.tickInterval = tickInterval; } } @@ -92,16 +101,9 @@ class Singleton { } } -function convertFromMinutesToMilliseconds(minutes) { - return minutes * 60 * 1000; -} - -function convertFromHoursToMilliseconds(hours) { - return hours * 60 * 60 * 1000; -} - -function returnHumanReadableTime(milliseconds) { +function returnHumanReadableTime(milliseconds, tenthsOfSeconds=false) { + let tenths = Math.floor((milliseconds / 100) % 10); let seconds = Math.floor((milliseconds / 1000) % 60); let minutes = Math.floor((milliseconds / (1000 * 60)) % 60); let hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); @@ -110,5 +112,7 @@ function returnHumanReadableTime(milliseconds) { minutes = minutes < 10 ? "0" + minutes : minutes; seconds = seconds < 10 ? "0" + seconds : seconds; - return hours + ":" + minutes + ":" + seconds; + return tenthsOfSeconds + ? hours + ":" + minutes + ":" + seconds + '.' + tenths + : hours + ":" + minutes + ":" + seconds; } diff --git a/client/scripts/create.js b/client/scripts/create.js index 519f899..10992ca 100644 --- a/client/scripts/create.js +++ b/client/scripts/create.js @@ -13,13 +13,12 @@ export const create = () => { gameCreationStepManager.renderStep("creation-step-container", 1); } -// Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. function loadDefaultCards(deckManager) { defaultCards.sort((a, b) => { return a.role.localeCompare(b.role); }); let deck = []; - for (let i = 0; i < defaultCards.length; i ++) { // each dropdown should include every + for (let i = 0; i < defaultCards.length; i ++) { let card = defaultCards[i]; card.quantity = 0; deck.push(card); @@ -27,8 +26,6 @@ function loadDefaultCards(deckManager) { deckManager.deck = deck; } -/* Display a dropdown containing all the custom roles. Adding one will add it to the game deck and -create a widget for it */ function loadCustomRoles(deckManager) { customCards.sort((a, b) => { return a.role.localeCompare(b.role); diff --git a/client/scripts/game.js b/client/scripts/game.js index a739c5b..4d9ecad 100644 --- a/client/scripts/game.js +++ b/client/scripts/game.js @@ -3,20 +3,23 @@ import { globals } from "../config/globals.js"; import {templates} from "../modules/Templates.js"; import {GameStateRenderer} from "../modules/GameStateRenderer.js"; import {cancelCurrentToast, toast} from "../modules/Toast.js"; +import {GameTimerManager} from "../modules/GameTimerManager.js"; export const game = () => { + let timerWorker = new Worker('../modules/Timer.js'); const socket = io('/in-game'); socket.on('disconnect', () => { + timerWorker.terminate(); toast('You are disconnected.', 'error', true); }); socket.on('connect', () => { socket.emit(globals.COMMANDS.GET_ENVIRONMENT, function(returnedEnvironment) { - prepareGamePage(returnedEnvironment, socket); + prepareGamePage(returnedEnvironment, socket, timerWorker); }); }) }; -function prepareGamePage(environment, socket, reconnect=false) { +function prepareGamePage(environment, socket, timerWorker) { let userId = UserUtility.validateAnonUserSignature(environment); const splitUrl = window.location.href.split('/game/'); const accessCode = splitUrl[1]; @@ -30,9 +33,12 @@ function prepareGamePage(environment, socket, reconnect=false) { userId = gameState.client.id; UserUtility.setAnonymousUserId(userId, environment); let gameStateRenderer = new GameStateRenderer(gameState); - const timerWorker = new Worker('../modules/Timer.js'); - setClientSocketHandlers(gameStateRenderer, socket, timerWorker); - processGameState(gameState, userId, socket, gameStateRenderer, timerWorker); + let gameTimerManager; + if (gameState.timerParams) { + gameTimerManager = new GameTimerManager(); + } + setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager); + processGameState(gameState, userId, socket, gameStateRenderer); } }); } else { @@ -40,7 +46,7 @@ function prepareGamePage(environment, socket, reconnect=false) { } } -function processGameState (gameState, userId, socket, gameStateRenderer, timerWorker) { +function processGameState (gameState, userId, socket, gameStateRenderer) { switch (gameState.status) { case globals.STATUS.LOBBY: document.getElementById("game-state-container").innerHTML = templates.LOBBY; @@ -81,7 +87,7 @@ function processGameState (gameState, userId, socket, gameStateRenderer, timerWo } } -function setClientSocketHandlers(gameStateRenderer, socket, timerWorker) { +function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager) { if (!socket.hasListeners(globals.EVENTS.PLAYER_JOINED)) { socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => { toast(player.name + " joined!", "success", false); @@ -111,28 +117,8 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker) { }); } - if (!socket.hasListeners(globals.EVENTS.START_TIMER)) { - socket.on(globals.EVENTS.START_TIMER, () => { - runGameTimer( - gameStateRenderer.gameState.timerParams.hours, - gameStateRenderer.gameState.timerParams.minutes, - globals.CLOCK_TICK_INTERVAL_MILLIS, - null, - timerWorker - ) - }); - } - - if(!socket.hasListeners(globals.COMMANDS.PAUSE_TIMER)) { - socket.on(globals.COMMANDS.PAUSE_TIMER, (timeRemaining) => { - console.log(timeRemaining); - }); - } - - if(!socket.hasListeners(globals.COMMANDS.RESUME_TIMER)) { - socket.on(globals.COMMANDS.RESUME_TIMER, (timeRemaining) => { - console.log(timeRemaining); - }); + if (timerWorker && gameTimerManager) { + gameTimerManager.attachTimerSocketListeners(socket, timerWorker, gameStateRenderer); } } diff --git a/client/styles/game.css b/client/styles/game.css index a101038..b92f85c 100644 --- a/client/styles/game.css +++ b/client/styles/game.css @@ -237,6 +237,18 @@ label[for='lobby-players'] { border: 2px solid #1c8a36; } +.paused { + animation: pulse 0.75s linear infinite alternate; +} + +@keyframes pulse { + from { + color: rgba(255, 255, 255, 0.1); + } to { + color: rgba(255, 255, 255, 1); + } +} + @keyframes fade-in-slide-up { 0% { opacity: 0; diff --git a/client/views/404.html b/client/views/404.html index 4a212d6..210c111 100644 --- a/client/views/404.html +++ b/client/views/404.html @@ -71,7 +71,7 @@ } @media(max-width: 700px) { h1 { - font-size: 44vw; + font-size: 35vw; } } diff --git a/server/config/globals.js b/server/config/globals.js index 30d8721..638df4d 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -1,7 +1,7 @@ const globals = { ACCESS_CODE_CHAR_POOL: 'abcdefghijklmnopqrstuvwxyz0123456789', ACCESS_CODE_LENGTH: 6, - CLOCK_TICK_INTERVAL_MILLIS: 100, + CLOCK_TICK_INTERVAL_MILLIS: 10, CLIENT_COMMANDS: { FETCH_GAME_STATE: 'fetchGameState', GET_ENVIRONMENT: 'getEnvironment', @@ -43,7 +43,8 @@ const globals = { START_GAME: "startGame", START_TIMER: "startTimer", PAUSE_TIMER: "pauseTimer", - RESUME_TIMER: "resumeTimer" + RESUME_TIMER: "resumeTimer", + GET_TIME_REMAINING: "getTimeRemaining" } }; diff --git a/server/modules/ActiveGameRunner.js b/server/modules/ActiveGameRunner.js index 23c2d91..742e12b 100644 --- a/server/modules/ActiveGameRunner.js +++ b/server/modules/ActiveGameRunner.js @@ -19,23 +19,31 @@ class ActiveGameRunner { gameProcess.on('message', (msg) => { switch (msg.command) { case globals.GAME_PROCESS_COMMANDS.END_GAME: - game.status = globals.STATUS.ENDED; + //game.status = globals.STATUS.ENDED; + game.timerParams.paused = false; + game.timerParams.timeRemaining = 0; this.logger.debug('PARENT: END GAME'); namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.END_GAME, game.accessCode); break; case globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER: game.timerParams.paused = true; this.logger.trace(msg); - game.timeRemaining = msg.timeRemaining; + game.timerParams.timeRemaining = msg.timeRemaining; this.logger.debug('PARENT: PAUSE TIMER'); - namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, game.timeRemaining); + namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, game.timerParams.timeRemaining); break; case globals.GAME_PROCESS_COMMANDS.RESUME_TIMER: game.timerParams.paused = false; this.logger.trace(msg); - game.timeRemaining = msg.timeRemaining; + game.timerParams.timeRemaining = msg.timeRemaining; this.logger.debug('PARENT: RESUME TIMER'); - namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, game.timeRemaining); + namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, game.timerParams.timeRemaining); + break; + case globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING: + this.logger.trace(msg); + game.timerParams.timeRemaining = msg.timeRemaining; + this.logger.debug('PARENT: GET TIME REMAINING'); + namespace.to(msg.socketId).emit(globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, game.timerParams.timeRemaining, game.timerParams.paused); break; } }); diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 190b8e3..c90be74 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -220,13 +220,13 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe if (matchingPerson) { if (matchingPerson.socketId === socket.id) { logger.trace("matching person found with an established connection to the room: " + matchingPerson.name); - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson)); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } else { if (!roomContainsSocketOfMatchingPerson(namespace, matchingPerson, logger, accessCode)) { logger.trace("matching person found with a new connection to the room: " + matchingPerson.name); socket.join(accessCode); matchingPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson)); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } else { rejectClientRequestForGameState(ackFn); } @@ -235,7 +235,7 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe let personWithMatchingSocketId = findPersonWithMatchingSocketId(game.people, socket.id); if (personWithMatchingSocketId) { logger.trace("matching person found whose cookie got cleared after establishing a connection to the room: " + personWithMatchingSocketId.name); - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, personWithMatchingSocketId)); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, personWithMatchingSocketId, gameRunner, socket, logger)); } else { let unassignedPerson = game.moderator.assigned === false ? game.moderator @@ -245,7 +245,7 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe socket.join(accessCode); unassignedPerson.assigned = true; unassignedPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson)); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson, gameRunner, socket, logger)); let isFull = isGameFull(game); game.isFull = isFull; socket.to(accessCode).emit( diff --git a/server/modules/GameProcess.js b/server/modules/GameProcess.js index e4cb8dd..60b3fca 100644 --- a/server/modules/GameProcess.js +++ b/server/modules/GameProcess.js @@ -37,6 +37,16 @@ process.on('message', (msg) => { logger.debug('CHILD PROCESS ' + msg.accessCode + ': RESUME TIMER'); process.send({ command: globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, timeRemaining: timer.currentTimeInMillis}); + break; + + case globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING: + logger.debug('CHILD PROCESS ' + msg.accessCode + ': GET TIME REMAINING'); + process.send({ + command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, + timeRemaining: timer.currentTimeInMillis, + socketId: msg.socketId + }); + break; } }); diff --git a/server/modules/GameStateCurator.js b/server/modules/GameStateCurator.js index 9680be0..47b402d 100644 --- a/server/modules/GameStateCurator.js +++ b/server/modules/GameStateCurator.js @@ -1,12 +1,15 @@ const globals = require("../config/globals") const GameStateCurator = { - getGameStateFromPerspectiveOfPerson: (game, person) => { - return getGameStateBasedOnPermissions(game, person); + getGameStateFromPerspectiveOfPerson: (game, person, gameRunner, socket, logger) => { + if (game.timerParams && game.status === globals.STATUS.IN_PROGRESS) { + getTimeRemaining(game.accessCode, gameRunner, socket, logger) + } + return getGameStateBasedOnPermissions(game, person, gameRunner); } } -function getGameStateBasedOnPermissions(game, person) { +function getGameStateBasedOnPermissions(game, person, gameRunner) { let client = game.status === globals.STATUS.LOBBY // people won't be able to know their role until past the lobby stage. ? { name: person.name, id: person.id } : { @@ -89,4 +92,16 @@ function mapPerson(person) { return { name: person.name }; } +function getTimeRemaining(accessCode, gameRunner, socket, logger) { + let thread = gameRunner.timerThreads[accessCode]; + if (thread) { + thread.send({ + command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, + accessCode: accessCode, + socketId: socket.id, + logLevel: logger.logLevel + }); + } +} + module.exports = GameStateCurator; diff --git a/server/modules/ServerTimer.js b/server/modules/ServerTimer.js index 48379c7..e17f801 100644 --- a/server/modules/ServerTimer.js +++ b/server/modules/ServerTimer.js @@ -37,7 +37,9 @@ class ServerTimer { } runTimer () { - this.totalTime = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); + let total = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); + this.totalTime = total; + this.currentTimeInMillis = total; this.logger.debug('STARTING TIMER FOR ' + this.totalTime + 'ms'); this.start = Date.now(); const expected = Date.now() + this.tickInterval; From 7f86aa22aa25230f3b47c5a4c26187bd49c690eb Mon Sep 17 00:00:00 2001 From: Alec Date: Tue, 30 Nov 2021 22:21:30 -0500 Subject: [PATCH 16/60] some improvements to the lobby view --- client/config/globals.js | 7 ++++++- client/modules/GameStateRenderer.js | 21 ++++++++----------- client/modules/Templates.js | 6 +----- client/scripts/game.js | 14 +++++++++++++ client/styles/game.css | 31 +++++++++++++++++++++++++++++ client/views/game.html | 7 +++++++ server/config/globals.js | 2 +- 7 files changed, 68 insertions(+), 20 deletions(-) diff --git a/client/config/globals.js b/client/config/globals.js index db15ade..ea4eb80 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -28,10 +28,15 @@ export const globals = { USER_TYPES: { MODERATOR: "moderator", PLAYER: "player", - TEMPORARY_MODERATOR: "temporary moderator" + TEMPORARY_MODERATOR: "player / temp mod" }, ENVIRONMENT: { LOCAL: "local", PRODUCTION: "production" + }, + USER_TYPE_ICONS: { + PLAYER: ' \uD83C\uDFAE', + MODERATOR: ' \uD83D\uDC51', + TEMP_MOD: ' \uD83C\uDFAE\uD83D\uDC51' } }; diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js index 95af8c0..5a2725b 100644 --- a/client/modules/GameStateRenderer.js +++ b/client/modules/GameStateRenderer.js @@ -11,7 +11,10 @@ export class GameStateRenderer { document.querySelectorAll('.lobby-player').forEach((el) => el.remove()) let lobbyPlayersContainer = document.getElementById("lobby-players"); if (this.gameState.userType !== globals.USER_TYPES.MODERATOR) { - renderClient(this.gameState.client, lobbyPlayersContainer); + let modEl = document.createElement("div"); + modEl.innerText = this.gameState.moderator.name; + modEl.classList.add('lobby-player'); + lobbyPlayersContainer.appendChild(modEl); } for (let person of this.gameState.people) { let personEl = document.createElement("div"); @@ -26,7 +29,7 @@ export class GameStateRenderer { playerCount = 1 + this.gameState.people.length; } document.querySelector("label[for='lobby-players']").innerText = - "Players ( " + playerCount + " / " + getGameSize(this.gameState.deck) + " )"; + "Other People ( " + playerCount + " )"; } renderLobbyHeader() { @@ -47,15 +50,6 @@ export class GameStateRenderer { let copyImg = document.createElement("img"); copyImg.setAttribute("src", "../images/copy.svg"); gameLinkContainer.appendChild(copyImg); - - let moderatorContainer = document.getElementById("moderator"); - let text, modClass; - if (this.gameState.userType === globals.USER_TYPES.MODERATOR || this.gameState.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { - moderatorContainer.innerText = this.gameState.moderator.name + " (you)"; - moderatorContainer.classList.add('moderator-client'); - } else { - moderatorContainer.innerText = this.gameState.moderator.name; - } } renderLobbyFooter() { @@ -105,8 +99,9 @@ export class GameStateRenderer { } } -function renderClient(client, container) { - let clientEl = document.createElement("div"); +function renderLobbyPlayer(name, userType) { + let el = document.createElement("div"); + el.innerHTML = "" clientEl.innerText = client.name + ' (you)'; clientEl.classList.add('lobby-player'); clientEl.classList.add('lobby-player-client'); diff --git a/client/modules/Templates.js b/client/modules/Templates.js index 87f44b0..dcdf771 100644 --- a/client/modules/Templates.js +++ b/client/modules/Templates.js @@ -7,14 +7,10 @@ export const templates = { "" + "
    " + "
    " + - "
    " + - "" + - "
    " + - "
    " + "" + "
    " + "
    " + - "" + + "" + "
    " + "
    " + "
    +
    + +
    +
    +
    +
    +
    - - - + + + + + + + + + + diff --git a/server/main.js b/server/main.js index 5ae182f..0638610 100644 --- a/server/main.js +++ b/server/main.js @@ -90,12 +90,17 @@ app.use('/favicon.ico', (req, res) => { }); const router = require('./routes/router'); -const faviconRouter = require('./routes/favicon-router'); -const staticRouter = require('./routes/static-router'); - app.use('', router); -app.use('', staticRouter); -app.use('', faviconRouter); + +app.use('/images', express.static(path.join(__dirname, '../client/images'))); +app.use('/scripts', express.static(path.join(__dirname, '../client/scripts'))); +app.use('/styles', express.static(path.join(__dirname, '../client/styles'))); +app.use('/styles/third-party', express.static(path.join(__dirname, '../client/styles/third_party'))); +app.use('/modules/third-party', express.static(path.join(__dirname, '../client/modules/third_party'))) +app.use('/modules', express.static(path.join(__dirname, '../client/modules'))) +app.use('/model', express.static(path.join(__dirname, '../client/model'))) +app.use('/config', express.static(path.join(__dirname, '../client/config'))) +app.use('/webfonts', express.static(path.join(__dirname, '../client/webfonts'))); app.use(function (req, res) { res.sendFile(path.join(__dirname, '../client/views/404.html')); diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 9d30c5c..6729059 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -244,7 +244,16 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe matchingPerson.socketId = socket.id; ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } else { - rejectClientRequestForGameState(ackFn); + logger.trace('this person is already associated with a socket connection'); + let alreadyConnectedSocket = namespace.connected[matchingPerson.socketId]; + if (alreadyConnectedSocket && alreadyConnectedSocket.leave) { + alreadyConnectedSocket.leave(accessCode, ()=> { + logger.trace('kicked existing connection out of room ' + accessCode); + socket.join(accessCode); + matchingPerson.socketId = socket.id; + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); + }) + } } } } else { @@ -271,11 +280,13 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe ); } else { rejectClientRequestForGameState(ackFn); + logger.trace('this game is full'); } } } } else { rejectClientRequestForGameState(ackFn); + logger.trace('the game' + accessCode + ' was not found'); } } diff --git a/server/modules/GameStateCurator.js b/server/modules/GameStateCurator.js index ad2543a..059c00b 100644 --- a/server/modules/GameStateCurator.js +++ b/server/modules/GameStateCurator.js @@ -71,7 +71,8 @@ function mapPeopleForModerator(people, client) { userType: person.userType, gameRole: person.gameRole, gameRoleDescription: person.gameRoleDescription, - alignment: person.alignment + alignment: person.alignment, + out: person.out })); } @@ -82,12 +83,13 @@ function mapPeopleForTempModerator(people, client) { }) .map((person) => ({ name: person.name, - userType: person.userType + userType: person.userType, + out: person.out })); } function mapPerson(person) { - return { name: person.name, userType: person.userType }; + return { name: person.name, userType: person.userType, out: person.out }; } module.exports = GameStateCurator; diff --git a/server/modules/ServerTimer.js b/server/modules/ServerTimer.js index 32a9049..603a93d 100644 --- a/server/modules/ServerTimer.js +++ b/server/modules/ServerTimer.js @@ -1,14 +1,14 @@ function stepFn (serverTimerInstance, expected) { - const now = Date.now(); + const now = Date.now(); // serverTimerInstance.currentTimeInMillis = serverTimerInstance.totalTime - (now - serverTimerInstance.start); - if (now - serverTimerInstance.start >= serverTimerInstance.totalTime) { - clearTimeout(serverTimerInstance.ticking); + if (now - serverTimerInstance.start >= serverTimerInstance.totalTime) { // check if the time has elapsed serverTimerInstance.logger.debug( 'ELAPSED: ' + (now - serverTimerInstance.start) + 'ms (~' + (Math.abs(serverTimerInstance.totalTime - (now - serverTimerInstance.start)) / serverTimerInstance.totalTime).toFixed(3) + '% error).' ); serverTimerInstance.timesUpResolver(); // this is a reference to the callback defined in the construction of the promise in runTimer() + clearTimeout(serverTimerInstance.ticking); return; } const delta = now - expected; @@ -64,10 +64,6 @@ class ServerTimer { clearTimeout(this.ticking); } let now = Date.now(); - this.logger.debug( - 'ELAPSED (PAUSE): ' + (now - this.start) + 'ms (~' - + (Math.abs(this.totalTime - (now - this.start)) / this.totalTime).toFixed(3) + '% error).' - ); } resumeTimer() { diff --git a/server/routes/favicon-router.js b/server/routes/favicon-router.js deleted file mode 100644 index bb101f0..0000000 --- a/server/routes/favicon-router.js +++ /dev/null @@ -1,18 +0,0 @@ -const express = require('express'); -const faviconRouter = express.Router(); -const path = require('path'); -const checkIfFileExists = require("./util"); - -faviconRouter.use('/client/favicon_package/*', (req, res) => { - let filePath = path.join(__dirname, ('../../' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.png' || extension === '.ico' || extension === '.svg' || extension === 'xml')) { - res.sendFile(filePath); - } else { - res.sendStatus(404); - } - }); -}); - -module.exports = faviconRouter; diff --git a/server/routes/router.js b/server/routes/router.js index c878d9f..37da71a 100644 --- a/server/routes/router.js +++ b/server/routes/router.js @@ -1,5 +1,5 @@ const express = require('express'); -const router = express.Router(); +const router = express.Router({ strict: true }); const path = require('path'); router.get('/', function (request, response) { diff --git a/server/routes/static-router.js b/server/routes/static-router.js deleted file mode 100644 index 0f99a98..0000000 --- a/server/routes/static-router.js +++ /dev/null @@ -1,115 +0,0 @@ -const express = require('express'); -const staticRouter = express.Router(); -const path = require('path'); -const checkIfFileExists = require("./util"); - -staticRouter.use('/styles/**', (req, res) => { - let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.css' || extension === '.min.css')) { - res.sendFile(filePath); - } else { - res.sendStatus(404); - } - }); -}); - -staticRouter.use('/client/webfonts/*', (req, res) => { - let filePath = path.join(__dirname, ('../' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.ttf' || extension === '.woff2')) { - res.sendFile(filePath); - } else { - res.sendStatus(404); - } - }); -}); - -staticRouter.use('/images/*', (req, res) => { - let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.svg' || extension === '.png' || extension === '.jpg' || extension === '.gif')) { - res.sendFile(filePath); - } else { - res.sendStatus(404); - } - }); -}); - -staticRouter.use('/scripts/*', (req, res) => { - let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.js')) { - res.sendFile(filePath); - } else { - res.sendStatus(404); - } - }); -}); - -staticRouter.use('/webfonts/*', (req, res) => { - let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.woff2')) { - res.sendFile(filePath); - } else { - res.sendStatus(404); - } - }); -}); - -staticRouter.use('/views/*', (req, res) => { - let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.html')) { - res.sendFile(filePath); - } else { - res.sendFile('../views/404.html'); - } - }); -}); - - -staticRouter.use('/config/*', (req, res) => { - let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.js')) { - res.sendFile(filePath); - } else { - res.sendFile('../views/404.html'); - } - }); -}); - -staticRouter.use('/modules/**', (req, res) => { - let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.js' || extension === '.min.js')) { - res.sendFile(filePath); - } else { - res.sendFile('../views/404.html'); - } - }); -}); - -staticRouter.use('/model/**', (req, res) => { - let filePath = path.join(__dirname, ('../../client/' + req.baseUrl)); - let extension = path.extname(filePath); - checkIfFileExists(filePath).then((fileExists) => { - if (fileExists && (extension === '.js')) { - res.sendFile(filePath); - } else { - res.sendFile('../views/404.html'); - } - }); -}); - -module.exports = staticRouter; From bad4e3dfc4671591193a8a30dbc637b60e923f55 Mon Sep 17 00:00:00 2001 From: Alec Date: Tue, 7 Dec 2021 01:49:16 -0500 Subject: [PATCH 20/60] gcloud, crude killing players functionality --- .gcloudignore | 17 ++++ app.yaml | 1 + client/config/globals.js | 3 +- client/modules/GameStateRenderer.js | 138 ++++++++++++++++++++-------- client/modules/Templates.js | 21 +++-- client/scripts/game.js | 14 +-- client/styles/game.css | 23 ++++- server/api/GamesAPI.js | 2 +- server/config/globals.js | 3 +- server/model/Person.js | 3 +- server/modules/GameManager.js | 24 +++-- server/modules/GameStateCurator.js | 12 ++- 12 files changed, 187 insertions(+), 74 deletions(-) create mode 100644 .gcloudignore create mode 100644 app.yaml diff --git a/.gcloudignore b/.gcloudignore new file mode 100644 index 0000000..4d7d693 --- /dev/null +++ b/.gcloudignore @@ -0,0 +1,17 @@ +# This file specifies files that are *not* uploaded to Google Cloud +# using gcloud. It follows the same syntax as .gitignore, with the addition of +# "#!include" directives (which insert the entries of the given .gitignore-style +# file at that point). +# +# For more information, run: +# $ gcloud topic gcloudignore +# +.gcloudignore +# If you would like to upload your .git directory, .gitignore file or files +# from your .gitignore file, remove the corresponding line +# below: +.git +.gitignore + +# Node.js dependencies: +node_modules/ \ No newline at end of file diff --git a/app.yaml b/app.yaml new file mode 100644 index 0000000..7fde67a --- /dev/null +++ b/app.yaml @@ -0,0 +1 @@ +runtime: nodejs14 diff --git a/client/config/globals.js b/client/config/globals.js index 17ebd04..8eec0a7 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -11,7 +11,8 @@ export const globals = { START_GAME: 'startGame', PAUSE_TIMER: 'pauseTimer', RESUME_TIMER: 'resumeTimer', - GET_TIME_REMAINING: 'getTimeRemaining' + GET_TIME_REMAINING: 'getTimeRemaining', + KILL_PLAYER: 'killPlayer' }, STATUS: { LOBBY: "lobby", diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js index c545ac7..46fb670 100644 --- a/client/modules/GameStateRenderer.js +++ b/client/modules/GameStateRenderer.js @@ -3,8 +3,10 @@ import { toast } from "./Toast.js"; import {templates} from "./Templates.js"; export class GameStateRenderer { - constructor(gameState) { + constructor(gameState, socket) { this.gameState = gameState; + this.socket = socket; + this.killPlayerHandlers = {}; this.cardFlipped = false; } @@ -61,38 +63,26 @@ export class GameStateRenderer { // document.getElementById("game-title").appendChild(title); } - renderPlayerRole() { - let name = document.querySelector('#role-name'); - name.innerText = this.gameState.client.gameRole; - if (this.gameState.client.alignment === globals.ALIGNMENT.GOOD) { - name.classList.add('good'); - } else { - name.classList.add('evil'); - } - name.setAttribute("title", this.gameState.client.gameRole); - document.querySelector('#role-description').innerText = this.gameState.client.gameRoleDescription; - document.getElementById("role-image").setAttribute( - 'src', - '../images/roles/' + this.gameState.client.gameRole.replaceAll(' ', '') + '.png' - ); - - document.getElementById("game-role-back").addEventListener('click', () => { - document.getElementById("game-role").style.display = 'flex'; - document.getElementById("game-role-back").style.display = 'none'; - }); - - document.getElementById("game-role").addEventListener('click', () => { - document.getElementById("game-role-back").style.display = 'flex'; - document.getElementById("game-role").style.display = 'none'; - }); - } - renderModeratorView() { let div = document.createElement("div"); div.innerHTML = templates.END_GAME_PROMPT; document.body.appendChild(div); - renderModeratorPlayers(this.gameState.people); + renderPlayersWithRoleAndAlignmentInfo(this.gameState.people, this.socket, this.gameState.accessCode, this.killPlayerHandlers); } + + renderPlayerView() { + renderPlayerRole(this.gameState); + renderPlayersWithNoRoleInformation(this.gameState.people, this.killPlayerHandlers); + } + + refreshPlayerList(isModerator) { + if (isModerator) { + renderPlayersWithRoleAndAlignmentInfo(this.gameState.people, this.socket, this.gameState.accessCode, this.killPlayerHandlers) + } else { + renderPlayersWithNoRoleInformation(this.gameState.people, this.killPlayerHandlers); + } + } + } function renderLobbyPerson(name, userType) { @@ -125,27 +115,95 @@ function removeExistingTitle() { } } -function renderModeratorPlayers(people) { - people.sort(); +function renderPlayersWithRoleAndAlignmentInfo(people, socket, accessCode, handlers) { + document.querySelectorAll('.game-player').forEach((el) => { + let pointer = el.dataset.pointer; + if (pointer && handlers[pointer]) { + el.removeEventListener('click', handlers[pointer]) + } + el.remove(); + }); + people.sort((a, b) => { + return a.name >= b.name ? 1 : -1; + }); let teamGood = people.filter((person) => person.alignment === globals.ALIGNMENT.GOOD); let teamEvil = people.filter((person) => person.alignment === globals.ALIGNMENT.EVIL); - renderGroupOfPlayersFromTeam(teamEvil, globals.ALIGNMENT.EVIL); - renderGroupOfPlayersFromTeam(teamGood, globals.ALIGNMENT.GOOD); + renderGroupOfPlayers(teamEvil, handlers, accessCode, globals.ALIGNMENT.EVIL, true, socket); + renderGroupOfPlayers(teamGood, handlers, accessCode, globals.ALIGNMENT.GOOD, true, socket); document.getElementById("players-alive-label").innerText = 'Players: ' + people.filter((person) => !person.out).length + ' / ' + people.length + ' Alive'; } -function renderGroupOfPlayersFromTeam(players, alignment) { +function renderPlayersWithNoRoleInformation(people, handlers) { + document.querySelectorAll('.game-player').forEach((el) => el.remove()); + people.sort((a, b) => { + return a.name >= b.name ? 1 : -1; + }); + renderGroupOfPlayers(people, handlers); + document.getElementById("players-alive-label").innerText = + 'Players: ' + people.filter((person) => !person.out).length + ' / ' + people.length + ' Alive'; + +} + +function renderGroupOfPlayers(players, handlers, accessCode=null, alignment=null, moderator=false, socket=null) { for (let player of players) { let container = document.createElement("div"); - container.classList.add('moderator-player'); - container.innerHTML = templates.MODERATOR_PLAYER; - container.querySelector('.moderator-player-name').innerText = player.name; - let roleElement = container.querySelector('.moderator-player-role') - roleElement.innerText = player.gameRole; - roleElement.classList.add(alignment); + container.classList.add('game-player'); + container.dataset.pointer = player.id; + if (alignment) { + container.innerHTML = templates.MODERATOR_PLAYER; + } else { + container.innerHTML = templates.GAME_PLAYER; + } + container.querySelector('.game-player-name').innerText = player.name; + let roleElement = container.querySelector('.game-player-role') - document.getElementById("player-list-moderator-team-" + alignment).appendChild(container); + if (alignment) { + roleElement.classList.add(alignment); + roleElement.innerText = player.gameRole; + document.getElementById("player-list-moderator-team-" + alignment).appendChild(container); + } else { + roleElement.innerText = "Unknown" + document.getElementById("game-player-list").appendChild(container); + } + + if (moderator) { + handlers[player.id] = () => { + socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id); + } + if (player.out) { + container.classList.add('killed'); + container.querySelector('.kill-player-button').remove(); + } else { + container.querySelector('.kill-player-button').addEventListener('click', handlers[player.id]); + } + } } } + +function renderPlayerRole(gameState) { + let name = document.querySelector('#role-name'); + name.innerText = gameState.client.gameRole; + if (gameState.client.alignment === globals.ALIGNMENT.GOOD) { + name.classList.add('good'); + } else { + name.classList.add('evil'); + } + name.setAttribute("title", gameState.client.gameRole); + document.querySelector('#role-description').innerText = gameState.client.gameRoleDescription; + document.getElementById("role-image").setAttribute( + 'src', + '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' + ); + + document.getElementById("game-role-back").addEventListener('click', () => { + document.getElementById("game-role").style.display = 'flex'; + document.getElementById("game-role-back").style.display = 'none'; + }); + + document.getElementById("game-role").addEventListener('click', () => { + document.getElementById("game-role-back").style.display = 'flex'; + document.getElementById("game-role").style.display = 'none'; + }); +} diff --git a/client/modules/Templates.js b/client/modules/Templates.js index 44418d1..8c1e41f 100644 --- a/client/modules/Templates.js +++ b/client/modules/Templates.js @@ -25,16 +25,12 @@ export const templates = { "
    " + "" + "
    ", - GAME: + PLAYER_GAME_VIEW: "
    " + "
    " + "" + "
    " + "
    " + - "
    " + - "" + - "
    " + - "
    " + "
    " + "", MODERATOR_PLAYER: "
    " + "
    " + diff --git a/client/scripts/game.js b/client/scripts/game.js index 9e59192..0988a8c 100644 --- a/client/scripts/game.js +++ b/client/scripts/game.js @@ -41,7 +41,6 @@ function prepareGamePage(environment, socket, timerWorker) { gameTimerManager = new GameTimerManager(gameState, socket); } setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager); - displayClientInfo(gameState.client.name, gameState.client.userType); processGameState(gameState, userId, socket, gameStateRenderer); } }); @@ -51,6 +50,7 @@ function prepareGamePage(environment, socket, timerWorker) { } function processGameState (gameState, userId, socket, gameStateRenderer) { + displayClientInfo(gameState.client.name, gameState.client.userType); switch (gameState.status) { case globals.STATUS.LOBBY: document.getElementById("game-state-container").innerHTML = templates.LOBBY; @@ -67,7 +67,6 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { } break; case globals.STATUS.IN_PROGRESS: - gameStateRenderer.gameState = gameState; gameStateRenderer.renderGameHeader(); switch (gameState.client.userType) { case globals.USER_TYPES.PLAYER: @@ -75,6 +74,7 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { gameStateRenderer.renderPlayerView(); break; case globals.USER_TYPES.KILLED_PLAYER: + document.querySelector("#end-game-prompt")?.remove(); document.getElementById("game-state-container").innerHTML = templates.PLAYER_GAME_VIEW; gameStateRenderer.renderPlayerView(true); break; @@ -85,6 +85,13 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { break; case globals.USER_TYPES.TEMPORARY_MODERATOR: document.querySelector("#start-game-prompt")?.remove(); + document.getElementById("game-state-container").innerHTML = templates.TEMP_MOD_GAME_VIEW; + gameStateRenderer.renderTempModView(); + break; + case globals.USER_TYPES.SPECTATOR: + document.querySelector("#end-game-prompt")?.remove(); + document.getElementById("game-state-container").innerHTML = templates.SPECTATOR_GAME_VIEW; + gameStateRenderer.renderSpectatorView(); break; default: break; @@ -127,6 +134,8 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim gameStateRenderer.gameState.accessCode, gameStateRenderer.gameState.client.cookie, function (gameState) { + gameStateRenderer.gameState = gameState; + gameTimerManager.gameState = gameState; processGameState(gameState, gameState.client.cookie, socket, gameStateRenderer); } ); @@ -156,7 +165,11 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim } else { toast(killedPerson.name + ' was killed!', 'warning', false, true, 6); } - gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(); + if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); + } else { + gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); + } } } }); @@ -178,7 +191,11 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim } else { toast(revealedPerson.name + ' was revealed as a ' + revealedPerson.gameRole + '!', 'warning', false, true, 6); } - gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(); + if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); + } else { + gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); + } } } }); diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css index 6a51085..c00c31e 100644 --- a/client/styles/GLOBAL.css +++ b/client/styles/GLOBAL.css @@ -96,7 +96,6 @@ button:active, input[type=submit]:active { flex-direction: column; justify-content: center; width: 95%; - max-width: 68em; margin: 0 auto; } diff --git a/client/styles/game.css b/client/styles/game.css index fa53cc7..e703ee9 100644 --- a/client/styles/game.css +++ b/client/styles/game.css @@ -454,6 +454,14 @@ label[for='moderator'] { font-size: 25px; } +#transfer-mod-form { + width: 100%; +} + +#transfer-mod-form #modal-button-container { + justify-content: center; +} + @media(max-width: 685px) { #end-game-button { font-size: 25px; diff --git a/server/config/globals.js b/server/config/globals.js index 53f24ba..45d1b7b 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -10,7 +10,8 @@ const globals = { RESUME_TIMER: 'resumeTimer', GET_TIME_REMAINING: 'getTimeRemaining', KILL_PLAYER: 'killPlayer', - REVEAL_PLAYER: 'revealPlayer' + REVEAL_PLAYER: 'revealPlayer', + TRANSFER_MODERATOR: 'transferModerator' }, STATUS: { LOBBY: "lobby", diff --git a/server/model/Game.js b/server/model/Game.js index 9768b1f..0e4c6f1 100644 --- a/server/model/Game.js +++ b/server/model/Game.js @@ -9,6 +9,7 @@ class Game { this.timerParams = timerParams; this.isFull = false; this.timeRemaining = null; + this.spectators = []; } } diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 9dd1078..ae4ae0f 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -125,6 +125,33 @@ class GameManager { }) } } + }); + + socket.on(globals.CLIENT_COMMANDS.TRANSFER_MODERATOR, (accessCode, personId) => { + let game = this.activeGameRunner.activeGames[accessCode]; + if (game) { + let person = game.people.find((person) => person.id === personId) + if (!person) { + person = game.spectators.find((spectator) => spectator.id === personId) + } + if (person && (person.out || person.userType === globals.USER_TYPES.SPECTATOR)) { + this.logger.debug('game ' + accessCode + ': transferring mod powers to ' + person.name); + if (game.people.includes(game.moderator)) { // the current moderator was at one point a dealt-in player. + game.moderator.userType = globals.USER_TYPES.KILLED_PLAYER; // restore their state from before being made mod. + } else { + game.moderator.userType = globals.USER_TYPES.SPECTATOR; + if (!game.spectators.includes(game.moderator)) { + game.spectators.push(game.moderator); + } + if (game.spectators.includes(person)) { + game.spectators.splice(game.spectators.indexOf(person), 1); + } + } + person.userType = globals.USER_TYPES.MODERATOR; + game.moderator = person; + namespace.in(accessCode).emit(globals.EVENTS.SYNC_GAME_STATE); + } + } }) } @@ -265,7 +292,10 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe const game = gameRunner.activeGames[accessCode]; if (game) { let matchingPerson = game.people.find((person) => person.cookie === personCookie); - if (!matchingPerson && game.moderator.cookie === personCookie) { + if (!matchingPerson) { + matchingPerson = game.spectators.find((spectator) => spectator.cookie = personCookie); + } + if (game.moderator.cookie === personCookie) { matchingPerson = game.moderator; } if (matchingPerson) { diff --git a/server/modules/GameStateCurator.js b/server/modules/GameStateCurator.js index 44e6722..09a4b95 100644 --- a/server/modules/GameStateCurator.js +++ b/server/modules/GameStateCurator.js @@ -34,7 +34,6 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { people: game.people .filter((person) => { return person.assigned === true - && (person.userType !== globals.USER_TYPES.MODERATOR && person.userType !== globals.USER_TYPES.TEMPORARY_MODERATOR) }) .map((filteredPerson) => mapPerson(filteredPerson)), timerParams: game.timerParams, @@ -49,7 +48,8 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { deck: game.deck, people: mapPeopleForModerator(game.people, client), timerParams: game.timerParams, - isFull: game.isFull + isFull: game.isFull, + spectators: game.spectators } case globals.USER_TYPES.TEMPORARY_MODERATOR: return { @@ -58,19 +58,38 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { moderator: mapPerson(game.moderator), client: client, deck: game.deck, - people: mapPeopleForTempModerator(game.people, client), + people: game.people + .filter((person) => { + return person.assigned === true + }) + .map((filteredPerson) => mapPerson(filteredPerson)), timerParams: game.timerParams, isFull: game.isFull } + case globals.USER_TYPES.SPECTATOR: + return { + accessCode: game.accessCode, + status: game.status, + moderator: mapPerson(game.moderator), + client: client, + deck: game.deck, + people: game.people + .filter((person) => { + return person.assigned === true + }) + .map((filteredPerson) => mapPerson(filteredPerson)), + timerParams: game.timerParams, + isFull: game.isFull, + } default: break; } } -function mapPeopleForModerator(people, client) { +function mapPeopleForModerator(people) { return people .filter((person) => { - return person.assigned === true && person.cookie !== client.cookie + return person.assigned === true }) .map((person) => ({ name: person.name, @@ -84,20 +103,6 @@ function mapPeopleForModerator(people, client) { })); } -function mapPeopleForTempModerator(people, client) { - return people - .filter((person) => { - return person.assigned === true && person.cookie !== client.cookie - }) - .map((person) => ({ - name: person.name, - id: person.id, - userType: person.userType, - out: person.out, - revealed: person.revealed - })); -} - function mapPerson(person) { if (person.revealed) { return { From 82ca096600c31a6e374e44f40eb1d21fb85be996 Mon Sep 17 00:00:00 2001 From: Alec Date: Mon, 20 Dec 2021 00:28:40 -0500 Subject: [PATCH 24/60] some name change stuff --- client/config/globals.js | 9 +- client/modules/GameCreationStepManager.js | 7 +- client/modules/GameStateRenderer.js | 86 +++++++++++--- client/modules/Templates.js | 49 +++++++- client/scripts/game.js | 132 ++++++++++++++++------ client/styles/GLOBAL.css | 117 +++++++++++++++++++ client/styles/create.css | 15 ++- client/styles/game.css | 80 ++++++++++--- client/views/create.html | 15 +-- client/views/game.html | 30 +++-- server/config/globals.js | 6 +- server/model/Person.js | 1 + server/modules/ActiveGameRunner.js | 8 +- server/modules/GameManager.js | 88 ++++++++++++--- server/modules/GameProcess.js | 8 +- server/modules/GameStateCurator.js | 8 +- 16 files changed, 534 insertions(+), 125 deletions(-) diff --git a/client/config/globals.js b/client/config/globals.js index 333dc99..23feb1e 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -14,7 +14,8 @@ export const globals = { GET_TIME_REMAINING: 'getTimeRemaining', KILL_PLAYER: 'killPlayer', REVEAL_PLAYER: 'revealPlayer', - TRANSFER_MODERATOR: 'transferModerator' + TRANSFER_MODERATOR: 'transferModerator', + CHANGE_NAME: 'changeName' }, STATUS: { LOBBY: "lobby", @@ -24,12 +25,16 @@ export const globals = { GOOD: "good", EVIL: "evil" }, + MESSAGES: { + ENTER_NAME: "Client must enter name." + }, EVENTS: { PLAYER_JOINED: "playerJoined", SYNC_GAME_STATE: "syncGameState", START_TIMER: "startTimer", KILL_PLAYER: "killPlayer", - REVEAL_PLAYER: 'revealPlayer' + REVEAL_PLAYER: 'revealPlayer', + CHANGE_NAME: 'changeName' }, USER_TYPES: { MODERATOR: "moderator", diff --git a/client/modules/GameCreationStepManager.js b/client/modules/GameCreationStepManager.js index 30e0dc7..d2a5c09 100644 --- a/client/modules/GameCreationStepManager.js +++ b/client/modules/GameCreationStepManager.js @@ -313,12 +313,13 @@ function updateTracker(step) { function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) { document.querySelector("#step-back-button")?.remove(); document.querySelector("#step-forward-button")?.remove(); + document.querySelector("#create-game")?.remove(); if (back) { let backButton = document.createElement("button"); backButton.innerText = "\u2bc7 Back"; backButton.addEventListener('click', backHandler); backButton.setAttribute("id", "step-back-button"); - document.getElementById("creation-step-buttons").appendChild(backButton); + document.getElementById("tracker-container").prepend(backButton); } if (forward && builtGame === null) { @@ -326,7 +327,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) fwdButton.innerHTML = "Next \u25b6"; fwdButton.addEventListener('click', forwardHandler); fwdButton.setAttribute("id", "step-forward-button"); - document.getElementById("creation-step-buttons").appendChild(fwdButton); + document.getElementById("tracker-container").appendChild(fwdButton); } else if (forward && builtGame !== null) { let createButton = document.createElement("button"); createButton.innerText = "Create"; @@ -344,7 +345,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) toast("You must provide your name.", "error", true); } }); - document.getElementById("creation-step-buttons").appendChild(createButton); + document.getElementById("tracker-container").appendChild(createButton); } } diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js index 3200bb5..79fe67c 100644 --- a/client/modules/GameStateRenderer.js +++ b/client/modules/GameStateRenderer.js @@ -16,13 +16,13 @@ export class GameStateRenderer { renderLobbyPlayers() { document.querySelectorAll('.lobby-player').forEach((el) => el.remove()) let lobbyPlayersContainer = document.getElementById("lobby-players"); - if (this.gameState.client.userType === globals.USER_TYPES.PLAYER) { + if (this.gameState.client.userType === globals.USER_TYPES.PLAYER && this.gameState.moderator.userType === globals.USER_TYPES.MODERATOR) { lobbyPlayersContainer.appendChild(renderLobbyPerson(this.gameState.moderator.name, this.gameState.moderator.userType)) } for (let person of this.gameState.people) { lobbyPlayersContainer.appendChild(renderLobbyPerson(person.name,person.userType)) } - let playerCount = this.gameState.people.filter((person) => person.userType === globals.USER_TYPES.PLAYER).length; + let playerCount = this.gameState.people.length; document.querySelector("label[for='lobby-players']").innerText = "People (" + playerCount + "/" + getGameSize(this.gameState.deck) + " Players)"; } @@ -129,8 +129,23 @@ export class GameStateRenderer { }); let teamGood = this.gameState.people.filter((person) => person.alignment === globals.ALIGNMENT.GOOD); let teamEvil = this.gameState.people.filter((person) => person.alignment === globals.ALIGNMENT.EVIL); - renderGroupOfPlayers(teamEvil, this.killPlayerHandlers, this.revealRoleHandlers, this.gameState.accessCode, globals.ALIGNMENT.EVIL, true, this.socket); - renderGroupOfPlayers(teamGood, this.killPlayerHandlers, this.revealRoleHandlers, this.gameState.accessCode, globals.ALIGNMENT.GOOD, true, this.socket); + renderGroupOfPlayers( + teamEvil, + this.killPlayerHandlers, + this.revealRoleHandlers, + this.gameState.accessCode, + globals.ALIGNMENT.EVIL, + this.gameState.moderator.userType, + this.socket + ); + renderGroupOfPlayers( + teamGood, + this.killPlayerHandlers, + this.revealRoleHandlers, + this.gameState.accessCode, + globals.ALIGNMENT.GOOD, + this.gameState.moderator.userType, + this.socket); document.getElementById("players-alive-label").innerText = 'Players: ' + this.gameState.people.filter((person) => !person.out).length + ' / ' + this.gameState.people.length + ' Alive'; @@ -152,10 +167,17 @@ export class GameStateRenderer { }); } document.querySelectorAll('.game-player').forEach((el) => el.remove()); - this.gameState.people.sort((a, b) => { - return a.name >= b.name ? 1 : -1; - }); - renderGroupOfPlayers(this.gameState, this.killPlayerHandlers, this.revealRoleHandlers, this.gameState.accessCode, null, tempMod, this.socket); + sortPeopleByStatus(this.gameState.people); + let modType = tempMod ? this.gameState.moderator.userType : null; + renderGroupOfPlayers( + this.gameState.people, + this.killPlayerHandlers, + this.revealRoleHandlers, + this.gameState.accessCode, + null, + modType, + this.socket + ); document.getElementById("players-alive-label").innerText = 'Players: ' + this.gameState.people.filter((person) => !person.out).length + ' / ' + this.gameState.people.length + ' Alive'; @@ -220,6 +242,19 @@ function renderLobbyPerson(name, userType) { return el; } +function sortPeopleByStatus(people) { + people.sort((a, b) => { + if (a.out !== b.out) { + return a.out ? 1 : -1; + } else { + if (a.revealed !== b.revealed) { + return a.revealed? -1 : 1; + } + return a.name >= b.name ? 1 : -1; + } + }); +} + function getGameSize(cards) { let quantity = 0; for (let card of cards) { @@ -237,11 +272,11 @@ function removeExistingTitle() { } // TODO: refactor to reduce the cyclomatic complexity of this function -function renderGroupOfPlayers(gameState, killPlayerHandlers, revealRoleHandlers, accessCode=null, alignment=null, moderator=false, socket=null) { - for (let player of gameState.people) { +function renderGroupOfPlayers(people, killPlayerHandlers, revealRoleHandlers, accessCode=null, alignment=null, moderatorType, socket=null) { + for (let player of people) { let container = document.createElement("div"); container.classList.add('game-player'); - if (moderator) { + if (moderatorType) { container.dataset.pointer = player.id; container.innerHTML = templates.MODERATOR_PLAYER; } else { @@ -250,9 +285,9 @@ function renderGroupOfPlayers(gameState, killPlayerHandlers, revealRoleHandlers, container.querySelector('.game-player-name').innerText = player.name; let roleElement = container.querySelector('.game-player-role') - if (moderator) { + if (moderatorType) { roleElement.classList.add(alignment); - if (gameState.moderator.userType === globals.USER_TYPES.MODERATOR) { + if (moderatorType === globals.USER_TYPES.MODERATOR) { roleElement.innerText = player.gameRole; document.getElementById("player-list-moderator-team-" + alignment).appendChild(container); } else { @@ -275,11 +310,12 @@ function renderGroupOfPlayers(gameState, killPlayerHandlers, revealRoleHandlers, if (player.out) { container.classList.add('killed'); - if (moderator) { + if (moderatorType) { container.querySelector('.kill-player-button')?.remove(); + insertPlaceholderButton(container, false, "killed"); } } else { - if (moderator) { + if (moderatorType) { killPlayerHandlers[player.id] = () => { if (confirm("KILL " + player.name + "?")) { socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id); @@ -290,11 +326,12 @@ function renderGroupOfPlayers(gameState, killPlayerHandlers, revealRoleHandlers, } if (player.revealed) { - if (moderator) { + if (moderatorType) { container.querySelector('.reveal-role-button')?.remove(); + insertPlaceholderButton(container, true, "revealed"); } } else { - if (moderator) { + if (moderatorType) { revealRoleHandlers[player.id] = () => { if (confirm("REVEAL " + player.name + "?")) { socket.emit(globals.COMMANDS.REVEAL_PLAYER, accessCode, player.id); @@ -340,3 +377,18 @@ function renderPlayerRole(gameState) { document.getElementById("game-role").style.display = 'none'; }); } + +function insertPlaceholderButton(container, append, type) { + let button = document.createElement("div"); + button.classList.add('placeholder-button'); + if (type === "killed") { + button.innerText = 'Killed'; + } else { + button.innerText = "Revealed"; + } + if (append) { + container.querySelector('.player-action-buttons').appendChild(button); + } else { + container.querySelector('.player-action-buttons').prepend(button); + } +} diff --git a/client/modules/Templates.js b/client/modules/Templates.js index 4a1a09b..3699a98 100644 --- a/client/modules/Templates.js +++ b/client/modules/Templates.js @@ -9,7 +9,7 @@ export const templates = { "
    " + "
    " + "
    " + - "
    " + + "
    " + "" + "
    " + "
    " + @@ -41,7 +41,7 @@ export const templates = { "

    Click to reveal your role

    " + "

    (click again to hide)

    " + "
    " + - "
    " + + "
    " + "" + "
    " + "
    ", @@ -121,7 +121,7 @@ export const templates = { "

    Click to reveal your role

    " + "

    (click again to hide)

    " + "
    " + - "
    " + + "
    " + "" + "
    " + "
    " + @@ -131,13 +131,52 @@ export const templates = { "
    " + "
    " + "
    " + - "
    " + + "
    " + "" + - "" + + "" + "
    ", GAME_PLAYER: "
    " + "
    " + "
    " + + "
    ", + INITIAL_GAME_DOM: + "
    " + + "
    " + + "" + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    ", + // via https://loading.io/css/ + SPINNER: + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    " + + "
    ", + NAME_CHANGE_MODAL: + "" + + "" } diff --git a/client/scripts/game.js b/client/scripts/game.js index 0988a8c..62618a5 100644 --- a/client/scripts/game.js +++ b/client/scripts/game.js @@ -4,6 +4,7 @@ import {templates} from "../modules/Templates.js"; import {GameStateRenderer} from "../modules/GameStateRenderer.js"; import {cancelCurrentToast, toast} from "../modules/Toast.js"; import {GameTimerManager} from "../modules/GameTimerManager.js"; +import {ModalManager} from "../modules/ModalManager.js"; export const game = () => { let timerWorker; @@ -28,20 +29,43 @@ function prepareGamePage(environment, socket, timerWorker) { const accessCode = splitUrl[1]; if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) { socket.emit(globals.COMMANDS.FETCH_GAME_STATE, accessCode, userId, function (gameState) { + let currentGameState = gameState; + document.querySelector('.spinner-container')?.remove(); + document.querySelector('.spinner-background')?.remove(); if (gameState === null) { window.location = '/not-found?reason=' + encodeURIComponent('game-not-found'); + } else if (!gameState.client.hasEnteredName) { + userId = gameState.client.cookie; + UserUtility.setAnonymousUserId(userId, environment); + document.getElementById("game-content").innerHTML = templates.NAME_CHANGE_MODAL; + document.getElementById("change-name-form").onsubmit = (e) => { + e.preventDefault(); + let name = document.getElementById("player-new-name").value; + if (validateName(name)) { + socket.emit(globals.COMMANDS.CHANGE_NAME, gameState.accessCode, { name: name, personId: gameState.client.id }, (result) => { + switch (result) { + case "taken": + toast('This name is already taken.', 'error', true, true, 8); + break; + case "changed": + ModalManager.dispelModal("change-name-modal", "change-name-modal-background") + toast('Name set.', 'success', true, true, 5); + document.getElementById("game-content").innerHTML = templates.INITIAL_GAME_DOM; + propagateNameChange(currentGameState, name, currentGameState.client.id); + initializeGame(currentGameState, socket, timerWorker, userId); + } + }) + } else { + toast("Name must be fewer than 30 characters.", 'error', true, true, 8); + } + } } else { - toast('You are connected.', 'success', true, true, 3); + document.getElementById("game-content").innerHTML = templates.INITIAL_GAME_DOM; + toast('You are connected.', 'success', true, true, 2); console.log(gameState); userId = gameState.client.cookie; UserUtility.setAnonymousUserId(userId, environment); - let gameStateRenderer = new GameStateRenderer(gameState, socket); - let gameTimerManager; - if (gameState.timerParams) { - gameTimerManager = new GameTimerManager(gameState, socket); - } - setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager); - processGameState(gameState, userId, socket, gameStateRenderer); + initializeGame(gameState, socket, timerWorker, userId); } }); } else { @@ -49,18 +73,28 @@ function prepareGamePage(environment, socket, timerWorker) { } } -function processGameState (gameState, userId, socket, gameStateRenderer) { - displayClientInfo(gameState.client.name, gameState.client.userType); - switch (gameState.status) { +function initializeGame(currentGameState, socket, timerWorker, userId) { + let gameStateRenderer = new GameStateRenderer(currentGameState, socket); + let gameTimerManager; + if (currentGameState.timerParams) { + gameTimerManager = new GameTimerManager(currentGameState, socket); + } + setClientSocketHandlers(currentGameState, gameStateRenderer, socket, timerWorker, gameTimerManager); + processGameState(currentGameState, userId, socket, gameStateRenderer); +} + +function processGameState (currentGameState, userId, socket, gameStateRenderer) { + displayClientInfo(currentGameState.client.name, currentGameState.client.userType); + switch (currentGameState.status) { case globals.STATUS.LOBBY: document.getElementById("game-state-container").innerHTML = templates.LOBBY; gameStateRenderer.renderLobbyHeader(); gameStateRenderer.renderLobbyPlayers(); if ( - gameState.isFull + currentGameState.isFull && ( - gameState.client.userType === globals.USER_TYPES.MODERATOR - || gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR + currentGameState.client.userType === globals.USER_TYPES.MODERATOR + || currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR ) ) { displayStartGamePromptForModerators(gameStateRenderer, socket); @@ -68,7 +102,7 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { break; case globals.STATUS.IN_PROGRESS: gameStateRenderer.renderGameHeader(); - switch (gameState.client.userType) { + switch (currentGameState.client.userType) { case globals.USER_TYPES.PLAYER: document.getElementById("game-state-container").innerHTML = templates.PLAYER_GAME_VIEW; gameStateRenderer.renderPlayerView(); @@ -97,7 +131,7 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { break; } - socket.emit(globals.COMMANDS.GET_TIME_REMAINING, gameState.accessCode); + socket.emit(globals.COMMANDS.GET_TIME_REMAINING, currentGameState.accessCode); break; default: break; @@ -110,17 +144,17 @@ function displayClientInfo(name, userType) { document.getElementById("client-user-type").innerText += globals.USER_TYPE_ICONS[userType]; } -function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager) { +function setClientSocketHandlers(currentGameState, gameStateRenderer, socket, timerWorker, gameTimerManager) { if (!socket.hasListeners(globals.EVENTS.PLAYER_JOINED)) { socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => { toast(player.name + " joined!", "success", false); - gameStateRenderer.gameState.people.push(player); + currentGameState.people.push(player); gameStateRenderer.renderLobbyPlayers(); if ( gameIsFull && ( - gameStateRenderer.gameState.client.userType === globals.USER_TYPES.MODERATOR - || gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR + currentGameState.client.userType === globals.USER_TYPES.MODERATOR + || currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR ) ) { displayStartGamePromptForModerators(gameStateRenderer, socket); @@ -131,12 +165,13 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim socket.on(globals.EVENTS.SYNC_GAME_STATE, () => { socket.emit( globals.COMMANDS.FETCH_GAME_STATE, - gameStateRenderer.gameState.accessCode, - gameStateRenderer.gameState.client.cookie, + currentGameState.accessCode, + currentGameState.client.cookie, function (gameState) { - gameStateRenderer.gameState = gameState; - gameTimerManager.gameState = gameState; - processGameState(gameState, gameState.client.cookie, socket, gameStateRenderer); + currentGameState = gameState; + gameStateRenderer.gameState = currentGameState; + gameTimerManager.gameState = currentGameState; + processGameState(currentGameState, gameState.client.cookie, socket, gameStateRenderer); } ); }); @@ -148,14 +183,14 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim if (!socket.hasListeners(globals.EVENTS.KILL_PLAYER)) { socket.on(globals.EVENTS.KILL_PLAYER, (id) => { - let killedPerson = gameStateRenderer.gameState.people.find((person) => person.id === id); + let killedPerson = currentGameState.people.find((person) => person.id === id); if (killedPerson) { killedPerson.out = true; - if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.MODERATOR) { + if (currentGameState.client.userType === globals.USER_TYPES.MODERATOR) { toast(killedPerson.name + ' killed.', 'success', true, true, 6); gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo() } else { - if (killedPerson.id === gameStateRenderer.gameState.client.id) { + if (killedPerson.id === currentGameState.client.id) { let clientUserType = document.getElementById("client-user-type"); if (clientUserType) { clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80' @@ -165,7 +200,7 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim } else { toast(killedPerson.name + ' was killed!', 'warning', false, true, 6); } - if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + if (currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); } else { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); @@ -177,21 +212,21 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim if (!socket.hasListeners(globals.EVENTS.REVEAL_PLAYER)) { socket.on(globals.EVENTS.REVEAL_PLAYER, (revealData) => { - let revealedPerson = gameStateRenderer.gameState.people.find((person) => person.id === revealData.id); + let revealedPerson = currentGameState.people.find((person) => person.id === revealData.id); if (revealedPerson) { revealedPerson.revealed = true; revealedPerson.gameRole = revealData.gameRole; revealedPerson.alignment = revealData.alignment; - if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.MODERATOR) { + if (currentGameState.client.userType === globals.USER_TYPES.MODERATOR) { toast(revealedPerson.name + ' revealed.', 'success', true, true, 6); gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo() } else { - if (revealedPerson.id === gameStateRenderer.gameState.client.id) { + if (revealedPerson.id === currentGameState.client.id) { toast('Your role has been revealed!', 'warning', false, true, 6); } else { toast(revealedPerson.name + ' was revealed as a ' + revealedPerson.gameRole + '!', 'warning', false, true, 6); } - if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + if (currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); } else { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); @@ -200,17 +235,23 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim } }); } + + if (!socket.hasListeners(globals.EVENTS.CHANGE_NAME)) { + socket.on(globals.EVENTS.CHANGE_NAME, (personId, name) => { + propagateNameChange(currentGameState, name, personId); + processGameState(currentGameState, currentGameState.client.cookie, socket, gameStateRenderer); + }); + } } function displayStartGamePromptForModerators(gameStateRenderer, socket) { - document.getElementById("lobby-players").setAttribute("style", 'margin-bottom: 130px'); let div = document.createElement("div"); div.innerHTML = templates.START_GAME_PROMPT; document.body.appendChild(div); document.getElementById("start-game-button").addEventListener('click', (e) => { e.preventDefault(); if (confirm("Start the game and deal roles?")) { - socket.emit(globals.COMMANDS.START_GAME, gameStateRenderer.gameState.accessCode, gameStateRenderer.gameState.client.cookie); + socket.emit(globals.COMMANDS.START_GAME, gameStateRenderer.gameState.accessCode,gameStateRenderer.gameState.client.cookie); } }); @@ -226,3 +267,24 @@ function runGameTimer (hours, minutes, tickRate, soundManager, timerWorker) { timerWorker.postMessage({ hours: hours, minutes: minutes, tickInterval: tickRate }); } } + +function validateName(name) { + return typeof name === 'string' && name.length <= 30; +} + +function propagateNameChange(gameState, name, personId) { + gameState.client.name = name; + let matchingPerson = gameState.people.find((person) => person.id === personId); + if (matchingPerson) { + matchingPerson.name = name; + } + + if (gameState.moderator.id === personId) { + gameState.moderator.name = name; + } + + let matchingSpectator = gameState.spectators?.find((spectator) => spectator.id === personId); + if (matchingSpectator) { + matchingSpectator.name = name; + } +} diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css index c00c31e..b782f18 100644 --- a/client/styles/GLOBAL.css +++ b/client/styles/GLOBAL.css @@ -97,6 +97,7 @@ button:active, input[type=submit]:active { justify-content: center; width: 95%; margin: 0 auto; + align-items: center; } button:hover, input[type="submit"]:hover, #game-link:hover { @@ -108,6 +109,7 @@ input { } .info-message { + pointer-events: none; display: flex; align-items: center; justify-content: center; @@ -295,3 +297,118 @@ input { font-size: 25px; } } + +.spinner-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: calc(100% + 100px); + background-color: rgba(0, 0, 0, 0.75); + z-index: 50; +} + + +/* via https://loading.io/css/ */ + +.spinner-container { + position: relative; +} + +.spinner-container p { + margin: auto; + position: fixed; + top: 0; + left: 0; + bottom: -8em; + font-size: 20px; + z-index: 51; + text-align: center; + right: 0; + color: #d7d7d7; + height: fit-content; +} + +.lds-spinner { + margin: auto; + position: fixed; + top: -80px; + left: 0; + bottom: 0; + z-index: 51; + right: 0; + height: fit-content; + display: inline-block; + width: 80px; +} +.lds-spinner div { + transform-origin: 40px 40px; + animation: lds-spinner 1.2s linear infinite; +} +.lds-spinner div:after { + content: " "; + display: block; + position: absolute; + top: 3px; + left: 37px; + width: 6px; + height: 18px; + border-radius: 20%; + background: #d7d7d7; +} +.lds-spinner div:nth-child(1) { + transform: rotate(0deg); + animation-delay: -1.1s; +} +.lds-spinner div:nth-child(2) { + transform: rotate(30deg); + animation-delay: -1s; +} +.lds-spinner div:nth-child(3) { + transform: rotate(60deg); + animation-delay: -0.9s; +} +.lds-spinner div:nth-child(4) { + transform: rotate(90deg); + animation-delay: -0.8s; +} +.lds-spinner div:nth-child(5) { + transform: rotate(120deg); + animation-delay: -0.7s; +} +.lds-spinner div:nth-child(6) { + transform: rotate(150deg); + animation-delay: -0.6s; +} +.lds-spinner div:nth-child(7) { + transform: rotate(180deg); + animation-delay: -0.5s; +} +.lds-spinner div:nth-child(8) { + transform: rotate(210deg); + animation-delay: -0.4s; +} +.lds-spinner div:nth-child(9) { + transform: rotate(240deg); + animation-delay: -0.3s; +} +.lds-spinner div:nth-child(10) { + transform: rotate(270deg); + animation-delay: -0.2s; +} +.lds-spinner div:nth-child(11) { + transform: rotate(300deg); + animation-delay: -0.1s; +} +.lds-spinner div:nth-child(12) { + transform: rotate(330deg); + animation-delay: 0s; +} +@keyframes lds-spinner { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} diff --git a/client/styles/create.css b/client/styles/create.css index 5976516..0903409 100644 --- a/client/styles/create.css +++ b/client/styles/create.css @@ -222,14 +222,25 @@ input[type="number"] { margin-bottom: 8em; } +#game-creation-container { + width: 95%; + max-width: 60em; +} + +#tracker-container { + display: flex; + align-items: center; + margin-top: 2em; + justify-content: center; + width: 100%; +} + #creation-step-tracker { display: flex; justify-content: center; - margin-top: 2em; } #step-forward-button, #step-back-button, #create-game { - position: absolute; font-family: sans-serif; font-size: 20px; padding: 10px 20px; diff --git a/client/styles/game.css b/client/styles/game.css index e703ee9..5ac025a 100644 --- a/client/styles/game.css +++ b/client/styles/game.css @@ -14,6 +14,19 @@ margin: 0.5em 0; } +#lobby-players { + overflow-y: auto; + max-height: 30em; + overflow-x: hidden; + padding: 0 10px; + border-radius: 3px; +} + +#lobby-people-container label { + display: block; + margin-bottom: 0.5em; +} + .lobby-player-client { border: 2px solid #21ba45; } @@ -30,6 +43,9 @@ justify-content: center; flex-direction: row; flex-wrap: wrap; + display: flex; + width: 95%; + margin: 0 auto 115px auto; } #lobby-header { @@ -124,7 +140,7 @@ h1 { display: flex; align-items: center; justify-content: center; - background-color: #333243; + background-color: #171522; border: 5px solid #61606a; position: relative; flex-direction: column; @@ -215,7 +231,7 @@ h1 { #client-container { max-width: 35em; - margin-top: 2em; + margin: 1em 0; } #client { @@ -226,12 +242,14 @@ h1 { justify-content: space-between; background-color: #333243; border-radius: 3px; + min-width: 15em; } #client-name { color: whitesmoke; font-family: 'diavlo', sans-serif; font-size: 30px; + margin: 0.25em 2em 0.25em 0; } #client-user-type { @@ -267,7 +285,7 @@ label[for='moderator'] { bottom: 0; /* width: fit-content; */ font-size: 20px; - height: 120px; + height: 85px; margin: 0 auto; animation: fade-in-slide-up 10s ease; animation-fill-mode: forwards; @@ -276,16 +294,20 @@ label[for='moderator'] { background-color: #333243; } +#end-game-prompt { + box-shadow: 0 -6px 40px black; +} + #start-game-button, #end-game-button { font-family: 'signika-negative', sans-serif !important; padding: 10px; border-radius: 3px; color: whitesmoke; - font-size: 30px; cursor: pointer; border: 2px solid transparent; transition: background-color, border 0.3s ease-out; text-shadow: 0 3px 4px rgb(0 0 0 / 85%); + font-size: 25px; } #start-game-button { @@ -358,9 +380,16 @@ label[for='moderator'] { justify-content: space-between; margin: 0.5em 0; position: relative; + box-shadow: 0 1px 1px rgba(0,0,0,0.11), + 0 2px 2px rgba(0,0,0,0.11), + 0 4px 4px rgba(0,0,0,0.11), + 0 8px 8px rgba(0,0,0,0.11), + 0 16px 16px rgba(0,0,0,0.11), + 0 32px 32px rgba(0,0,0,0.11); } .game-player-name { + position: relative; width: 10em; overflow: hidden; white-space: nowrap; @@ -380,12 +409,27 @@ label[for='moderator'] { transition: background-color, border 0.3s ease-out; text-shadow: 0 3px 4px rgb(0 0 0 / 55%); margin: 5px 0 5px 25px; - min-width: 6em; + width: 117px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } +.placeholder-button { + font-family: 'signika-negative', sans-serif !important; + padding: 5px; + display: flex; + justify-content: center; + border-radius: 3px; + color: #767676; + font-weight: bold; + font-size: 16px; + border: 2px solid transparent; + text-shadow: 0 3px 4px rgb(0 0 0 / 55%); + margin: 5px 0 5px 25px; + width: 103px; +} + .reveal-role-button { background-color: #3f5256; } @@ -395,11 +439,10 @@ label[for='moderator'] { margin-left: 5px; } -.killed::after { +#game-player-list > .game-player.killed::after { content: '\01F480'; - position: absolute; - right: -44px; font-size: 24px; + margin-left: 1em; } .killed, .killed .game-player-role { @@ -442,6 +485,13 @@ label[for='moderator'] { flex-wrap: wrap; } +#game-player-list { + overflow-y: auto; + overflow-x: hidden; + padding: 0 10px; + max-height: 37em; +} + #game-player-list > div { padding: 2px 10px; border-radius: 3px; @@ -462,14 +512,14 @@ label[for='moderator'] { justify-content: center; } -@media(max-width: 685px) { - #end-game-button { - font-size: 25px; - } +#change-name-modal-background { + cursor: default; +} - #end-game-prompt { - height: 85px; - } +#lobby-people-container , #game-people-container { + background-color: #333243; + padding: 10px 10px 0 10px; + border-radius: 3px; } @keyframes pulse { diff --git a/client/views/create.html b/client/views/create.html index c6f2c0b..4dbbe2d 100644 --- a/client/views/create.html +++ b/client/views/create.html @@ -29,7 +29,7 @@ Home
    -
    +

    Create A Game

    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    +
    +

    Select your method of moderation:

    @@ -70,7 +72,6 @@
    -
    diff --git a/client/views/game.html b/client/views/game.html index 3a12de1..f17d6f1 100644 --- a/client/views/game.html +++ b/client/views/game.html @@ -16,21 +16,33 @@ + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Connecting to game...

    +
    -
    -
    - -
    -
    -
    -
    -
    -
    +
    - - - - +
    - - + +

    Werewolf created by Andrew Plotkin

    diff --git a/client/webpack/webpack-dev.config.js b/client/webpack/webpack-dev.config.js index 2f26a0c..c884208 100644 --- a/client/webpack/webpack-dev.config.js +++ b/client/webpack/webpack-dev.config.js @@ -12,6 +12,7 @@ module.exports = { }, mode: "development", node: false, + devtool: 'source-map', module: { rules: [ { diff --git a/client/webpack/webpack-prod.config.js b/client/webpack/webpack-prod.config.js index 8d2a2a4..743bd7a 100644 --- a/client/webpack/webpack-prod.config.js +++ b/client/webpack/webpack-prod.config.js @@ -1,4 +1,5 @@ const path = require('path'); +'use strict'; module.exports = { entry: { @@ -12,7 +13,6 @@ module.exports = { }, mode: "production", node: false, - devtool: 'source-map', module: { rules: [ { diff --git a/package-lock.json b/package-lock.json index 2c29a0f..611a5db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", - "dev": true, "requires": { "@babel/highlight": "^7.16.0" } @@ -16,14 +15,12 @@ "@babel/compat-data": { "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", - "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", - "dev": true + "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==" }, "@babel/core": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.5.tgz", "integrity": "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ==", - "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.5", @@ -46,7 +43,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -54,8 +50,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -63,7 +58,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", "integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==", - "dev": true, "requires": { "@babel/types": "^7.16.0", "jsesc": "^2.5.1", @@ -74,7 +68,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz", "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -83,7 +76,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.5.tgz", "integrity": "sha512-3JEA9G5dmmnIWdzaT9d0NmFRgYnWUThLsDaL7982H0XqqWr56lRrsmwheXFMjR+TMl7QMBb6mzy9kvgr1lRLUA==", - "dev": true, "requires": { "@babel/helper-explode-assignable-expression": "^7.16.0", "@babel/types": "^7.16.0" @@ -93,7 +85,6 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", - "dev": true, "requires": { "@babel/compat-data": "^7.16.0", "@babel/helper-validator-option": "^7.14.5", @@ -105,7 +96,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.5.tgz", "integrity": "sha512-NEohnYA7mkB8L5JhU7BLwcBdU3j83IziR9aseMueWGeAjblbul3zzb8UvJ3a1zuBiqCMObzCJHFqKIQE6hTVmg==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.0", "@babel/helper-environment-visitor": "^7.16.5", @@ -120,7 +110,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz", "integrity": "sha512-3DyG0zAFAZKcOp7aVr33ddwkxJ0Z0Jr5V99y3I690eYLpukJsJvAbzTy1ewoCqsML8SbIrjH14Jc/nSQ4TvNPA==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.0", "regexpu-core": "^4.7.1" @@ -130,7 +119,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.0.tgz", "integrity": "sha512-7hfT8lUljl/tM3h+izTX/pO3W3frz2ok6Pk+gzys8iJqDfZrZy2pXjRTZAvG2YmfHun1X4q8/UZRLatMfqc5Tg==", - "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.13.0", "@babel/helper-module-imports": "^7.12.13", @@ -146,7 +134,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -154,8 +141,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -163,7 +149,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", "integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -172,7 +157,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz", "integrity": "sha512-Hk2SLxC9ZbcOhLpg/yMznzJ11W++lg5GMbxt1ev6TXUiJB0N42KPC+7w8a+eWGuqDnUYuwStJoZHM7RgmIOaGQ==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -181,7 +165,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.16.0", "@babel/template": "^7.16.0", @@ -192,7 +175,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -201,7 +183,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -210,7 +191,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.5.tgz", "integrity": "sha512-7fecSXq7ZrLE+TWshbGT+HyCLkxloWNhTbU2QM1NTI/tDqyf0oZiMcEfYtDuUDCo528EOlt39G1rftea4bRZIw==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -219,7 +199,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -228,7 +207,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz", "integrity": "sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ==", - "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.5", "@babel/helper-module-imports": "^7.16.0", @@ -244,7 +222,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -252,14 +229,12 @@ "@babel/helper-plugin-utils": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz", - "integrity": "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ==", - "dev": true + "integrity": "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ==" }, "@babel/helper-remap-async-to-generator": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.5.tgz", "integrity": "sha512-X+aAJldyxrOmN9v3FKp+Hu1NO69VWgYgDGq6YDykwRPzxs5f2N+X988CBXS7EQahDU+Vpet5QYMqLk+nsp+Qxw==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.0", "@babel/helper-wrap-function": "^7.16.5", @@ -270,7 +245,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.5.tgz", "integrity": "sha512-ao3seGVa/FZCMCCNDuBcqnBFSbdr8N2EW35mzojx3TwfIbdPmNK+JV6+2d5bR0Z71W5ocLnQp9en/cTF7pBJiQ==", - "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.5", "@babel/helper-member-expression-to-functions": "^7.16.5", @@ -283,7 +257,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -292,7 +265,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -301,7 +273,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", - "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -309,20 +280,17 @@ "@babel/helper-validator-identifier": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "dev": true + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" }, "@babel/helper-validator-option": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", - "dev": true + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==" }, "@babel/helper-wrap-function": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.5.tgz", "integrity": "sha512-2J2pmLBqUqVdJw78U0KPNdeE2qeuIyKoG4mKV7wAq3mc4jJG282UgjZw4ZYDnqiWQuS3Y3IYdF/AQ6CpyBV3VA==", - "dev": true, "requires": { "@babel/helper-function-name": "^7.16.0", "@babel/template": "^7.16.0", @@ -334,7 +302,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.5.tgz", "integrity": "sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==", - "dev": true, "requires": { "@babel/template": "^7.16.0", "@babel/traverse": "^7.16.5", @@ -345,7 +312,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", @@ -355,14 +321,12 @@ "@babel/parser": { "version": "7.16.6", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz", - "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==", - "dev": true + "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.16.2", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz", "integrity": "sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -371,7 +335,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.0.tgz", "integrity": "sha512-4tcFwwicpWTrpl9qjf7UsoosaArgImF85AxqCRZlgc3IQDvkUHjJpruXAL58Wmj+T6fypWTC/BakfEkwIL/pwA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", @@ -382,7 +345,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.5.tgz", "integrity": "sha512-C/FX+3HNLV6sz7AqbTQqEo1L9/kfrKjxcVtgyBCmvIgOjvuBVUWooDoi7trsLxOzCEo5FccjRvKHkfDsJFZlfA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/helper-remap-async-to-generator": "^7.16.5", @@ -393,7 +355,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.5.tgz", "integrity": "sha512-pJD3HjgRv83s5dv1sTnDbZOaTjghKEz8KUn1Kbh2eAIRhGuyQ1XSeI4xVXU3UlIEVA3DAyIdxqT1eRn7Wcn55A==", - "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.16.5", "@babel/helper-plugin-utils": "^7.16.5" @@ -403,7 +364,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.5.tgz", "integrity": "sha512-EEFzuLZcm/rNJ8Q5krK+FRKdVkd6FjfzT9tuSZql9sQn64K0hHA2KLJ0DqVot9/iV6+SsuadC5yI39zWnm+nmQ==", - "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.16.5", "@babel/helper-plugin-utils": "^7.16.5", @@ -414,7 +374,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.5.tgz", "integrity": "sha512-P05/SJZTTvHz79LNYTF8ff5xXge0kk5sIIWAypcWgX4BTRUgyHc8wRxJ/Hk+mU0KXldgOOslKaeqnhthcDJCJQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -424,7 +383,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.5.tgz", "integrity": "sha512-i+sltzEShH1vsVydvNaTRsgvq2vZsfyrd7K7vPLUU/KgS0D5yZMe6uipM0+izminnkKrEfdUnz7CxMRb6oHZWw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -434,7 +392,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.5.tgz", "integrity": "sha512-QQJueTFa0y9E4qHANqIvMsuxM/qcLQmKttBACtPCQzGUEizsXDACGonlPiSwynHfOa3vNw0FPMVvQzbuXwh4SQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/plugin-syntax-json-strings": "^7.8.3" @@ -444,7 +401,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.5.tgz", "integrity": "sha512-xqibl7ISO2vjuQM+MzR3rkd0zfNWltk7n9QhaD8ghMmMceVguYrNDt7MikRyj4J4v3QehpnrU8RYLnC7z/gZLA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -454,7 +410,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.5.tgz", "integrity": "sha512-YwMsTp/oOviSBhrjwi0vzCUycseCYwoXnLiXIL3YNjHSMBHicGTz7GjVU/IGgz4DtOEXBdCNG72pvCX22ehfqg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -464,7 +419,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.5.tgz", "integrity": "sha512-DvB9l/TcsCRvsIV9v4jxR/jVP45cslTVC0PMVHvaJhhNuhn2Y1SOhCSFlPK777qLB5wb8rVDaNoqMTyOqtY5Iw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -474,7 +428,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.5.tgz", "integrity": "sha512-UEd6KpChoyPhCoE840KRHOlGhEZFutdPDMGj+0I56yuTTOaT51GzmnEl/0uT41fB/vD2nT+Pci2KjezyE3HmUw==", - "dev": true, "requires": { "@babel/compat-data": "^7.16.4", "@babel/helper-compilation-targets": "^7.16.3", @@ -487,7 +440,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.5.tgz", "integrity": "sha512-ihCMxY1Iljmx4bWy/PIMJGXN4NS4oUj1MKynwO07kiKms23pNvIn1DMB92DNB2R0EA882sw0VXIelYGdtF7xEQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -497,7 +449,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.5.tgz", "integrity": "sha512-kzdHgnaXRonttiTfKYnSVafbWngPPr2qKw9BWYBESl91W54e+9R5pP70LtWxV56g0f05f/SQrwHYkfvbwcdQ/A==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", @@ -508,7 +459,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.5.tgz", "integrity": "sha512-+yFMO4BGT3sgzXo+lrq7orX5mAZt57DwUK6seqII6AcJnJOIhBJ8pzKH47/ql/d426uQ7YhN8DpUFirQzqYSUA==", - "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.16.5", "@babel/helper-plugin-utils": "^7.16.5" @@ -518,7 +468,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.5.tgz", "integrity": "sha512-+YGh5Wbw0NH3y/E5YMu6ci5qTDmAEVNoZ3I54aB6nVEOZ5BQ7QJlwKq5pYVucQilMByGn/bvX0af+uNaPRCabA==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.0", "@babel/helper-create-class-features-plugin": "^7.16.5", @@ -530,7 +479,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.5.tgz", "integrity": "sha512-s5sKtlKQyFSatt781HQwv1hoM5BQ9qRH30r+dK56OLDsHmV74mzwJNX7R1yMuE7VZKG5O6q/gmOGSAO6ikTudg==", - "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.0", "@babel/helper-plugin-utils": "^7.16.5" @@ -540,7 +488,6 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -549,7 +496,6 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.12.13" } @@ -558,7 +504,6 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -567,7 +512,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -576,7 +520,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.3" } @@ -585,7 +528,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -594,7 +536,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -603,7 +544,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -612,7 +552,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -621,7 +560,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -630,7 +568,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -639,7 +576,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -648,7 +584,6 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -657,7 +592,6 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -666,7 +600,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.5.tgz", "integrity": "sha512-8bTHiiZyMOyfZFULjsCnYOWG059FVMes0iljEHSfARhNgFfpsqE92OrCffv3veSw9rwMkYcFe9bj0ZoXU2IGtQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -675,7 +608,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.5.tgz", "integrity": "sha512-TMXgfioJnkXU+XRoj7P2ED7rUm5jbnDWwlCuFVTpQboMfbSya5WrmubNBAMlk7KXvywpo8rd8WuYZkis1o2H8w==", - "dev": true, "requires": { "@babel/helper-module-imports": "^7.16.0", "@babel/helper-plugin-utils": "^7.16.5", @@ -686,7 +618,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.5.tgz", "integrity": "sha512-BxmIyKLjUGksJ99+hJyL/HIxLIGnLKtw772zYDER7UuycDZ+Xvzs98ZQw6NGgM2ss4/hlFAaGiZmMNKvValEjw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -695,7 +626,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.5.tgz", "integrity": "sha512-JxjSPNZSiOtmxjX7PBRBeRJTUKTyJ607YUYeT0QJCNdsedOe+/rXITjP08eG8xUpsLfPirgzdCFN+h0w6RI+pQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -704,7 +634,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.5.tgz", "integrity": "sha512-DzJ1vYf/7TaCYy57J3SJ9rV+JEuvmlnvvyvYKFbk5u46oQbBvuB9/0w+YsVsxkOv8zVWKpDmUoj4T5ILHoXevA==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.0", "@babel/helper-environment-visitor": "^7.16.5", @@ -720,7 +649,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.5.tgz", "integrity": "sha512-n1+O7xtU5lSLraRzX88CNcpl7vtGdPakKzww74bVwpAIRgz9JVLJJpOLb0uYqcOaXVM0TL6X0RVeIJGD2CnCkg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -729,7 +657,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.5.tgz", "integrity": "sha512-GuRVAsjq+c9YPK6NeTkRLWyQskDC099XkBSVO+6QzbnOnH2d/4mBVXYStaPrZD3dFRfg00I6BFJ9Atsjfs8mlg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -738,7 +665,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.5.tgz", "integrity": "sha512-iQiEMt8Q4/5aRGHpGVK2Zc7a6mx7qEAO7qehgSug3SDImnuMzgmm/wtJALXaz25zUj1PmnNHtShjFgk4PDx4nw==", - "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.0", "@babel/helper-plugin-utils": "^7.16.5" @@ -748,7 +674,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.5.tgz", "integrity": "sha512-81tijpDg2a6I1Yhj4aWY1l3O1J4Cg/Pd7LfvuaH2VVInAkXtzibz9+zSPdUM1WvuUi128ksstAP0hM5w48vQgg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -757,7 +682,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.5.tgz", "integrity": "sha512-12rba2HwemQPa7BLIKCzm1pT2/RuQHtSFHdNl41cFiC6oi4tcrp7gjB07pxQvFpcADojQywSjblQth6gJyE6CA==", - "dev": true, "requires": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.5", "@babel/helper-plugin-utils": "^7.16.5" @@ -767,7 +691,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.5.tgz", "integrity": "sha512-+DpCAJFPAvViR17PIMi9x2AE34dll5wNlXO43wagAX2YcRGgEVHCNFC4azG85b4YyyFarvkc/iD5NPrz4Oneqw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -776,7 +699,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.5.tgz", "integrity": "sha512-Fuec/KPSpVLbGo6z1RPw4EE1X+z9gZk1uQmnYy7v4xr4TO9p41v1AoUuXEtyqAI7H+xNJYSICzRqZBhDEkd3kQ==", - "dev": true, "requires": { "@babel/helper-function-name": "^7.16.0", "@babel/helper-plugin-utils": "^7.16.5" @@ -786,7 +708,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.5.tgz", "integrity": "sha512-B1j9C/IfvshnPcklsc93AVLTrNVa69iSqztylZH6qnmiAsDDOmmjEYqOm3Ts2lGSgTSywnBNiqC949VdD0/gfw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -795,7 +716,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.5.tgz", "integrity": "sha512-d57i3vPHWgIde/9Y8W/xSFUndhvhZN5Wu2TjRrN1MVz5KzdUihKnfDVlfP1U7mS5DNj/WHHhaE4/tTi4hIyHwQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -804,7 +724,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.5.tgz", "integrity": "sha512-oHI15S/hdJuSCfnwIz+4lm6wu/wBn7oJ8+QrkzPPwSFGXk8kgdI/AIKcbR/XnD1nQVMg/i6eNaXpszbGuwYDRQ==", - "dev": true, "requires": { "@babel/helper-module-transforms": "^7.16.5", "@babel/helper-plugin-utils": "^7.16.5", @@ -815,7 +734,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.5.tgz", "integrity": "sha512-ABhUkxvoQyqhCWyb8xXtfwqNMJD7tx+irIRnUh6lmyFud7Jln1WzONXKlax1fg/ey178EXbs4bSGNd6PngO+SQ==", - "dev": true, "requires": { "@babel/helper-module-transforms": "^7.16.5", "@babel/helper-plugin-utils": "^7.16.5", @@ -827,7 +745,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.5.tgz", "integrity": "sha512-53gmLdScNN28XpjEVIm7LbWnD/b/TpbwKbLk6KV4KqC9WyU6rq1jnNmVG6UgAdQZVVGZVoik3DqHNxk4/EvrjA==", - "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.16.0", "@babel/helper-module-transforms": "^7.16.5", @@ -840,7 +757,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.5.tgz", "integrity": "sha512-qTFnpxHMoenNHkS3VoWRdwrcJ3FhX567GvDA3hRZKF0Dj8Fmg0UzySZp3AP2mShl/bzcywb/UWAMQIjA1bhXvw==", - "dev": true, "requires": { "@babel/helper-module-transforms": "^7.16.5", "@babel/helper-plugin-utils": "^7.16.5" @@ -850,7 +766,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.5.tgz", "integrity": "sha512-/wqGDgvFUeKELW6ex6QB7dLVRkd5ehjw34tpXu1nhKC0sFfmaLabIswnpf8JgDyV2NeDmZiwoOb0rAmxciNfjA==", - "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.0" } @@ -859,7 +774,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.5.tgz", "integrity": "sha512-ZaIrnXF08ZC8jnKR4/5g7YakGVL6go6V9ql6Jl3ecO8PQaQqFE74CuM384kezju7Z9nGCCA20BqZaR1tJ/WvHg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -868,7 +782,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.16.5.tgz", "integrity": "sha512-KVuJ7sWf6bcXawKVH6ZDQFYcOulObt1IOvl/gvNrkNXzmFf1IdgKOy4thmVomReleXqffMbptmXXMl3zPI7zHw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -877,7 +790,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.5.tgz", "integrity": "sha512-tded+yZEXuxt9Jdtkc1RraW1zMF/GalVxaVVxh41IYwirdRgyAxxxCKZ9XB7LxZqmsjfjALxupNE1MIz9KH+Zg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/helper-replace-supers": "^7.16.5" @@ -887,7 +799,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.5.tgz", "integrity": "sha512-B3O6AL5oPop1jAVg8CV+haeUte9oFuY85zu0jwnRNZZi3tVAbJriu5tag/oaO2kGaQM/7q7aGPBlTI5/sr9enA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -896,7 +807,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.5.tgz", "integrity": "sha512-+IRcVW71VdF9pEH/2R/Apab4a19LVvdVsr/gEeotH00vSDVlKD+XgfSIw+cgGWsjDB/ziqGv/pGoQZBIiQVXHg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -905,7 +815,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.5.tgz", "integrity": "sha512-2z+it2eVWU8TtQQRauvGUqZwLy4+7rTfo6wO4npr+fvvN1SW30ZF3O/ZRCNmTuu4F5MIP8OJhXAhRV5QMJOuYg==", - "dev": true, "requires": { "regenerator-transform": "^0.14.2" } @@ -914,7 +823,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.5.tgz", "integrity": "sha512-aIB16u8lNcf7drkhXJRoggOxSTUAuihTSTfAcpynowGJOZiGf+Yvi7RuTwFzVYSYPmWyARsPqUGoZWWWxLiknw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -923,7 +831,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.5.tgz", "integrity": "sha512-ZbuWVcY+MAXJuuW7qDoCwoxDUNClfZxoo7/4swVbOW1s/qYLOMHlm9YRWMsxMFuLs44eXsv4op1vAaBaBaDMVg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -932,7 +839,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.5.tgz", "integrity": "sha512-5d6l/cnG7Lw4tGHEoga4xSkYp1euP7LAtrah1h1PgJ3JY7yNsjybsxQAnVK4JbtReZ/8z6ASVmd3QhYYKLaKZw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" @@ -942,7 +848,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.5.tgz", "integrity": "sha512-usYsuO1ID2LXxzuUxifgWtJemP7wL2uZtyrTVM4PKqsmJycdS4U4mGovL5xXkfUheds10Dd2PjoQLXw6zCsCbg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -951,7 +856,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.5.tgz", "integrity": "sha512-gnyKy9RyFhkovex4BjKWL3BVYzUDG6zC0gba7VMLbQoDuqMfJ1SDXs8k/XK41Mmt1Hyp4qNAvGFb9hKzdCqBRQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -960,7 +864,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.5.tgz", "integrity": "sha512-ldxCkW180qbrvyCVDzAUZqB0TAeF8W/vGJoRcaf75awm6By+PxfJKvuqVAnq8N9wz5Xa6mSpM19OfVKKVmGHSQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -969,7 +872,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.5.tgz", "integrity": "sha512-shiCBHTIIChGLdyojsKQjoAyB8MBwat25lKM7MJjbe1hE0bgIppD+LX9afr41lLHOhqceqeWl4FkLp+Bgn9o1Q==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.5" } @@ -978,7 +880,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.5.tgz", "integrity": "sha512-GTJ4IW012tiPEMMubd7sD07iU9O/LOo8Q/oU4xNhcaq0Xn8+6TcUQaHtC8YxySo1T+ErQ8RaWogIEeFhKGNPzw==", - "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.0", "@babel/helper-plugin-utils": "^7.16.5" @@ -988,7 +889,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.5.tgz", "integrity": "sha512-MiJJW5pwsktG61NDxpZ4oJ1CKxM1ncam9bzRtx9g40/WkLRkxFP6mhpkYV0/DxcciqoiHicx291+eUQrXb/SfQ==", - "dev": true, "requires": { "@babel/compat-data": "^7.16.4", "@babel/helper-compilation-targets": "^7.16.3", @@ -1070,7 +970,6 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -1083,7 +982,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz", "integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } @@ -1092,7 +990,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", - "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/parser": "^7.16.0", @@ -1103,7 +1000,6 @@ "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz", "integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==", - "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.5", @@ -1121,7 +1017,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -1129,8 +1024,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -1138,7 +1032,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1147,14 +1040,12 @@ "@discoveryjs/json-ext": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", - "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", - "dev": true + "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==" }, "@socket.io/component-emitter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", - "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==", - "dev": true + "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==" }, "@types/component-emitter": { "version": "1.2.11", @@ -1175,7 +1066,6 @@ "version": "8.2.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz", "integrity": "sha512-UP9rzNn/XyGwb5RQ2fok+DzcIRIYwc16qTXse5+Smsy8MOIccCChT15KAwnsgQx4PzJkaMq4myFyZ4CL5TjhIQ==", - "dev": true, "requires": { "@types/estree": "*", "@types/json-schema": "*" @@ -1185,7 +1075,6 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz", "integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==", - "dev": true, "requires": { "@types/eslint": "*", "@types/estree": "*" @@ -1194,14 +1083,12 @@ "@types/estree": { "version": "0.0.50", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==" }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" }, "@types/node": { "version": "17.0.3", @@ -1212,7 +1099,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, "requires": { "@webassemblyjs/helper-numbers": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1" @@ -1221,26 +1107,22 @@ "@webassemblyjs/floating-point-hex-parser": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" }, "@webassemblyjs/helper-api-error": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" }, "@webassemblyjs/helper-buffer": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" }, "@webassemblyjs/helper-numbers": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, "requires": { "@webassemblyjs/floating-point-hex-parser": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -1250,14 +1132,12 @@ "@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" }, "@webassemblyjs/helper-wasm-section": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -1269,7 +1149,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } @@ -1278,7 +1157,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, "requires": { "@xtuc/long": "4.2.2" } @@ -1286,14 +1164,12 @@ "@webassemblyjs/utf8": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" }, "@webassemblyjs/wasm-edit": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -1309,7 +1185,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.1", @@ -1322,7 +1197,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-buffer": "1.11.1", @@ -1334,7 +1208,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", @@ -1348,7 +1221,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" @@ -1357,14 +1229,12 @@ "@webpack-cli/configtest": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", - "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", - "dev": true + "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==" }, "@webpack-cli/info": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", - "dev": true, "requires": { "envinfo": "^7.7.3" } @@ -1372,20 +1242,17 @@ "@webpack-cli/serve": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", - "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", - "dev": true + "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==" }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" }, "@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "accepts": { "version": "1.3.7", @@ -1399,20 +1266,17 @@ "acorn": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", - "dev": true + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==" }, "acorn-import-assertions": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1423,14 +1287,12 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -1444,7 +1306,6 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", - "dev": true, "requires": { "find-cache-dir": "^3.3.1", "loader-utils": "^1.4.0", @@ -1456,7 +1317,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, "requires": { "object.assign": "^4.1.0" } @@ -1465,7 +1325,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz", "integrity": "sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==", - "dev": true, "requires": { "@babel/compat-data": "^7.13.11", "@babel/helper-define-polyfill-provider": "^0.3.0", @@ -1476,7 +1335,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz", "integrity": "sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw==", - "dev": true, "requires": { "@babel/helper-define-polyfill-provider": "^0.3.0", "core-js-compat": "^3.18.0" @@ -1486,7 +1344,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz", "integrity": "sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==", - "dev": true, "requires": { "@babel/helper-define-polyfill-provider": "^0.3.0" } @@ -1494,13 +1351,7 @@ "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, "base64-arraybuffer": { "version": "1.0.1", @@ -1515,40 +1366,29 @@ "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", "requires": { - "bytes": "3.1.0", + "bytes": "3.1.1", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "1.7.2", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" } }, "browserslist": { "version": "4.19.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", - "dev": true, "requires": { "caniuse-lite": "^1.0.30001286", "electron-to-chromium": "^1.4.17", @@ -1560,19 +1400,17 @@ "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -1581,14 +1419,12 @@ "caniuse-lite": { "version": "1.0.30001292", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", - "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==", - "dev": true + "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==" }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1598,14 +1434,12 @@ "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, "requires": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -1616,7 +1450,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -1624,43 +1457,41 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "content-type": { @@ -1672,15 +1503,14 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "cookie-signature": { "version": "1.0.6", @@ -1691,7 +1521,6 @@ "version": "3.20.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.0.tgz", "integrity": "sha512-relrah5h+sslXssTTOkvqcC/6RURifB0W5yhYBdBkaPYa5/2KBMiog3XiD+s3TwEHWxInWVv4Jx2/Lw0vng+IQ==", - "dev": true, "requires": { "browserslist": "^4.19.1", "semver": "7.0.0" @@ -1700,8 +1529,7 @@ "semver": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" } } }, @@ -1718,46 +1546,12 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, - "css-loader": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", - "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", - "dev": true, - "requires": { - "icss-utils": "^5.1.0", - "postcss": "^8.2.15", - "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", - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1770,7 +1564,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -1793,14 +1586,12 @@ "electron-to-chromium": { "version": "1.4.27", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.27.tgz", - "integrity": "sha512-uZ95szi3zUbzRDx1zx/xnsCG+2xgZyy57pDOeaeO4r8zx5Dqe8Jv1ti8cunvBwJHVI5LzPuw8umKwZb3WKYxSQ==", - "dev": true + "integrity": "sha512-uZ95szi3zUbzRDx1zx/xnsCG+2xgZyy57pDOeaeO4r8zx5Dqe8Jv1ti8cunvBwJHVI5LzPuw8umKwZb3WKYxSQ==" }, "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, "encodeurl": { "version": "1.0.2", @@ -1824,11 +1615,6 @@ "ws": "~8.2.3" }, "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -1848,7 +1634,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz", "integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==", - "dev": true, "requires": { "@socket.io/component-emitter": "~3.0.0", "debug": "~4.3.1", @@ -1865,7 +1650,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -1873,8 +1657,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -1890,7 +1673,6 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", - "dev": true, "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -1899,20 +1681,17 @@ "envinfo": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==" }, "es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-html": { "version": "1.0.3", @@ -1922,14 +1701,12 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -1939,7 +1716,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "requires": { "estraverse": "^5.2.0" }, @@ -1947,22 +1723,19 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" } } }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "etag": { "version": "1.8.1", @@ -1972,14 +1745,12 @@ "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, "requires": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -1993,16 +1764,16 @@ } }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", "requires": { "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.4.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~1.1.2", @@ -2016,17 +1787,24 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", "statuses": "~1.5.0", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "express-force-https": { @@ -2034,23 +1812,25 @@ "resolved": "https://registry.npmjs.org/express-force-https/-/express-force-https-1.0.0.tgz", "integrity": "sha1-KtuBIaaJRhb8eOoFsVvY1LhV/Qw=" }, + "express-rate-limit": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.0.1.tgz", + "integrity": "sha512-4J8og2zuaafv9egUfQ3G5+hRZfTtckimd4leYPkEXNn2XOQ/IBJIwDmHrwbd2ZbI6UEX3AlyAKLG2EWiXvgCig==" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==" }, "finalhandler": { "version": "1.1.2", @@ -2070,7 +1850,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, "requires": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -2081,44 +1860,35 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -2128,45 +1898,27 @@ "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" }, "glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -2174,38 +1926,41 @@ "has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "requires": { "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "toidentifier": "1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } } }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" }, "iconv-lite": { "version": "0.4.24", @@ -2215,41 +1970,19 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true - }, "import-local": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", - "dev": true, "requires": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, "interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" }, "ipaddr.js": { "version": "1.9.1", @@ -2260,7 +1993,6 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -2268,14 +2000,12 @@ "is-docker": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", - "dev": true + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, "requires": { "isobject": "^3.0.1" } @@ -2283,14 +2013,12 @@ "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, "is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, "requires": { "is-docker": "^2.0.0" } @@ -2298,34 +2026,17 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "jasmine": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.6.3.tgz", - "integrity": "sha512-Th91zHsbsALWjDUIiU5d/W5zaYQsZFMPTdeNmi8GivZPmAaUAK8MblSG3yQI4VMGC/abF2us7ex60NH1AAIMTA==", - "requires": { - "glob": "^7.1.6", - "jasmine-core": "~3.6.0" - } - }, - "jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "jest-worker": { "version": "27.4.5", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", - "dev": true, "requires": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -2335,14 +2046,12 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -2352,32 +2061,27 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -2385,20 +2089,17 @@ "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==" }, "loader-utils": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -2409,7 +2110,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, "requires": { "minimist": "^1.2.0" } @@ -2420,7 +2120,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -2428,23 +2127,12 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "requires": { "semver": "^6.0.0" } @@ -2462,8 +2150,7 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "methods": { "version": "1.1.2", @@ -2491,34 +2178,18 @@ "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", - "dev": true - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -2527,20 +2198,17 @@ "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node-releases": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", - "dev": true + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, "requires": { "path-key": "^3.0.0" } @@ -2553,14 +2221,12 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", @@ -2576,19 +2242,10 @@ "ee-first": "1.1.1" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, "onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, "requires": { "mimic-fn": "^2.1.0" } @@ -2597,7 +2254,6 @@ "version": "7.3.0", "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", - "dev": true, "requires": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -2607,7 +2263,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "requires": { "p-try": "^2.0.0" } @@ -2616,7 +2271,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -2624,20 +2278,17 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "parseqs": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", - "dev": true + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" }, "parseuri": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", - "dev": true + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" }, "parseurl": { "version": "1.3.3", @@ -2647,25 +2298,17 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { "version": "0.1.7", @@ -2675,105 +2318,39 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, "requires": { "find-up": "^4.0.0" } }, - "postcss": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", - "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", - "dev": true, - "requires": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.1" - } - }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true - }, - "postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.8.tgz", - "integrity": "sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -2784,12 +2361,12 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.1", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } @@ -2798,7 +2375,6 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, "requires": { "resolve": "^1.9.0" } @@ -2806,14 +2382,12 @@ "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "regenerate-unicode-properties": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", - "dev": true, "requires": { "regenerate": "^1.4.2" } @@ -2821,14 +2395,12 @@ "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "regenerator-transform": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, "requires": { "@babel/runtime": "^7.8.4" } @@ -2837,7 +2409,6 @@ "version": "4.8.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", - "dev": true, "requires": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^9.0.0", @@ -2850,14 +2421,12 @@ "regjsgen": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" }, "regjsparser": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", - "dev": true, "requires": { "jsesc": "~0.5.0" }, @@ -2865,8 +2434,7 @@ "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" } } }, @@ -2874,7 +2442,6 @@ "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, "requires": { "is-core-module": "^2.2.0", "path-parse": "^1.0.6" @@ -2884,7 +2451,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, "requires": { "resolve-from": "^5.0.0" } @@ -2892,8 +2458,7 @@ "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" }, "safe-buffer": { "version": "5.1.2", @@ -2909,7 +2474,6 @@ "version": "2.7.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, "requires": { "@types/json-schema": "^7.0.5", "ajv": "^6.12.4", @@ -2919,13 +2483,12 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", "requires": { "debug": "2.6.9", "depd": "~1.1.2", @@ -2934,18 +2497,18 @@ "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "1.8.1", "mime": "1.6.0", - "ms": "2.1.1", + "ms": "2.1.3", "on-finished": "~2.3.0", "range-parser": "~1.2.1", "statuses": "~1.5.0" }, "dependencies": { "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -2953,32 +2516,30 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, "requires": { "randombytes": "^2.1.0" } }, "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.17.2" } }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, "requires": { "kind-of": "^6.0.2" } @@ -2987,7 +2548,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -2995,14 +2555,12 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "signal-exit": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" }, "socket.io": { "version": "4.4.0", @@ -3041,7 +2599,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.0.tgz", "integrity": "sha512-g7riSEJXi7qCFImPow98oT8X++MSsHz6MMFRXkWNJ6uEROSHOa3kxdrsYWMq85dO+09CFMkcqlpjvbVXQl4z6g==", - "dev": true, "requires": { "@socket.io/component-emitter": "~3.0.0", "backo2": "~1.0.2", @@ -3055,7 +2612,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -3063,14 +2619,12 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "socket.io-parser": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz", "integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==", - "dev": true, "requires": { "@socket.io/component-emitter": "~3.0.0", "debug": "~4.3.1" @@ -3106,20 +2660,12 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", - "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -3128,8 +2674,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -3141,20 +2686,12 @@ "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "style-loader": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "dev": true + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -3162,14 +2699,12 @@ "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" }, "terser": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", - "dev": true, "requires": { "commander": "^2.20.0", "source-map": "~0.7.2", @@ -3179,8 +2714,7 @@ "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" } } }, @@ -3188,7 +2722,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", - "dev": true, "requires": { "jest-worker": "^27.4.1", "schema-utils": "^3.1.1", @@ -3201,7 +2734,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, "requires": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -3211,21 +2743,19 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "type-is": { "version": "1.6.18", @@ -3239,14 +2769,12 @@ "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" }, "unicode-match-property-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, "requires": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -3255,14 +2783,12 @@ "unicode-match-property-value-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", - "dev": true + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" }, "unicode-property-aliases-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", - "dev": true + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" }, "unpipe": { "version": "1.0.0", @@ -3273,17 +2799,10 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "requires": { "punycode": "^2.1.0" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -3298,7 +2817,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", - "dev": true, "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -3308,7 +2826,6 @@ "version": "5.65.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", - "dev": true, "requires": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.50", @@ -3336,11 +2853,15 @@ "webpack-sources": "^3.2.2" }, "dependencies": { + "acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==" + }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, "requires": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -3353,7 +2874,6 @@ "version": "4.9.1", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", - "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.1.0", @@ -3372,8 +2892,7 @@ "commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" } } }, @@ -3381,7 +2900,6 @@ "version": "5.8.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": true, "requires": { "clone-deep": "^4.0.1", "wildcard": "^2.0.0" @@ -3390,20 +2908,17 @@ "webpack-remove-debug": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/webpack-remove-debug/-/webpack-remove-debug-0.1.0.tgz", - "integrity": "sha1-tfCSLQ24HG37498BrgYeaXfDa4E=", - "dev": true + "integrity": "sha1-tfCSLQ24HG37498BrgYeaXfDa4E=" }, "webpack-sources": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz", - "integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==", - "dev": true + "integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==" }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -3411,13 +2926,7 @@ "wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, "ws": { "version": "8.2.3", @@ -3427,20 +2936,12 @@ "xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" }, "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" } } } diff --git a/package.json b/package.json index 07e8609..1ac9557 100644 --- a/package.json +++ b/package.json @@ -15,25 +15,26 @@ "test:unit": "jasmine", "test:e2e": "node browsertest.js" }, + "engines": { + "node": ">=14.0.0" + }, "author": "", "license": "ISC", "dependencies": { - "express": "^4.17.1", - "express-force-https": "^1.0.0", - "jasmine": "^3.5.0", - "socket.io": "^4.4.0" - }, - "devDependencies": { "@babel/core": "^7.16.5", "@babel/plugin-transform-object-assign": "^7.16.5", "@babel/preset-env": "^7.16.5", + "acorn": "^8.6.0", "babel-loader": "^8.2.3", - "css-loader": "^6.5.1", + "express": "^4.17.1", + "express-force-https": "^1.0.0", + "express-rate-limit": "^6.0.1", "open": "^7.0.3", + "socket.io": "^4.4.0", "socket.io-client": "^4.4.0", - "style-loader": "^3.3.1", "webpack": "^5.65.0", "webpack-cli": "^4.9.1", "webpack-remove-debug": "^0.1.0" - } + }, + "devDependencies": {} } diff --git a/server/api/GamesAPI.js b/server/api/GamesAPI.js index 3d5a801..677c2b4 100644 --- a/server/api/GamesAPI.js +++ b/server/api/GamesAPI.js @@ -3,9 +3,21 @@ const router = express.Router(); const debugMode = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())).includes('debug'); const logger = require('../modules/Logger')(debugMode); const GameManager = require('../modules/GameManager.js'); +const rateLimit = require('express-rate-limit').default const gameManager = new GameManager().getInstance(); +const apiLimiter = rateLimit({ + windowMs: 600000, + max: 3, + standardHeaders: true, + legacyHeaders: false, +}) + +if (process.env.NODE_ENV.trim() === 'production') { // in prod, limit clients to creating 3 games per 10 minutes. + router.use('/create', apiLimiter); +} + router.post('/create', function (req, res) { logger.debug('Received request to create new game: ' + JSON.stringify(req.body, null, 4)); const gameCreationPromise = gameManager.createGame(req.body, false); diff --git a/server/config/globals.js b/server/config/globals.js index c958312..48dee1d 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -2,6 +2,7 @@ const globals = { ACCESS_CODE_CHAR_POOL: 'abcdefghijklmnopqrstuvwxyz0123456789', ACCESS_CODE_LENGTH: 6, CLOCK_TICK_INTERVAL_MILLIS: 10, + STALE_GAME_HOURS: 12, CLIENT_COMMANDS: { FETCH_GAME_STATE: 'fetchGameState', GET_ENVIRONMENT: 'getEnvironment', @@ -50,7 +51,7 @@ const globals = { TRACE: "trace" }, GAME_PROCESS_COMMANDS: { - END_GAME: "endGame", + END_TIMER: "endTimer", START_GAME: "startGame", START_TIMER: "startTimer", PAUSE_TIMER: "pauseTimer", diff --git a/server/main.js b/server/main.js index 89c1b35..8b19615 100644 --- a/server/main.js +++ b/server/main.js @@ -3,7 +3,6 @@ const http = require('http'); const https = require('https'); const path = require('path'); const fs = require('fs'); -const socketIO = require('socket.io'); const app = express(); const bodyParser = require('body-parser'); const GameManager = require('./modules/GameManager.js'); @@ -62,10 +61,31 @@ if (localServer) { app.use(secure); } -const io = socketIO(main); - app.set('port', port); +let io; + +if (process.env.NODE_ENV.trim() === 'development') { + io = require("socket.io")(main, { + cors: { + origin: "http://localhost:" + port, + methods: ["GET", "POST"], + allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], + credentials: false + } + }); +} else { + io = require("socket.io")(main, { + cors: { + origin: ["https://playwerewolf.uk.r.appspot.com", "wss://playwerewolf.uk.r.appspot.com"], + methods: ["GET", "POST"], + allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], + credentials: false + }, + transports: ["polling"] + }); +} + const inGame = io.of('/in-game'); @@ -76,7 +96,6 @@ const gameManager = new GameManager(logger, globals.ENVIRONMENT.LOCAL).getInstan /* Instantiate the singleton queue manager */ //const queueManager = new QueueManager(matchmaking, logger).getInstance(); - /* api endpoints */ const games = require('./api/GamesAPI'); app.use('/api/games', games); diff --git a/server/modules/ActiveGameRunner.js b/server/modules/ActiveGameRunner.js index 13c592c..727ba55 100644 --- a/server/modules/ActiveGameRunner.js +++ b/server/modules/ActiveGameRunner.js @@ -18,12 +18,10 @@ class ActiveGameRunner { this.timerThreads[game.accessCode] = gameProcess; gameProcess.on('message', (msg) => { switch (msg.command) { - case globals.GAME_PROCESS_COMMANDS.END_GAME: - //game.status = globals.STATUS.ENDED; + case globals.GAME_PROCESS_COMMANDS.END_TIMER: game.timerParams.paused = false; game.timerParams.timeRemaining = 0; - this.logger.trace('PARENT: END GAME'); - namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.END_GAME, game.accessCode); + this.logger.trace('PARENT: END TIMER'); break; case globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER: game.timerParams.paused = true; diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 4a8885f..f93466f 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -178,6 +178,8 @@ class GameManager { this.logger.error('Tried to create game with invalid options: ' + JSON.stringify(gameParams)); return Promise.reject('Tried to create game with invalid options: ' + gameParams); } else { + // to avoid excessive memory build-up, every time a game is created, check for and purge any stale games. + pruneStaleGames(this.activeGameRunner.activeGames, this.activeGameRunner.timerThreads, this.logger); const newAccessCode = this.generateAccessCode(); let moderator = initializeModerator(UsernameGenerator.generate(), gameParams.hasDedicatedModerator); if (gameParams.timerParams !== null) { @@ -192,6 +194,7 @@ class GameManager { moderator, gameParams.timerParams ); + this.activeGameRunner.activeGames[newAccessCode].createTime = new Date().toJSON(); return Promise.resolve(newAccessCode); } } @@ -359,12 +362,11 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe logger.trace('this person is already associated with a socket connection'); let alreadyConnectedSocket = namespace.connected[matchingPerson.socketId]; if (alreadyConnectedSocket && alreadyConnectedSocket.leave) { - alreadyConnectedSocket.leave(accessCode, ()=> { - logger.trace('kicked existing connection out of room ' + accessCode); - socket.join(accessCode); - matchingPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); - }) + alreadyConnectedSocket.leave(accessCode); + logger.trace('kicked existing connection out of room ' + accessCode); + socket.join(accessCode); + matchingPerson.socketId = socket.id; + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } } } @@ -398,7 +400,7 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe } } else { rejectClientRequestForGameState(ackFn); - logger.trace('the game' + accessCode + ' was not found'); + logger.trace('the game ' + accessCode + ' was not found'); } } @@ -442,4 +444,21 @@ function isNameTaken(game, name) { || (game.spectators.find((spectator) => spectator.name.toLowerCase().trim() === processedName)) } +function pruneStaleGames(activeGames, timerThreads, logger) { + for (const [accessCode, game] of Object.entries(activeGames)) { + if (game.createTime) { + let createDate = new Date(game.createTime); + if (createDate.setHours(createDate.getHours() + globals.STALE_GAME_HOURS) < Date.now()) { // clear games created more than 12 hours ago + logger.info('PRUNING STALE GAME ' + accessCode); + delete activeGames[accessCode]; + if (timerThreads[accessCode]) { + logger.info('KILLING STALE TIMER PROCESS FOR ' + accessCode); + timerThreads[accessCode].kill(); + delete timerThreads[accessCode]; + } + } + } + } +} + module.exports = Singleton; diff --git a/server/modules/GameProcess.js b/server/modules/GameProcess.js index 241d293..bf1227c 100644 --- a/server/modules/GameProcess.js +++ b/server/modules/GameProcess.js @@ -16,7 +16,7 @@ process.on('message', (msg) => { ); timer.runTimer().then(() => { logger.debug('Timer finished for ' + msg.accessCode); - process.send({ command: globals.GAME_PROCESS_COMMANDS.END_GAME }); + process.send({ command: globals.GAME_PROCESS_COMMANDS.END_TIMER }); process.exit(0); }); @@ -31,7 +31,7 @@ process.on('message', (msg) => { case globals.GAME_PROCESS_COMMANDS.RESUME_TIMER: timer.resumeTimer().then(() => { logger.debug('Timer finished for ' + msg.accessCode); - process.send({ command: globals.GAME_PROCESS_COMMANDS.END_GAME }); + process.send({ command: globals.GAME_PROCESS_COMMANDS.END_TIMER }); process.exit(0); }); logger.trace('CHILD PROCESS ' + msg.accessCode + ': RESUME TIMER'); diff --git a/server/routes/router.js b/server/routes/router.js index 961755a..473368a 100644 --- a/server/routes/router.js +++ b/server/routes/router.js @@ -14,5 +14,13 @@ router.get('/game/:code', function (request, response) { response.sendFile(path.join(__dirname, '../../client/src/views/game.html')); }); +router.get('/liveness_check', (req, res) => { + res.sendStatus(200); +}); + +router.get('/readiness_check', (req, res) => { + res.sendStatus(200); +}); + module.exports = router; From c8b06b8258de41e14a5cea55054b78b2b4e59d30 Mon Sep 17 00:00:00 2001 From: Alec Date: Mon, 27 Dec 2021 23:20:33 -0500 Subject: [PATCH 32/60] view updates --- client/src/views/404.html | 4 ++-- client/src/views/create.html | 2 +- client/src/views/game.html | 5 ++--- client/src/views/home.html | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/client/src/views/404.html b/client/src/views/404.html index a4ef610..3624117 100644 --- a/client/src/views/404.html +++ b/client/src/views/404.html @@ -4,10 +4,10 @@ Create A Game - + - + diff --git a/client/src/views/create.html b/client/src/views/create.html index 672e1f7..5f70e52 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -7,7 +7,7 @@ - + diff --git a/client/src/views/game.html b/client/src/views/game.html index f5e08b6..d87b0cf 100644 --- a/client/src/views/game.html +++ b/client/src/views/game.html @@ -4,11 +4,10 @@ Active Game - + - - + diff --git a/client/src/views/home.html b/client/src/views/home.html index 12f1be4..9ebb3ff 100644 --- a/client/src/views/home.html +++ b/client/src/views/home.html @@ -5,11 +5,11 @@ Werewolf Utility - + - - + + From d5a606b66daaf34a0f4a24b2db5082f2ebcf5d7b Mon Sep 17 00:00:00 2001 From: Alec Date: Mon, 27 Dec 2021 23:45:13 -0500 Subject: [PATCH 33/60] restore jasmine --- client/src/scripts/game.js | 1 + package-lock.json | 91 ++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 93 insertions(+) diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index 32307c7..ef6086f 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -259,6 +259,7 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo if (!socket.hasListeners(globals.COMMANDS.END_GAME)) { socket.on(globals.COMMANDS.END_GAME, (people) => { + document.querySelector("#end-game-prompt")?.remove(); stateBucket.currentGameState.people = people; stateBucket.currentGameState.status = globals.STATUS.ENDED; processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer, gameTimerManager, timerWorker); diff --git a/package-lock.json b/package-lock.json index 611a5db..d41b01d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1353,6 +1353,11 @@ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, "base64-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", @@ -1385,6 +1390,15 @@ "type-is": "~1.6.18" } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "browserslist": { "version": "4.19.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", @@ -1479,6 +1493,11 @@ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1875,6 +1894,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1900,6 +1924,19 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", @@ -1979,6 +2016,20 @@ "resolve-cwd": "^3.0.0" } }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, "interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -2033,6 +2084,20 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, + "jasmine": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.10.0.tgz", + "integrity": "sha512-2Y42VsC+3CQCTzTwJezOvji4qLORmKIE0kwowWC+934Krn6ZXNQYljiwK5st9V3PVx96BSiDYXSB60VVah3IlQ==", + "requires": { + "glob": "^7.1.6", + "jasmine-core": "~3.10.0" + } + }, + "jasmine-core": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.10.1.tgz", + "integrity": "sha512-ooZWSDVAdh79Rrj4/nnfklL3NQVra0BcuhcuWoAwwi+znLDoUeH87AFfeX8s+YeYi6xlv5nveRyaA1v7CintfA==" + }, "jest-worker": { "version": "27.4.5", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", @@ -2180,6 +2245,14 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", @@ -2242,6 +2315,14 @@ "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, "onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -2300,6 +2381,11 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2928,6 +3014,11 @@ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, "ws": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", diff --git a/package.json b/package.json index 1ac9557..a94f981 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "express": "^4.17.1", "express-force-https": "^1.0.0", "express-rate-limit": "^6.0.1", + "jasmine": "^3.5.0", "open": "^7.0.3", "socket.io": "^4.4.0", "socket.io-client": "^4.4.0", From 9920ce4055663b0c55de5e7c94b1ce5aeac8aff7 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 00:22:30 -0500 Subject: [PATCH 34/60] add 400 for create game --- package.json | 1 + server/api/GamesAPI.js | 7 ++++++- server/config/globals.js | 3 ++- server/main.js | 14 ++++++++++++++ server/modules/GameManager.js | 2 +- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a94f981..76b9fad 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@babel/preset-env": "^7.16.5", "acorn": "^8.6.0", "babel-loader": "^8.2.3", + "cors": "^2.8.5", "express": "^4.17.1", "express-force-https": "^1.0.0", "express-rate-limit": "^6.0.1", diff --git a/server/api/GamesAPI.js b/server/api/GamesAPI.js index 677c2b4..c65d4d5 100644 --- a/server/api/GamesAPI.js +++ b/server/api/GamesAPI.js @@ -4,6 +4,7 @@ const debugMode = Array.from(process.argv.map((arg) => arg.trim().toLowerCase()) const logger = require('../modules/Logger')(debugMode); const GameManager = require('../modules/GameManager.js'); const rateLimit = require('express-rate-limit').default +const globals = require('../config/globals'); const gameManager = new GameManager().getInstance(); @@ -19,7 +20,7 @@ if (process.env.NODE_ENV.trim() === 'production') { // in prod, limit clients to } router.post('/create', function (req, res) { - logger.debug('Received request to create new game: ' + JSON.stringify(req.body, null, 4)); + logger.trace('Received request to create new game: ' + JSON.stringify(req.body, null, 4)); const gameCreationPromise = gameManager.createGame(req.body, false); gameCreationPromise.then((result) => { if (result instanceof Error) { @@ -27,6 +28,10 @@ router.post('/create', function (req, res) { } else { res.send(result); // game was created successfully, and access code was returned } + }).catch((e) => { + if (e === globals.ERROR_MESSAGE.BAD_CREATE_REQUEST) { + res.status(400).send(globals.ERROR_MESSAGE.BAD_CREATE_REQUEST); + } }); }); diff --git a/server/config/globals.js b/server/config/globals.js index 48dee1d..131659e 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -33,7 +33,8 @@ const globals = { SPECTATOR: "spectator" }, ERROR_MESSAGE: { - GAME_IS_FULL: "This game is full" + GAME_IS_FULL: "This game is full", + BAD_CREATE_REQUEST: "Game has invalid options." }, EVENTS: { PLAYER_JOINED: "playerJoined", diff --git a/server/main.js b/server/main.js index 8b19615..fedfe26 100644 --- a/server/main.js +++ b/server/main.js @@ -4,6 +4,7 @@ const https = require('https'); const path = require('path'); const fs = require('fs'); const app = express(); +const cors = require('cors') const bodyParser = require('body-parser'); const GameManager = require('./modules/GameManager.js'); const globals = require('./config/globals'); @@ -66,6 +67,12 @@ app.set('port', port); let io; if (process.env.NODE_ENV.trim() === 'development') { + const corsOptions = { + origin: "http://localhost:" + port, + optionsSuccessStatus: 200, + methods: ["GET", "POST"] + } + app.use(cors(corsOptions)); io = require("socket.io")(main, { cors: { origin: "http://localhost:" + port, @@ -75,6 +82,13 @@ if (process.env.NODE_ENV.trim() === 'development') { } }); } else { + const corsOptions = { + origin: ["https://playwerewolf.uk.r.appspot.com"], + methods: ["GET", "POST"], + allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], + optionsSuccessStatus: 200, + } + app.use(cors(corsOptions)); io = require("socket.io")(main, { cors: { origin: ["https://playwerewolf.uk.r.appspot.com", "wss://playwerewolf.uk.r.appspot.com"], diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index f93466f..35ce963 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -176,7 +176,7 @@ class GameManager { const expectedKeys = ['deck', 'hasTimer', 'timerParams']; if (typeof gameParams !== 'object' || expectedKeys.some((key) => !Object.keys(gameParams).includes(key))) { this.logger.error('Tried to create game with invalid options: ' + JSON.stringify(gameParams)); - return Promise.reject('Tried to create game with invalid options: ' + gameParams); + return Promise.reject(globals.ERROR_MESSAGE.BAD_CREATE_REQUEST); } else { // to avoid excessive memory build-up, every time a game is created, check for and purge any stale games. pruneStaleGames(this.activeGameRunner.activeGames, this.activeGameRunner.timerThreads, this.logger); From e3094ae1a89c3052e666c5f3d63974120219818b Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 00:48:53 -0500 Subject: [PATCH 35/60] length requirement for custom roles --- client/src/modules/GameCreationStepManager.js | 8 ++++++++ client/src/styles/create.css | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index a47a1a4..1679ae5 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -455,6 +455,14 @@ function initializeRemainingEventListeners(deckManager) { let description = document.getElementById("role-description").value.trim(); let team = document.getElementById("role-alignment").value.toLowerCase().trim(); if (!deckManager.getCustomRoleOption(name)) { // confirm there is no existing custom role with the same name + if (name.length > 40) { + toast('Your name is too long (max 40 characters).', "error", true); + return; + } + if (description.length > 500) { + toast('Your description is too long (max 500 characters).', "error", true); + return; + } deckManager.addToCustomRoleOptions({role: name, description: description, team: team}); updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")) ModalManager.dispelModal("add-role-modal", "add-role-modal-background"); diff --git a/client/src/styles/create.css b/client/src/styles/create.css index 1c925d5..137881e 100644 --- a/client/src/styles/create.css +++ b/client/src/styles/create.css @@ -138,7 +138,7 @@ form { select { padding: 10px; - font-size: 18px; + font-size: 16px; font-family: 'signika-negative', sans-serif; background-color: transparent; color: #d7d7d7; From cdc3e195df573355bfa414b69bafa1a7dbb1388d Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 00:59:00 -0500 Subject: [PATCH 36/60] up producton rate limit --- server/api/GamesAPI.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/api/GamesAPI.js b/server/api/GamesAPI.js index c65d4d5..82aebc9 100644 --- a/server/api/GamesAPI.js +++ b/server/api/GamesAPI.js @@ -10,12 +10,12 @@ const gameManager = new GameManager().getInstance(); const apiLimiter = rateLimit({ windowMs: 600000, - max: 3, + max: 5, standardHeaders: true, legacyHeaders: false, }) -if (process.env.NODE_ENV.trim() === 'production') { // in prod, limit clients to creating 3 games per 10 minutes. +if (process.env.NODE_ENV.trim() === 'production') { // in prod, limit clients to creating 5 games per 10 minutes. router.use('/create', apiLimiter); } From 15624f28bce97a0cb2b5e215068b4ff3826a8bb2 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 01:51:58 -0500 Subject: [PATCH 37/60] attempted fix of end game listener bug --- client/src/modules/GameStateRenderer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index aabf69e..93fd873 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -94,8 +94,7 @@ export class GameStateRenderer { renderModeratorView() { let div = document.createElement("div"); div.innerHTML = templates.END_GAME_PROMPT; - document.getElementById("game-content").appendChild(div); - document.getElementById("end-game-button").addEventListener('click', (e) => { + div.querySelector("#end-game-button").addEventListener('click', (e) => { e.preventDefault(); if (confirm("End the game?")) { this.socket.emit( @@ -104,6 +103,8 @@ export class GameStateRenderer { ); } }); + document.getElementById("game-content").appendChild(div); + let modTransferButton = document.getElementById("mod-transfer-button"); modTransferButton.addEventListener( From 3e2b503ad2ef28f5b42b7f55ce8e4b9a0c7789e8 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 15:16:28 -0500 Subject: [PATCH 38/60] nodemon, fix small issues --- client/robots.txt | 2 + client/src/modules/GameCreationStepManager.js | 1 + client/src/modules/Templates.js | 2 +- client/src/scripts/game.js | 1 + client/src/styles/GLOBAL.css | 11 +- client/src/styles/create.css | 17 ++- client/src/styles/game.css | 13 +- client/src/styles/home.css | 7 +- client/src/styles/modal.css | 2 +- client/src/views/create.html | 2 +- manifest.json | 1 - package.json | 10 +- server/main.js | 125 +++--------------- server/modules/ServerBootstrapper.js | 106 +++++++++++++++ 14 files changed, 173 insertions(+), 127 deletions(-) create mode 100644 client/robots.txt create mode 100644 server/modules/ServerBootstrapper.js diff --git a/client/robots.txt b/client/robots.txt new file mode 100644 index 0000000..d346249 --- /dev/null +++ b/client/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: /api/ diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index 1679ae5..2dd9b54 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -319,6 +319,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) backButton.innerText = "\u2bc7 Back"; backButton.addEventListener('click', backHandler); backButton.setAttribute("id", "step-back-button"); + backButton.classList.add('cancel'); document.getElementById("tracker-container").prepend(backButton); } diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index 7ef70cb..c1179a5 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -116,7 +116,7 @@ export const templates = { "

    Transfer Mod Powers 👑

    " + "
    " + "" + "" + "
    " + diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index ef6086f..5e77c73 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -141,6 +141,7 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer, } break; case globals.STATUS.ENDED: + document.querySelector("#end-game-prompt")?.remove(); let container = document.getElementById("game-state-container") container.innerHTML = templates.END_OF_GAME_VIEW; container.classList.add('vertical-flex'); diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 0efac8e..7afcd54 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -23,7 +23,8 @@ th, thead, tr, tt, u, ul, var { html { font-family: 'signika-negative', sans-serif !important; - background-color: #121314 !important; + background-color: #0f0f10; + height: 100%; } body { @@ -48,7 +49,7 @@ body { h1 { font-family: 'diavlo', sans-serif; - color: #ab2626; + color: #b1afcd; filter: drop-shadow(2px 2px 4px black); margin: 0.5em 0; } @@ -154,6 +155,11 @@ button:active, input[type=submit]:active { border: 2px solid #21ba45; } +.cancel:hover { + background-color: #623232 !important; + border: 2px solid #8a1c1c; +} + .container { padding: 5px; border-radius: 3px; @@ -206,6 +212,7 @@ input { padding: 5px 0; width: 100%; background-color: #333243; + border-bottom: 2px solid #57566a } #navbar img { diff --git a/client/src/styles/create.css b/client/src/styles/create.css index 137881e..83626d3 100644 --- a/client/src/styles/create.css +++ b/client/src/styles/create.css @@ -85,9 +85,10 @@ #deck-container, #custom-roles-container { margin: 1em 0; - background-color: #1f1f1f; + background-color: #191920; padding: 10px; border-radius: 3px; + border: 2px solid #333243; } #step-3 { @@ -101,7 +102,7 @@ } option { - background-color: #1f1f1f; + background-color: #191920; cursor: pointer; } @@ -148,7 +149,7 @@ select { } #game-form > div { - background-color: #1f1f1f; + background-color: #191920; display: flex; flex-direction: column; padding: 10px; @@ -164,7 +165,8 @@ select { #game-time { display: flex; flex-wrap: wrap; - background-color: #1f1f1f; + background-color: #191920; + border: 2px solid #333243; border-radius: 3px; } @@ -290,13 +292,13 @@ input[type="number"] { } #step-1 div { - background-color: black; + background-color: #191920; color: whitesmoke; padding: 1em; max-width: 20em; margin: 0.5em; cursor: pointer; - border: 2px solid transparent; + border: 2px solid #333243; border-radius: 3px; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.6); } @@ -315,7 +317,8 @@ input[type="number"] { } .review-option { - background-color: #1f1f1f; + background-color: #191920; + border: 2px solid #333243; color: whitesmoke; padding: 10px; font-size: 18px; diff --git a/client/src/styles/game.css b/client/src/styles/game.css index c92930d..5577605 100644 --- a/client/src/styles/game.css +++ b/client/src/styles/game.css @@ -178,6 +178,7 @@ h1 { #role-info-modal #modal-button-container { margin-top: 1em; + justify-content: center; } #game-role-info-container .role-info-name { @@ -186,7 +187,7 @@ h1 { font-size: 20px; font-family: signika-negative, sans-serif; margin: 0.5em 0; - background-color: #15191c; + background-color: black; } #role-info-modal h2 { @@ -541,7 +542,15 @@ label[for='moderator'] { } .reveal-role-button { - background-color: #3f5256; + background-color: #586a6e; +} + +.reveal-role-button:hover, #mod-transfer-button:hover { + background-color: #4e5664; +} + +.kill-player-button:hover { + background-color: #b35c5c; } .reveal-role-button img { diff --git a/client/src/styles/home.css b/client/src/styles/home.css index ddf547d..a2b3e5f 100644 --- a/client/src/styles/home.css +++ b/client/src/styles/home.css @@ -1,5 +1,6 @@ html { - height: 100%; + background: rgb(0,0,0); + background: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgb(17 18 18) 35%, rgba(27,31,31,1) 100%); } body { @@ -18,7 +19,7 @@ form { margin: 1em 0; padding: 10px; border-radius: 3px; - background-color: #1f1f1f; + background-color: black; justify-content: center; align-items: center; } @@ -70,7 +71,7 @@ form > div { #join-container > label { font-size: 35px; font-family: 'diavlo', sans-serif; - color: #ab2626; + color: #b1afcd; filter: drop-shadow(2px 2px 4px black); } diff --git a/client/src/styles/modal.css b/client/src/styles/modal.css index 28f7bd3..20b41e8 100644 --- a/client/src/styles/modal.css +++ b/client/src/styles/modal.css @@ -7,7 +7,7 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - background-color: #23282b; + background-color: #191920; align-items: center; justify-content: center; max-width: 25em; diff --git a/client/src/views/create.html b/client/src/views/create.html index 5f70e52..0db4447 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -42,7 +42,7 @@
    diff --git a/manifest.json b/manifest.json index 13566cd..0e32c7b 100644 --- a/manifest.json +++ b/manifest.json @@ -18,6 +18,5 @@ "start_url": "/?source=pwa", "display": "fullscreen", "display_override": ["fullscreen", "standalone", "minimal-ui"], - "description": "A utility to deal Werewolf cards and run games in any setting, on any device." } diff --git a/package.json b/package.json index 76b9fad..c37d3f7 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "bundle": "webpack --config client/webpack/webpack-prod.config.js", "prestart": "npm run bundle", "build:dev": "webpack --watch --config client/webpack/webpack-dev.config.js --mode=development", - "start:dev": "NODE_ENV=development node server/main.js", - "start:dev:windows": "SET NODE_ENV=development && node server/main.js", + "start:dev": "NODE_ENV=development nodemon server/main.js", + "start:dev:windows": "SET NODE_ENV=development && nodemon server/main.js", "start": "NODE_ENV=production node server/main.js -- loglevel=warn port=8080", "start:windows": "SET NODE_ENV=production && node server/main.js -- loglevel=warn port=8080", "test": "jasmine && node browsertest.js openBrowser socket", @@ -26,6 +26,7 @@ "@babel/preset-env": "^7.16.5", "acorn": "^8.6.0", "babel-loader": "^8.2.3", + "body-parser": "^1.19.1", "cors": "^2.8.5", "express": "^4.17.1", "express-force-https": "^1.0.0", @@ -38,5 +39,8 @@ "webpack-cli": "^4.9.1", "webpack-remove-debug": "^0.1.0" }, - "devDependencies": {} + "devDependencies": {}, + "nodemonConfig": { + "ignore": ["client/*", "node_modules/*"] + } } diff --git a/server/main.js b/server/main.js index fedfe26..4d4a4ab 100644 --- a/server/main.js +++ b/server/main.js @@ -1,114 +1,33 @@ const express = require('express'); -const http = require('http'); -const https = require('https'); const path = require('path'); -const fs = require('fs'); const app = express(); -const cors = require('cors') const bodyParser = require('body-parser'); const GameManager = require('./modules/GameManager.js'); const globals = require('./config/globals'); -// const QueueManager = require('./modules/managers/QueueManager'); +const ServerBootstrapper = require('./modules/ServerBootstrapper'); -app.use(bodyParser.json()); // to support JSON-encoded bodies -app.use(bodyParser.urlencoded({ // to support URL-encoded bodies +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); -let main, environment; +const args = ServerBootstrapper.processCLIArgs(); -let args = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())); +const logger = require('./modules/Logger')(args.logLevel); +logger.log('LOG LEVEL IS: ' + args.logLevel); -const localServer = args.includes('local'); -const useHttps = args.includes('https'); -const port = process.env.PORT || args - .filter((arg) => { - return /port=\d+/.test(arg); - }) - .map((arg) => { - return /port=(\d+)/.exec(arg)[1]; - })[0] || 5000; -const logLevel = process.env.LOG_LEVEL || args - .filter((arg) => { - return /loglevel=[a-zA-Z]+/.test(arg); - }) - .map((arg) => { - return /loglevel=([a-zA-Z]+)/.exec(arg)[1]; - })[0] || globals.LOG_LEVEL.INFO; +const main = ServerBootstrapper.createServerWithCorrectHTTPProtocol(app, args.useHttps, args.port, logger) -const logger = require('./modules/Logger')(logLevel); +app.set('port', args.port); -logger.log('LOG LEVEL IS: ' + logLevel) - -if (localServer) { - environment = globals.ENVIRONMENT.LOCAL; - logger.log('starting main in LOCAL mode.'); - if (useHttps && fs.existsSync(path.join(__dirname, '../client/certs/localhost-key.pem')) && fs.existsSync(path.join(__dirname, '../client/certs/localhost.pem'))) { - const key = fs.readFileSync(path.join(__dirname, '../client/certs/localhost-key.pem'), 'utf-8'); - const cert = fs.readFileSync(path.join(__dirname, '../client/certs/localhost.pem'), 'utf-8'); - logger.log('local certs detected. Using HTTPS.'); - main = https.createServer({ key, cert }, app); - logger.log(`navigate to https://localhost:${port}`); - } else { - logger.log('https not specified or no local certs detected. Using HTTP.'); - main = http.createServer(app); - logger.log(`navigate to http://localhost:${port}`); - } -} else { - environment = globals.ENVIRONMENT.PRODUCTION; - logger.warn('starting main in PRODUCTION mode. This should not be used for local development.'); - main = http.createServer(app); - const secure = require('express-force-https'); - app.use(secure); -} - -app.set('port', port); - -let io; - -if (process.env.NODE_ENV.trim() === 'development') { - const corsOptions = { - origin: "http://localhost:" + port, - optionsSuccessStatus: 200, - methods: ["GET", "POST"] - } - app.use(cors(corsOptions)); - io = require("socket.io")(main, { - cors: { - origin: "http://localhost:" + port, - methods: ["GET", "POST"], - allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], - credentials: false - } - }); -} else { - const corsOptions = { - origin: ["https://playwerewolf.uk.r.appspot.com"], - methods: ["GET", "POST"], - allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], - optionsSuccessStatus: 200, - } - app.use(cors(corsOptions)); - io = require("socket.io")(main, { - cors: { - origin: ["https://playwerewolf.uk.r.appspot.com", "wss://playwerewolf.uk.r.appspot.com"], - methods: ["GET", "POST"], - allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], - credentials: false - }, - transports: ["polling"] - }); -} - -const inGame = io.of('/in-game'); +const inGameSocketServer = ServerBootstrapper.createSocketServer(main, app, args.port); +inGameSocketServer.on('connection', function (socket) { + gameManager.addGameSocketHandlers(inGameSocketServer, socket); +}); /* Instantiate the singleton game manager */ -//const gameManager = new GameManager(logger, environment).getInstance(); -const gameManager = new GameManager(logger, globals.ENVIRONMENT.LOCAL).getInstance(); // temporary - -/* Instantiate the singleton queue manager */ -//const queueManager = new QueueManager(matchmaking, logger).getInstance(); +const gameManager = new GameManager(logger, globals.ENVIRONMENT.LOCAL).getInstance(); // temporarily use local environment configuration for game manager /* api endpoints */ const games = require('./api/GamesAPI'); @@ -126,26 +45,20 @@ app.use('/favicon.ico', (req, res) => { const router = require('./routes/router'); app.use('', router); -if (process.env.NODE_ENV.trim() === 'development') { - app.use('/dist', express.static(path.join(__dirname, '../client/dist'))); -} else if (process.env.NODE_ENV.trim() === 'production') { - app.use('/dist', express.static(path.join(__dirname, '../client/dist'))); -} +app.use('/dist', express.static(path.join(__dirname, '../client/dist'))); // set up routing for static content that isn't being bundled. app.use('/images', express.static(path.join(__dirname, '../client/src/images'))); app.use('/styles', express.static(path.join(__dirname, '../client/src/styles'))); app.use('/webfonts', express.static(path.join(__dirname, '../client/src/webfonts'))); - +app.use('/robots.txt', (req, res) => { + res.sendFile(path.join(__dirname, '../client/robots.txt')); +}); app.use(function (req, res) { res.sendFile(path.join(__dirname, '../client/src/views/404.html')); }); -inGame.on('connection', function (socket) { - gameManager.addGameSocketHandlers(inGame, socket); -}); - -main.listen(port, function () { - logger.log(`Starting server on port ${port}` ); +main.listen(args.port, function () { + logger.log(`Starting server on port ${args.port}` ); }); diff --git a/server/modules/ServerBootstrapper.js b/server/modules/ServerBootstrapper.js new file mode 100644 index 0000000..345fe3b --- /dev/null +++ b/server/modules/ServerBootstrapper.js @@ -0,0 +1,106 @@ +const LOG_LEVEL = require('../config/globals').LOG_LEVEL; +const http = require('http'); +const https = require('https'); +const path = require('path'); +const fs = require('fs'); +const cors = require('cors') + +const ServerBootstrapper = { + processCLIArgs: () => { + try { + let args = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())); + const useHttps = args.includes('https'); + const port = process.env.PORT || args + .filter((arg) => { + return /port=\d+/.test(arg); + }) + .map((arg) => { + return /port=(\d+)/.exec(arg)[1]; + })[0] || 5000; + const logLevel = process.env.LOG_LEVEL || args + .filter((arg) => { + return /loglevel=[a-zA-Z]+/.test(arg); + }) + .map((arg) => { + return /loglevel=([a-zA-Z]+)/.exec(arg)[1]; + })[0] || LOG_LEVEL.INFO; + + return { + useHttps: useHttps, + port: port, + logLevel: logLevel + }; + } catch (e) { + throw new Error("Your server run command is malformed. Consult the codebase wiki for proper usage. Error: " + e); + } + }, + + createServerWithCorrectHTTPProtocol: (app, useHttps, port, logger) => { + let main; + if (process.env.NODE_ENV.trim() === 'development') { + logger.log('starting main in DEVELOPMENT mode.'); + if ( + useHttps + && fs.existsSync(path.join(__dirname, '../../client/certs/localhost-key.pem')) + && fs.existsSync(path.join(__dirname, '../../client/certs/localhost.pem')) + ) { + const key = fs.readFileSync(path.join(__dirname, '../../client/certs/localhost-key.pem'), 'utf-8'); + const cert = fs.readFileSync(path.join(__dirname, '../../client/certs/localhost.pem'), 'utf-8'); + logger.log('local certs detected. Using HTTPS.'); + main = https.createServer({ key, cert }, app); + logger.log(`navigate to https://localhost:${port}`); + } else { + logger.log('https not specified or no local certs detected. Certs should reside in /client/certs. Using HTTP.'); + main = http.createServer(app); + logger.log(`navigate to http://localhost:${port}`); + } + } else { + logger.warn('starting main in PRODUCTION mode. This should not be used for local development.'); + main = http.createServer(app); + app.use(require('express-force-https')); + } + + return main; + }, + + createSocketServer: (main, app, port) => { + let io; + if (process.env.NODE_ENV.trim() === 'development') { + const corsOptions = { + origin: "http://localhost:" + port, + optionsSuccessStatus: 200, + methods: ["GET", "POST"] + } + app.use(cors(corsOptions)); + io = require("socket.io")(main, { + cors: { + origin: "http://localhost:" + port, + methods: ["GET", "POST"], + allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], + credentials: false + } + }); + } else { + const corsOptions = { + origin: ["https://playwerewolf.uk.r.appspot.com"], + methods: ["GET", "POST"], + allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], + optionsSuccessStatus: 200, + } + app.use(cors(corsOptions)); + io = require("socket.io")(main, { + cors: { + origin: ["https://playwerewolf.uk.r.appspot.com", "wss://playwerewolf.uk.r.appspot.com"], + methods: ["GET", "POST"], + allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], + credentials: false + }, + transports: ["polling"] + }); + } + + return io.of('/in-game'); + } +} + +module.exports = ServerBootstrapper; From f3c04a39f37325a72cd63c880fe8dfbc875a2b5c Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 17:01:53 -0500 Subject: [PATCH 39/60] navbar --- client/src/modules/Navbar.js | 54 +++++++++++++++ client/src/scripts/create.js | 2 + client/src/scripts/game.js | 2 + client/src/scripts/home.js | 2 + client/src/styles/GLOBAL.css | 114 +++++++++++++++++++++++++++---- client/src/styles/create.css | 2 +- client/src/styles/game.css | 2 +- client/src/styles/hamburgers.css | 85 +++++++++++++++++++++++ client/src/styles/home.css | 9 +-- client/src/styles/modal.css | 2 +- client/src/views/404.html | 7 +- client/src/views/create.html | 7 +- client/src/views/game.html | 7 +- client/src/views/home.html | 5 +- spec/support/jasmine.json | 11 +++ 15 files changed, 279 insertions(+), 32 deletions(-) create mode 100644 client/src/modules/Navbar.js create mode 100644 client/src/styles/hamburgers.css create mode 100644 spec/support/jasmine.json diff --git a/client/src/modules/Navbar.js b/client/src/modules/Navbar.js new file mode 100644 index 0000000..2d3472e --- /dev/null +++ b/client/src/modules/Navbar.js @@ -0,0 +1,54 @@ +export const injectNavbar = (page=null) => { + if (document.getElementById('navbar') !== null) { + document.getElementById('navbar').innerHTML = + "' + + '' + + '' + + '
    ' + + '' + + '
    ' + + ''; + } + attachHamburgerListener(); +}; + +function flipHamburger () { + const hamburger = document.getElementById('navbar-hamburger'); + if (hamburger.classList.contains('is-active')) { + hamburger.classList.remove('is-active'); + document.getElementById('mobile-menu').classList.add('hidden'); + document.getElementById('mobile-menu-background-overlay').classList.remove('overlay'); + } else { + hamburger.classList.add('is-active'); + document.getElementById('mobile-menu-background-overlay').classList.add('overlay'); + document.getElementById('mobile-menu').classList.remove('hidden'); + } +} + +function getNavbarLinks (page=null, device) { + const linkClass = device === 'mobile' ? 'mobile-link' : 'desktop-link'; + return '' + + 'Home' + + 'Create' + + 'How to Use' + + 'Contact' + + 'Support the App' +} + +function attachHamburgerListener () { + if (document.getElementById('navbar') !== null && document.getElementById('navbar').style.display !== 'none') { + document.getElementById('navbar-hamburger').addEventListener('click', flipHamburger); + } +} diff --git a/client/src/scripts/create.js b/client/src/scripts/create.js index dd9dcaa..cdb8bf2 100644 --- a/client/src/scripts/create.js +++ b/client/src/scripts/create.js @@ -2,8 +2,10 @@ import { defaultCards } from "../config/defaultCards.js"; import { customCards } from "../config/customCards.js"; import { DeckStateManager } from "../modules/DeckStateManager.js"; import { GameCreationStepManager } from "../modules/GameCreationStepManager.js"; +import { injectNavbar } from "../modules/Navbar.js"; const create = () => { + injectNavbar(); let deckManager = new DeckStateManager(); let gameCreationStepManager = new GameCreationStepManager(deckManager); loadDefaultCards(deckManager); diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index 5e77c73..0c9d578 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -7,8 +7,10 @@ import {GameTimerManager} from "../modules/GameTimerManager.js"; import {ModalManager} from "../modules/ModalManager.js"; import {stateBucket} from "../modules/StateBucket.js"; import { io } from 'socket.io-client'; +import { injectNavbar } from "../modules/Navbar.js"; const game = () => { + injectNavbar(); let timerWorker; const socket = io('/in-game'); socket.on('disconnect', () => { diff --git a/client/src/scripts/home.js b/client/src/scripts/home.js index 65664a7..b7372a0 100644 --- a/client/src/scripts/home.js +++ b/client/src/scripts/home.js @@ -1,7 +1,9 @@ import { XHRUtility } from "../modules/XHRUtility.js"; import { toast } from "../modules/Toast.js"; +import { injectNavbar } from "../modules/Navbar.js"; const home = () => { + injectNavbar(); document.getElementById("join-form").onsubmit = (e) => { e.preventDefault(); let userCode = document.getElementById("room-code").value; diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 7afcd54..4fc7402 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -24,7 +24,6 @@ th, thead, tr, tt, u, ul, var { html { font-family: 'signika-negative', sans-serif !important; background-color: #0f0f10; - height: 100%; } body { @@ -78,7 +77,7 @@ h3 { flex-wrap: wrap; color: #d7d7d7; font-size: 14px; - margin-top: 3em; + margin-top: 4em; margin-bottom: 0.5em; } #footer a img { @@ -138,7 +137,7 @@ button { justify-content: center; } -button, input[type="submit"] { +button:not(#navbar-hamburger), input[type="submit"] { font-family: 'signika-negative', sans-serif !important; padding: 10px; background-color: #13762b; @@ -147,17 +146,16 @@ button, input[type="submit"] { font-size: 18px; cursor: pointer; border: 2px solid transparent; - transition: background-color 0.3s ease-out; text-shadow: 0 3px 4px rgb(0 0 0 / 55%); } -button:active, input[type=submit]:active { +button:not(#navbar-hamburger):active, input[type=submit]:active { border: 2px solid #21ba45; } .cancel:hover { background-color: #623232 !important; - border: 2px solid #8a1c1c; + border: 2px solid #8a1c1c !important; } .container { @@ -171,7 +169,7 @@ button:active, input[type=submit]:active { align-items: center; } -button:hover, input[type="submit"]:hover, #game-link:hover { +button:not(#navbar-hamburger):hover, input[type="submit"]:hover, #game-link:hover { background-color: #326243; border: 2px solid #1c8a36; } @@ -212,26 +210,118 @@ input { padding: 5px 0; width: 100%; background-color: #333243; - border-bottom: 2px solid #57566a + border-bottom: 2px solid #57566a; + height: 51px; } -#navbar img { - margin: 0 1em; +#desktop-links > a:nth-child(1), #mobile-links a:nth-child(1) { + margin: 0 0.5em; width: 50px; } -#navbar a { +.logo { + display: flex; + align-items: center; +} + +#navbar img { + width: 100%; +} + +#navbar a:not(.logo) { color: #f7f7f7; text-decoration: none; cursor: pointer; - font-size: 25px; font-family: 'diavlo', sans-serif; + border-radius: 5px; + padding: 2px 5px; + font-size: 18px; + margin: 0 0.5em; + width: fit-content; } #navbar a:hover { color: gray; } +.overlay { + position: fixed; + background-size: cover; + height: 100%; + opacity: 50%; + background-color: black; + width: 100%; + z-index: 50000; +} + +.hidden { + display: none !important; +} + +.hamburger-light { + min-width: auto; + color: whitesmoke; +} + +.hamburger-dark { + min-width: auto; + color: black; +} + +#mobile-links { + display: flex; + flex-direction: column; + margin-top: 3em; +} + +.mobile-link { + margin-top: 0.5em !important; + margin-left: 1em !important; +} + +@media(max-width: 1000px) { + #navbar { + display: flex; + padding: 0; + } + + #navbar-hamburger { + z-index: 52000; + position: relative; + } + + #desktop-menu { + display: none; + } + + #mobile-menu { + position: absolute; + top: 0; + left: 0; + height: auto; + padding-bottom: 2em; + width: 100%; + z-index: 51000; + background-color: #191920; + } +} + +@media(min-width: 1001px) { + + #navbar-hamburger, #mobile-menu, #mobile-menu-background-overlay { + display: none; + } + + .desktop-link { + display: flex; + } + + #desktop-links { + display: flex; + align-items: center; + } +} + .flex-row-container { display: flex; flex-direction: row; diff --git a/client/src/styles/create.css b/client/src/styles/create.css index 83626d3..3fb57f8 100644 --- a/client/src/styles/create.css +++ b/client/src/styles/create.css @@ -288,7 +288,7 @@ input[type="number"] { #step-back-button { left: 15%; - background-color: #762323; + background-color: #762323 !important; } #step-1 div { diff --git a/client/src/styles/game.css b/client/src/styles/game.css index 5577605..1983805 100644 --- a/client/src/styles/game.css +++ b/client/src/styles/game.css @@ -660,7 +660,7 @@ label[for='moderator'] { padding: 10px 10px 0 10px; border-radius: 3px; min-height: 25em; - min-width: 15em; + min-width: 18em; max-width: 30em; } diff --git a/client/src/styles/hamburgers.css b/client/src/styles/hamburgers.css new file mode 100644 index 0000000..be217df --- /dev/null +++ b/client/src/styles/hamburgers.css @@ -0,0 +1,85 @@ +/*! + * Hamburgers + * @description Tasty CSS-animated hamburgers + * @author Jonathan Suh @jonsuh + * @site https://jonsuh.com/hamburgers + * @link https://github.com/jonsuh/hamburgers + */ +.hamburger { + padding: 10px 10px; + display: inline-block; + cursor: pointer; + transition-property: opacity, filter; + transition-duration: 0.15s; + transition-timing-function: linear; + font: inherit; + color: inherit; + text-transform: none; + background-color: transparent; + border: 0; + margin: 0; + overflow: visible; +} +.hamburger:hover { + opacity: 0.7; } +.hamburger.is-active:hover { + opacity: 0.7; } +.hamburger.is-active .hamburger-inner, +.hamburger.is-active .hamburger-inner::before, +.hamburger.is-active .hamburger-inner::after { + background-color: #b1afcd; } + +.hamburger-box { + width: 40px; + height: 24px; + display: inline-block; + position: relative; } + +.hamburger-inner { + display: block; + top: 50%; + margin-top: -2px; } +.hamburger-inner, .hamburger-inner::before, .hamburger-inner::after { + width: 40px; + height: 4px; + background-color: #b1afcd; + border-radius: 4px; + position: absolute; + transition-property: transform; + transition-duration: 0.15s; + transition-timing-function: ease; } +.hamburger-inner::before, .hamburger-inner::after { + content: ""; + display: block; } +.hamburger-inner::before { + top: -10px; } +.hamburger-inner::after { + bottom: -10px; } + +/* + * Collapse + */ +.hamburger--collapse .hamburger-inner { + top: auto; + bottom: 0; + transition-duration: 0.13s; + transition-delay: 0.13s; + transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } +.hamburger--collapse .hamburger-inner::after { + top: -20px; + transition: top 0.2s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), opacity 0.1s linear; } +.hamburger--collapse .hamburger-inner::before { + transition: top 0.12s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19); } + +.hamburger--collapse.is-active .hamburger-inner { + transform: translate3d(0, -10px, 0) rotate(-45deg); + transition-delay: 0.22s; + transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } +.hamburger--collapse.is-active .hamburger-inner::after { + top: 0; + opacity: 0; + transition: top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333), opacity 0.1s 0.22s linear; } +.hamburger--collapse.is-active .hamburger-inner::before { + top: 0; + transform: rotate(-90deg); + transition: top 0.1s 0.16s cubic-bezier(0.33333, 0, 0.66667, 0.33333), transform 0.13s 0.25s cubic-bezier(0.215, 0.61, 0.355, 1); } diff --git a/client/src/styles/home.css b/client/src/styles/home.css index a2b3e5f..81c1ff7 100644 --- a/client/src/styles/home.css +++ b/client/src/styles/home.css @@ -1,14 +1,15 @@ html { background: rgb(0,0,0); background: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgb(17 18 18) 35%, rgba(27,31,31,1) 100%); + height: 100%; } body { align-items: center; - justify-content: center; + height: 100%; } -button { +button#home-create-button { padding: 20px; margin-bottom: 1em; } @@ -80,13 +81,13 @@ label[for="room-code"], label[for="player-name"] { } @media (min-width: 700px) { - button { + button#home-create-button { font-size: 35px; } } @media (max-width: 701px) { - button { + button#home-create-button { font-size: 5vw; } } diff --git a/client/src/styles/modal.css b/client/src/styles/modal.css index 20b41e8..4854377 100644 --- a/client/src/styles/modal.css +++ b/client/src/styles/modal.css @@ -39,7 +39,7 @@ } #close-modal-button { - background-color: #762323; + background-color: #762323 !important; } #modal-button-container { diff --git a/client/src/views/404.html b/client/src/views/404.html index 3624117..f67af9e 100644 --- a/client/src/views/404.html +++ b/client/src/views/404.html @@ -16,12 +16,11 @@ + - +
    +

    404

    The game or other resource that you are looking for could not be found, or you don't have permission to access it. diff --git a/client/src/views/create.html b/client/src/views/create.html index 0db4447..6f35d31 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -16,12 +16,11 @@ + - +
    +

    Connecting to game...

    - +
    +
    diff --git a/client/src/views/home.html b/client/src/views/home.html index 9ebb3ff..00cea22 100644 --- a/client/src/views/home.html +++ b/client/src/views/home.html @@ -18,12 +18,15 @@ + +
    +

    A tool to run werewolf when not in-person, or in any setting without a deck of cards.

    - +
    diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..1f935fe --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,11 @@ +{ + "spec_dir": "spec/unit", + "spec_files": [ + "**/*[sS]pec.js" + ], + "helpers": [ + "helpers/**/*.js" + ], + "stopSpecOnExpectationFailure": false, + "random": true +} From 60359f66e085dbe6b6e04fb739457e182321139a Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 17:43:59 -0500 Subject: [PATCH 40/60] styling tweaks --- client/src/styles/GLOBAL.css | 7 +++++-- client/src/styles/create.css | 2 +- client/src/styles/home.css | 8 +++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 4fc7402..1728faa 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -24,6 +24,7 @@ th, thead, tr, tt, u, ul, var { html { font-family: 'signika-negative', sans-serif !important; background-color: #0f0f10; + overflow: auto; } body { @@ -32,6 +33,8 @@ body { align-items: flex-start; margin: 0 auto; position: relative; + background-color: #0f0f10; + height: 100vh; } .bmc-btn { @@ -235,8 +238,8 @@ input { font-family: 'diavlo', sans-serif; border-radius: 5px; padding: 2px 5px; - font-size: 18px; - margin: 0 0.5em; + font-size: 19px; + margin: 0 0.75em; width: fit-content; } diff --git a/client/src/styles/create.css b/client/src/styles/create.css index 3fb57f8..6e29a76 100644 --- a/client/src/styles/create.css +++ b/client/src/styles/create.css @@ -192,7 +192,7 @@ label[for="game-time"], label[for="add-card-to-deck-form"], label[for="deck"] { } input[type="number"] { - min-width: 3em; + width: 3em; font-size: 40px; } diff --git a/client/src/styles/home.css b/client/src/styles/home.css index 81c1ff7..a727b4f 100644 --- a/client/src/styles/home.css +++ b/client/src/styles/home.css @@ -1,12 +1,10 @@ html { background: rgb(0,0,0); background: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgb(17 18 18) 35%, rgba(27,31,31,1) 100%); - height: 100%; } body { align-items: center; - height: 100%; } button#home-create-button { @@ -17,7 +15,7 @@ button#home-create-button { form { display: flex; flex-wrap: wrap; - margin: 1em 0; + margin: 10px 0; padding: 10px; border-radius: 3px; background-color: black; @@ -90,4 +88,8 @@ label[for="room-code"], label[for="player-name"] { button#home-create-button { font-size: 5vw; } + + #room-code { + max-width: 9em; + } } From cb541c8bf0b91b8e170d76a35a8f7b1f801aa7dc Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 18:04:23 -0500 Subject: [PATCH 41/60] add not found script --- client/src/scripts/notFound.js | 9 +++++++++ client/webpack/webpack-dev.config.js | 3 ++- client/webpack/webpack-prod.config.js | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 client/src/scripts/notFound.js diff --git a/client/src/scripts/notFound.js b/client/src/scripts/notFound.js new file mode 100644 index 0000000..e5a5ee2 --- /dev/null +++ b/client/src/scripts/notFound.js @@ -0,0 +1,9 @@ +import { injectNavbar } from "../modules/Navbar.js"; + +const notFound = () => { injectNavbar(); }; + +if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = notFound; +} else { + notFound(); +} diff --git a/client/webpack/webpack-dev.config.js b/client/webpack/webpack-dev.config.js index c884208..07ad9c0 100644 --- a/client/webpack/webpack-dev.config.js +++ b/client/webpack/webpack-dev.config.js @@ -4,7 +4,8 @@ module.exports = { entry: { game: './client/src/scripts/game.js', home: './client/src/scripts/home.js', - create: './client/src/scripts/create.js' + create: './client/src/scripts/create.js', + notFound: './client/src/scripts/notFound.js' }, output: { path: path.resolve(__dirname, '../dist'), diff --git a/client/webpack/webpack-prod.config.js b/client/webpack/webpack-prod.config.js index 743bd7a..a39d2fd 100644 --- a/client/webpack/webpack-prod.config.js +++ b/client/webpack/webpack-prod.config.js @@ -5,7 +5,8 @@ module.exports = { entry: { game: './client/src/scripts/game.js', home: './client/src/scripts/home.js', - create: './client/src/scripts/create.js' + create: './client/src/scripts/create.js', + notFound: './client/src/scripts/notFound.js' }, output: { path: path.resolve(__dirname, '../dist'), From a7c85a8d0f9b288c212db0536cfd0f9593065438 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 28 Dec 2021 18:11:42 -0500 Subject: [PATCH 42/60] add script --- client/src/views/404.html | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/client/src/views/404.html b/client/src/views/404.html index f67af9e..5e96291 100644 --- a/client/src/views/404.html +++ b/client/src/views/404.html @@ -28,10 +28,6 @@ + From 9de38cec5b443982c36d1083866292da3f8a90bd Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Wed, 29 Dec 2021 01:43:43 -0500 Subject: [PATCH 43/60] fix bugs with mod transfer --- client/src/modules/GameCreationStepManager.js | 4 +++ client/src/modules/GameStateRenderer.js | 4 +++ client/src/modules/Templates.js | 20 +++++------ client/src/scripts/game.js | 1 + client/src/styles/GLOBAL.css | 11 +++--- client/src/styles/game.css | 25 +++++++------ client/src/styles/home.css | 4 +-- client/src/views/create.html | 2 +- client/src/views/game.html | 9 ----- client/src/views/home.html | 2 +- server/modules/GameManager.js | 36 ++++++++++++------- 11 files changed, 68 insertions(+), 50 deletions(-) diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index 2dd9b54..c46b891 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -320,6 +320,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) backButton.addEventListener('click', backHandler); backButton.setAttribute("id", "step-back-button"); backButton.classList.add('cancel'); + backButton.classList.add('app-button'); document.getElementById("tracker-container").prepend(backButton); } @@ -328,11 +329,13 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) fwdButton.innerHTML = "Next \u25b6"; fwdButton.addEventListener('click', forwardHandler); fwdButton.setAttribute("id", "step-forward-button"); + fwdButton.classList.add('app-button'); document.getElementById("tracker-container").appendChild(fwdButton); } else if (forward && builtGame !== null) { let createButton = document.createElement("button"); createButton.innerText = "Create"; createButton.setAttribute("id", "create-game"); + createButton.classList.add('app-button'); createButton.addEventListener("click", () => { forwardHandler( builtGame.deck.filter((card) => card.quantity > 0), @@ -369,6 +372,7 @@ function loadCustomRoles(deckManager) { let createRoleButton = document.createElement("button"); createRoleButton.setAttribute("id", "custom-role-btn"); + createRoleButton.classList.add('app-button'); createRoleButton.innerText = '+ Create Custom Role'; let form = document.createElement("form"); diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 93fd873..a17795a 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -245,6 +245,10 @@ export class GameStateRenderer { this.transferModHandlers, this.socket ); + + if (document.querySelectorAll('.potential-moderator').length === 0) { + document.getElementById("transfer-mod-modal-content").innerText = "There is nobody available to transfer to." + } } renderEndOfGame() { diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index c1179a5..3bbade3 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -16,7 +16,7 @@ export const templates = { "
    " + "
    " + "
    " + - "
    " + "
    " + "
    " + @@ -43,7 +43,7 @@ export const templates = { "
    " + "
    " + "
    " + - "
    " + "

    " + "" + "
    " + - "
    " + "
    " + "
    " + @@ -79,7 +79,7 @@ export const templates = { "

    Transfer Mod Powers 👑

    " + "
    " + "" + "
    " + "
    " + @@ -92,7 +92,7 @@ export const templates = { "
    " + "" + "
    " + - "
    " + "
    " + "
    " + @@ -116,7 +116,7 @@ export const templates = { "

    Transfer Mod Powers 👑

    " + "
    " + "" + "" + "" + @@ -129,7 +129,7 @@ export const templates = { "
    " + "
    " + "" + "
    " + - "
    " + "" + "", END_OF_GAME_VIEW: "

    The moderator has ended the game. Roles are revealed.

    " + "
    " + "
    " + - "
    " + "" + "
    " + diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index 0c9d578..082d452 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -87,6 +87,7 @@ function initializeGame(stateBucket, socket, timerWorker, userId, gameStateRende } function processGameState (currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker) { + console.log(currentGameState); displayClientInfo(currentGameState.client.name, currentGameState.client.userType); switch (currentGameState.status) { case globals.STATUS.LOBBY: diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 1728faa..12aa42b 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -34,7 +34,6 @@ body { margin: 0 auto; position: relative; background-color: #0f0f10; - height: 100vh; } .bmc-btn { @@ -140,7 +139,7 @@ button { justify-content: center; } -button:not(#navbar-hamburger), input[type="submit"] { +.app-button, input[type="submit"] { font-family: 'signika-negative', sans-serif !important; padding: 10px; background-color: #13762b; @@ -152,10 +151,14 @@ button:not(#navbar-hamburger), input[type="submit"] { text-shadow: 0 3px 4px rgb(0 0 0 / 55%); } -button:not(#navbar-hamburger):active, input[type=submit]:active { +.app-button:active, input[type=submit]:active { border: 2px solid #21ba45; } +.cancel { + background-color: #762323 !important; +} + .cancel:hover { background-color: #623232 !important; border: 2px solid #8a1c1c !important; @@ -172,7 +175,7 @@ button:not(#navbar-hamburger):active, input[type=submit]:active { align-items: center; } -button:not(#navbar-hamburger):hover, input[type="submit"]:hover, #game-link:hover { +.app-button:hover, input[type="submit"]:hover, #game-link:hover { background-color: #326243; border: 2px solid #1c8a36; } diff --git a/client/src/styles/game.css b/client/src/styles/game.css index 1983805..f32930f 100644 --- a/client/src/styles/game.css +++ b/client/src/styles/game.css @@ -14,10 +14,6 @@ margin: 0.5em 0; } -body { - margin-bottom: 100px; -} - #lobby-players { overflow-y: auto; max-height: 30em; @@ -49,7 +45,7 @@ body { flex-wrap: wrap; display: flex; width: 95%; - margin: 1em auto 0 auto; + margin: 1em auto 85px auto; } #game-state-container h2 { @@ -66,7 +62,7 @@ h1 { margin: 0.5em auto; } -#game-state-container > div:not(#transfer-mod-modal-background):not(#transfer-mod-modal){ +#game-state-container > div:not(#transfer-mod-modal-background):not(#transfer-mod-modal):not(#game-people-container) { margin: 1em; } @@ -76,7 +72,7 @@ h1 { #footer.game-page-footer { margin-top: 1em; - margin-bottom: 1em; + margin-bottom: 85px; } #footer.game-page-footer a { @@ -581,6 +577,13 @@ label[for='moderator'] { background-color: #3f5256; font-size: 18px; padding: 10px; + border: 2px transparent; + border-radius: 3px; + color: #d7d7d7; +} + +.make-mod-button:hover { + cursor: pointer; } .good-players { @@ -660,12 +663,14 @@ label[for='moderator'] { padding: 10px 10px 0 10px; border-radius: 3px; min-height: 25em; - min-width: 18em; - max-width: 30em; + max-width: 25em; + min-width: 17em; + margin-top: 1em; } #transfer-mod-modal-content { margin-bottom: 2em; + color: #d7d7d7; } #game-state-container.vertical-flex { @@ -683,7 +688,7 @@ label[for='moderator'] { } #game-state-container { - margin: 0 auto 0 auto; + margin: 0 auto 85px auto; } button { diff --git a/client/src/styles/home.css b/client/src/styles/home.css index a727b4f..2bcc5f7 100644 --- a/client/src/styles/home.css +++ b/client/src/styles/home.css @@ -1,6 +1,6 @@ html { - background: rgb(0,0,0); - background: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgb(17 18 18) 35%, rgba(27,31,31,1) 100%); + /*background: rgb(0,0,0); + background: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgb(17 18 18) 35%, rgba(27,31,31,1) 100%);*/ } body { diff --git a/client/src/views/create.html b/client/src/views/create.html index 6f35d31..ae67cae 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -41,7 +41,7 @@ diff --git a/client/src/views/game.html b/client/src/views/game.html index f23c587..89ab19a 100644 --- a/client/src/views/game.html +++ b/client/src/views/game.html @@ -58,15 +58,6 @@
    -
    -
    - - -
    -
    -

    Werewolf created by Andrew Plotkin

    -
    -
    diff --git a/client/src/views/home.html b/client/src/views/home.html index 00cea22..4239e3f 100644 --- a/client/src/views/home.html +++ b/client/src/views/home.html @@ -26,7 +26,7 @@

    A tool to run werewolf when not in-person, or in any setting without a deck of cards.

    - +
    diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 35ce963..124ed35 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -102,13 +102,15 @@ class GameManager { let person = game.people.find((person) => person.id === personId) if (person && !person.out) { this.logger.debug('game ' + accessCode + ': killing player ' + person.name); - person.userType = globals.USER_TYPES.KILLED_PLAYER; + if (person.userType !== globals.USER_TYPES.TEMPORARY_MODERATOR) { + person.userType = globals.USER_TYPES.KILLED_PLAYER; + } person.out = true; + namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.KILL_PLAYER, person.id); // temporary moderators will transfer their powers automatically to the first person they kill. if (game.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { transferModeratorPowers(game, person, namespace, this.logger); } - namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.KILL_PLAYER, person.id) } } }); @@ -313,19 +315,27 @@ class Singleton { function transferModeratorPowers(game, person, namespace, logger) { if (person && (person.out || person.userType === globals.USER_TYPES.SPECTATOR)) { logger.debug('game ' + game.accessCode + ': transferring mod powers to ' + person.name); - if (game.people.includes(game.moderator)) { // the current moderator was at one point a dealt-in player. - game.moderator.userType = globals.USER_TYPES.KILLED_PLAYER; // restore their state from before being made mod. + if (game.moderator === person) { + logger.debug('temp mod killed themselves'); + person.userType = globals.USER_TYPES.MODERATOR; } else { - game.moderator.userType = globals.USER_TYPES.SPECTATOR; - if (!game.spectators.includes(game.moderator)) { - game.spectators.push(game.moderator); - } - if (game.spectators.includes(person)) { - game.spectators.splice(game.spectators.indexOf(person), 1); - } + if (game.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + game.moderator.userType = globals.USER_TYPES.PLAYER; + } else if (game.moderator.gameRole) { // the current moderator was at one point a dealt-in player. + game.moderator.userType = globals.USER_TYPES.KILLED_PLAYER; // restore their state from before being made mod. + } else if (game.moderator.userType === globals.USER_TYPES.MODERATOR) { + game.moderator.userType = globals.USER_TYPES.SPECTATOR; + if (!game.spectators.includes(game.moderator)) { + game.spectators.push(game.moderator); + } + if (game.spectators.includes(person)) { + game.spectators.splice(game.spectators.indexOf(person), 1); + } + } + person.userType = globals.USER_TYPES.MODERATOR; + game.moderator = person; } - person.userType = globals.USER_TYPES.MODERATOR; - game.moderator = person; + namespace.in(game.accessCode).emit(globals.EVENTS.SYNC_GAME_STATE); } } From 94a6ac595baeee5a778e47ce39b993f8899968e3 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Wed, 29 Dec 2021 14:55:23 -0500 Subject: [PATCH 44/60] various tweaks --- client/src/modules/GameCreationStepManager.js | 4 ++-- client/src/scripts/game.js | 10 +++------- client/src/styles/GLOBAL.css | 4 ++-- client/src/styles/home.css | 13 +++++++++++-- client/src/styles/modal.css | 2 +- client/src/views/create.html | 4 ---- client/src/views/home.html | 16 +++++++--------- package.json | 5 ++++- server/modules/ServerBootstrapper.js | 2 +- 9 files changed, 31 insertions(+), 29 deletions(-) diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index c46b891..0f508b5 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -316,7 +316,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) document.querySelector("#create-game")?.remove(); if (back) { let backButton = document.createElement("button"); - backButton.innerText = "\u2bc7 Back"; + backButton.innerText = "\u25C0"; backButton.addEventListener('click', backHandler); backButton.setAttribute("id", "step-back-button"); backButton.classList.add('cancel'); @@ -326,7 +326,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) if (forward && builtGame === null) { let fwdButton = document.createElement("button"); - fwdButton.innerHTML = "Next \u25b6"; + fwdButton.innerHTML = "\u25b6"; fwdButton.addEventListener('click', forwardHandler); fwdButton.setAttribute("id", "step-forward-button"); fwdButton.classList.add('app-button'); diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index 082d452..c9d2946 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -87,8 +87,9 @@ function initializeGame(stateBucket, socket, timerWorker, userId, gameStateRende } function processGameState (currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker) { - console.log(currentGameState); displayClientInfo(currentGameState.client.name, currentGameState.client.userType); + document.querySelector("#start-game-prompt")?.remove(); + document.querySelector("#end-game-prompt")?.remove(); switch (currentGameState.status) { case globals.STATUS.LOBBY: document.getElementById("game-state-container").innerHTML = templates.LOBBY; @@ -112,22 +113,19 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer, gameStateRenderer.renderPlayerView(); break; case globals.USER_TYPES.KILLED_PLAYER: - document.querySelector("#end-game-prompt")?.remove(); + document.getElementById("game-state-container").innerHTML = templates.PLAYER_GAME_VIEW; gameStateRenderer.renderPlayerView(true); break; case globals.USER_TYPES.MODERATOR: - document.querySelector("#start-game-prompt")?.remove(); document.getElementById("game-state-container").innerHTML = templates.MODERATOR_GAME_VIEW; gameStateRenderer.renderModeratorView(); break; case globals.USER_TYPES.TEMPORARY_MODERATOR: - document.querySelector("#start-game-prompt")?.remove(); document.getElementById("game-state-container").innerHTML = templates.TEMP_MOD_GAME_VIEW; gameStateRenderer.renderTempModView(); break; case globals.USER_TYPES.SPECTATOR: - document.querySelector("#end-game-prompt")?.remove(); document.getElementById("game-state-container").innerHTML = templates.SPECTATOR_GAME_VIEW; gameStateRenderer.renderSpectatorView(); break; @@ -144,7 +142,6 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer, } break; case globals.STATUS.ENDED: - document.querySelector("#end-game-prompt")?.remove(); let container = document.getElementById("game-state-container") container.innerHTML = templates.END_OF_GAME_VIEW; container.classList.add('vertical-flex'); @@ -263,7 +260,6 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo if (!socket.hasListeners(globals.COMMANDS.END_GAME)) { socket.on(globals.COMMANDS.END_GAME, (people) => { - document.querySelector("#end-game-prompt")?.remove(); stateBucket.currentGameState.people = people; stateBucket.currentGameState.status = globals.STATUS.ENDED; processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer, gameTimerManager, timerWorker); diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 12aa42b..189755a 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -241,7 +241,7 @@ input { font-family: 'diavlo', sans-serif; border-radius: 5px; padding: 2px 5px; - font-size: 19px; + font-size: 20px; margin: 0 0.75em; width: fit-content; } @@ -281,7 +281,7 @@ input { } .mobile-link { - margin-top: 0.5em !important; + margin-top: 1em !important; margin-left: 1em !important; } diff --git a/client/src/styles/home.css b/client/src/styles/home.css index 2bcc5f7..a61f86b 100644 --- a/client/src/styles/home.css +++ b/client/src/styles/home.css @@ -12,13 +12,22 @@ button#home-create-button { margin-bottom: 1em; } +#home-page-top-section { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + background-color: #1e1b26; + margin-bottom: 15px; +} + form { display: flex; flex-wrap: wrap; margin: 10px 0; padding: 10px; border-radius: 3px; - background-color: black; justify-content: center; align-items: center; } @@ -60,7 +69,7 @@ img[src='../images/logo_cropped.gif'] { } form > div { - margin: 1em 0; + margin: 15px 0; } #join-container { diff --git a/client/src/styles/modal.css b/client/src/styles/modal.css index 4854377..0988214 100644 --- a/client/src/styles/modal.css +++ b/client/src/styles/modal.css @@ -1,7 +1,7 @@ .modal { border-radius: 2px; text-align: center; - position: absolute; + position: fixed; width: 85%; z-index: 100; top: 50%; diff --git a/client/src/views/create.html b/client/src/views/create.html index ae67cae..a12a0f9 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -88,9 +88,5 @@
    - - diff --git a/client/src/views/home.html b/client/src/views/home.html index 4239e3f..22a764b 100644 --- a/client/src/views/home.html +++ b/client/src/views/home.html @@ -23,11 +23,13 @@
    - -

    A tool to run werewolf when not in-person, or in any setting without a deck of cards.

    - - - +
    + +

    A tool to run werewolf when not in-person, or in any setting without a deck of cards.

    + + + +
    @@ -49,9 +51,5 @@
    - - diff --git a/package.json b/package.json index c37d3f7..d0ef0aa 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,9 @@ }, "devDependencies": {}, "nodemonConfig": { - "ignore": ["client/*", "node_modules/*"] + "ignore": [ + "client/*", + "node_modules/*" + ] } } diff --git a/server/modules/ServerBootstrapper.js b/server/modules/ServerBootstrapper.js index 345fe3b..ee960c0 100644 --- a/server/modules/ServerBootstrapper.js +++ b/server/modules/ServerBootstrapper.js @@ -93,7 +93,7 @@ const ServerBootstrapper = { origin: ["https://playwerewolf.uk.r.appspot.com", "wss://playwerewolf.uk.r.appspot.com"], methods: ["GET", "POST"], allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], - credentials: false + credentials: true }, transports: ["polling"] }); From 9ba1cff82c77d15d49b534f8f7633ebed6ea2e80 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Wed, 29 Dec 2021 15:47:46 -0500 Subject: [PATCH 45/60] fix failure to attach event listener --- client/src/modules/GameStateRenderer.js | 35 +++++++++++++------------ client/src/styles/GLOBAL.css | 3 ++- client/src/styles/game.css | 8 ++++-- server/modules/GameManager.js | 23 +++++++++------- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index a17795a..27709b9 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -30,7 +30,7 @@ export class GameStateRenderer { } let playerCount = this.stateBucket.currentGameState.people.length; document.querySelector("label[for='lobby-players']").innerText = - "People (" + playerCount + "/" + getGameSize(this.stateBucket.currentGameState.deck) + " Players)"; + "Participants (" + playerCount + "/" + getGameSize(this.stateBucket.currentGameState.deck) + " Players)"; } renderLobbyHeader() { @@ -92,19 +92,7 @@ export class GameStateRenderer { } renderModeratorView() { - let div = document.createElement("div"); - div.innerHTML = templates.END_GAME_PROMPT; - div.querySelector("#end-game-button").addEventListener('click', (e) => { - e.preventDefault(); - if (confirm("End the game?")) { - this.socket.emit( - globals.COMMANDS.END_GAME, - this.stateBucket.currentGameState.accessCode - ); - } - }); - document.getElementById("game-content").appendChild(div); - + createEndGamePromptComponent(this.socket, this.stateBucket); let modTransferButton = document.getElementById("mod-transfer-button"); modTransferButton.addEventListener( @@ -121,9 +109,7 @@ export class GameStateRenderer { } renderTempModView() { - let div = document.createElement("div"); - div.innerHTML = templates.END_GAME_PROMPT; - document.body.appendChild(div); + createEndGamePromptComponent(this.socket, this.stateBucket) renderPlayerRole(this.stateBucket.currentGameState); this.renderPlayersWithNoRoleInformationUnlessRevealed(true); @@ -463,3 +449,18 @@ function removeExistingPlayerElements(killPlayerHandlers, revealRoleHandlers) { el.remove(); }); } + +function createEndGamePromptComponent(socket, stateBucket) { + let div = document.createElement("div"); + div.innerHTML = templates.END_GAME_PROMPT; + div.querySelector("#end-game-button").addEventListener('click', (e) => { + e.preventDefault(); + if (confirm("End the game?")) { + socket.emit( + globals.COMMANDS.END_GAME, + stateBucket.currentGameState.accessCode + ); + } + }); + document.getElementById("game-content").appendChild(div); +} diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 189755a..812f013 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -190,7 +190,7 @@ input { align-items: center; justify-content: center; position: fixed; - z-index: 1000; + z-index: 55000; padding: 10px; border-radius: 3px; font-family: 'signika-negative', sans-serif; @@ -218,6 +218,7 @@ input { background-color: #333243; border-bottom: 2px solid #57566a; height: 51px; + z-index: 53000; } #desktop-links > a:nth-child(1), #mobile-links a:nth-child(1) { diff --git a/client/src/styles/game.css b/client/src/styles/game.css index f32930f..98413a1 100644 --- a/client/src/styles/game.css +++ b/client/src/styles/game.css @@ -45,7 +45,7 @@ flex-wrap: wrap; display: flex; width: 95%; - margin: 1em auto 85px auto; + margin: 1em auto 100px auto; } #game-state-container h2 { @@ -278,6 +278,8 @@ h1 { min-width: 5em; display: flex; justify-content: center; + align-items: center; + height: 43px; } #game-timer.low { @@ -580,6 +582,7 @@ label[for='moderator'] { border: 2px transparent; border-radius: 3px; color: #d7d7d7; + font-family: signika-negative, sans-serif; } .make-mod-button:hover { @@ -663,7 +666,7 @@ label[for='moderator'] { padding: 10px 10px 0 10px; border-radius: 3px; min-height: 25em; - max-width: 25em; + max-width: 35em; min-width: 17em; margin-top: 1em; } @@ -711,6 +714,7 @@ label[for='moderator'] { #game-timer { font-size: 30px; + height: 38px; } #players-alive-label { diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 124ed35..c313dc3 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -343,17 +343,15 @@ function transferModeratorPowers(game, person, namespace, logger) { /* Since clients are anonymous, we have to rely to some extent on a cookie to identify them. Socket ids are unique to a client, but they are re-generated if a client disconnects and then reconnects. Thus, to have the most resilient identification i.e. to let them refresh, navigate away and come back, - get disconnected and reconnect, etc. we should have a combination of the socket id and the cookie. This - will also allow us to reject certain theoretical ways of breaking things, such as copying someone else's - cookie. Though if a client wants to clear their cookie and reset their connection, there's not much we can do. - The best thing in my opinion is to make it exceptionally difficult for clients to _accidentally_ break their experience. + get disconnected and reconnect, etc. we should have a combination of the socket id and the cookie. + My philosophy is to make it exceptionally difficult for clients to _accidentally_ break their experience. */ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket) { const game = gameRunner.activeGames[accessCode]; if (game) { let matchingPerson = game.people.find((person) => person.cookie === personCookie); if (!matchingPerson) { - matchingPerson = game.spectators.find((spectator) => spectator.cookie = personCookie); + matchingPerson = game.spectators.find((spectator) => spectator.cookie === personCookie); } if (game.moderator.cookie === personCookie) { matchingPerson = game.moderator; @@ -391,7 +389,6 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe : game.people.find((person) => person.assigned === false); if (unassignedPerson) { logger.trace("completely new person with a first connection to the room: " + unassignedPerson.name); - socket.join(accessCode); unassignedPerson.assigned = true; unassignedPerson.socketId = socket.id; ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson, gameRunner, socket, logger)); @@ -402,10 +399,18 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe {name: unassignedPerson.name, userType: unassignedPerson.userType}, isFull ); - } else { - rejectClientRequestForGameState(ackFn); - logger.trace('this game is full'); + } else { // if the game is full, make them a spectator. + let spectator = new Person( + createRandomId(), + createRandomId(), + UsernameGenerator.generate(), + globals.USER_TYPES.SPECTATOR + ); + logger.trace("new spectator: " + spectator.name); + game.spectators.push(spectator); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, spectator, gameRunner, socket, logger)); } + socket.join(accessCode); } } } else { From 3daf2cc6fe7468590ba1073b7f78b53c79622918 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Wed, 29 Dec 2021 16:18:24 -0500 Subject: [PATCH 46/60] tweak language --- client/src/modules/GameCreationStepManager.js | 2 +- client/src/styles/modal.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index 0f508b5..a208c07 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -471,7 +471,7 @@ function initializeRemainingEventListeners(deckManager) { deckManager.addToCustomRoleOptions({role: name, description: description, team: team}); updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")) ModalManager.dispelModal("add-role-modal", "add-role-modal-background"); - toast("Role Added", "success", true); + toast("Role Created", "success", true); } else { toast("There is already a custom role with this name.", "error", true); } diff --git a/client/src/styles/modal.css b/client/src/styles/modal.css index 0988214..4854377 100644 --- a/client/src/styles/modal.css +++ b/client/src/styles/modal.css @@ -1,7 +1,7 @@ .modal { border-radius: 2px; text-align: center; - position: fixed; + position: absolute; width: 85%; z-index: 100; top: 50%; From 522411e177ca1aadb511dc1a1155501bdce5fdd7 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Wed, 29 Dec 2021 20:45:22 -0500 Subject: [PATCH 47/60] configure env --- package.json | 2 +- server/main.js | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d0ef0aa..4421143 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build:dev": "webpack --watch --config client/webpack/webpack-dev.config.js --mode=development", "start:dev": "NODE_ENV=development nodemon server/main.js", "start:dev:windows": "SET NODE_ENV=development && nodemon server/main.js", - "start": "NODE_ENV=production node server/main.js -- loglevel=warn port=8080", + "start": "NODE_ENV=production node server/main.js -- loglevel=debug port=8080", "start:windows": "SET NODE_ENV=production && node server/main.js -- loglevel=warn port=8080", "test": "jasmine && node browsertest.js openBrowser socket", "test:unit": "jasmine", diff --git a/server/main.js b/server/main.js index 4d4a4ab..8d3006b 100644 --- a/server/main.js +++ b/server/main.js @@ -26,8 +26,14 @@ inGameSocketServer.on('connection', function (socket) { gameManager.addGameSocketHandlers(inGameSocketServer, socket); }); +let gameManager; + /* Instantiate the singleton game manager */ -const gameManager = new GameManager(logger, globals.ENVIRONMENT.LOCAL).getInstance(); // temporarily use local environment configuration for game manager +if (process.env.NODE_ENV.trim() === 'development') { + gameManager = new GameManager(logger, globals.ENVIRONMENT.LOCAL).getInstance(); +} else { + gameManager = new GameManager(logger, globals.ENVIRONMENT.PRODUCTION).getInstance(); +} /* api endpoints */ const games = require('./api/GamesAPI'); From 2cdd40ba275796a1c6130a4c6913dc53a417f621 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Thu, 30 Dec 2021 01:40:37 -0500 Subject: [PATCH 48/60] test critical parts of game manager --- jsconfig.json | 5 + package.json | 4 +- server/main.js | 2 +- server/modules/GameManager.js | 286 +++++++++---------- spec/unit/server/modules/GameManager_Spec.js | 257 +++++++++++++++++ 5 files changed, 407 insertions(+), 147 deletions(-) create mode 100644 jsconfig.json create mode 100644 spec/unit/server/modules/GameManager_Spec.js diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..83664f9 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/package.json b/package.json index 4421143..16b8f19 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,7 @@ "start:dev:windows": "SET NODE_ENV=development && nodemon server/main.js", "start": "NODE_ENV=production node server/main.js -- loglevel=debug port=8080", "start:windows": "SET NODE_ENV=production && node server/main.js -- loglevel=warn port=8080", - "test": "jasmine && node browsertest.js openBrowser socket", - "test:unit": "jasmine", - "test:e2e": "node browsertest.js" + "test": "jasmine" }, "engines": { "node": ">=14.0.0" diff --git a/server/main.js b/server/main.js index 8d3006b..22ada28 100644 --- a/server/main.js +++ b/server/main.js @@ -2,7 +2,7 @@ const express = require('express'); const path = require('path'); const app = express(); const bodyParser = require('body-parser'); -const GameManager = require('./modules/GameManager.js'); +const GameManager = require('./modules/GameManager.js'); const globals = require('./config/globals'); const ServerBootstrapper = require('./modules/ServerBootstrapper'); diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index c313dc3..a89b887 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -18,7 +18,7 @@ class GameManager { this.namespace = namespace; socket.on(globals.CLIENT_COMMANDS.FETCH_GAME_STATE, (accessCode, personId, ackFn) => { this.logger.trace('request for game state for accessCode ' + accessCode + ', person ' + personId); - handleRequestForGameState( + this.handleRequestForGameState( this.namespace, this.logger, this.activeGameRunner, @@ -100,18 +100,7 @@ class GameManager { let game = this.activeGameRunner.activeGames[accessCode]; if (game) { let person = game.people.find((person) => person.id === personId) - if (person && !person.out) { - this.logger.debug('game ' + accessCode + ': killing player ' + person.name); - if (person.userType !== globals.USER_TYPES.TEMPORARY_MODERATOR) { - person.userType = globals.USER_TYPES.KILLED_PLAYER; - } - person.out = true; - namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.KILL_PLAYER, person.id); - // temporary moderators will transfer their powers automatically to the first person they kill. - if (game.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { - transferModeratorPowers(game, person, namespace, this.logger); - } - } + this.killPlayer(game, person, namespace, this.logger); } }); @@ -140,7 +129,7 @@ class GameManager { if (!person) { person = game.spectators.find((spectator) => spectator.id === personId) } - transferModeratorPowers(game, person, namespace, this.logger); + this.transferModeratorPowers(game, person, namespace, this.logger); } }); @@ -226,6 +215,131 @@ class GameManager { return codeDigits.join(''); } + transferModeratorPowers = (game, person, namespace, logger) => { + if (person && (person.out || person.userType === globals.USER_TYPES.SPECTATOR)) { + logger.debug('game ' + game.accessCode + ': transferring mod powers to ' + person.name); + if (game.moderator === person) { + logger.debug('temp mod killed themselves'); + person.userType = globals.USER_TYPES.MODERATOR; + } else { + if (game.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + game.moderator.userType = globals.USER_TYPES.PLAYER; + } else if (game.moderator.gameRole) { // the current moderator was at one point a dealt-in player. + game.moderator.userType = globals.USER_TYPES.KILLED_PLAYER; // restore their state from before being made mod. + } else if (game.moderator.userType === globals.USER_TYPES.MODERATOR) { + game.moderator.userType = globals.USER_TYPES.SPECTATOR; + if (!game.spectators.includes(game.moderator)) { + game.spectators.push(game.moderator); + } + if (game.spectators.includes(person)) { + game.spectators.splice(game.spectators.indexOf(person), 1); + } + } + person.userType = globals.USER_TYPES.MODERATOR; + game.moderator = person; + } + + namespace.in(game.accessCode).emit(globals.EVENTS.SYNC_GAME_STATE); + } + } + + killPlayer = (game, person, namespace, logger) => { + if (person && !person.out) { + logger.debug('game ' + game.accessCode + ': killing player ' + person.name); + if (person.userType !== globals.USER_TYPES.TEMPORARY_MODERATOR) { + person.userType = globals.USER_TYPES.KILLED_PLAYER; + } + person.out = true; + namespace.in(game.accessCode).emit(globals.CLIENT_COMMANDS.KILL_PLAYER, person.id); + // temporary moderators will transfer their powers automatically to the first person they kill. + if (game.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + this.transferModeratorPowers(game, person, namespace, logger); + } + } + } + + /* Since clients are anonymous, we have to rely to some extent on a cookie to identify them. Socket ids + are unique to a client, but they are re-generated if a client disconnects and then reconnects. + Thus, to have the most resilient identification i.e. to let them refresh, navigate away and come back, + get disconnected and reconnect, etc. we should have a combination of the socket id and the cookie. + My philosophy is to make it exceptionally difficult for clients to _accidentally_ break their experience. + */ + handleRequestForGameState = (namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket) => { + const game = gameRunner.activeGames[accessCode]; + if (game) { + let matchingPerson = game.people.find((person) => person.cookie === personCookie); + if (!matchingPerson) { + matchingPerson = game.spectators.find((spectator) => spectator.cookie === personCookie); + } + if (game.moderator.cookie === personCookie) { + matchingPerson = game.moderator; + } + if (matchingPerson) { + if (matchingPerson.socketId === socket.id) { + logger.trace("matching person found with an established connection to the room: " + matchingPerson.name); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); + } else { + if (!this.roomContainsSocketOfMatchingPerson(namespace, matchingPerson, logger, accessCode)) { + logger.trace("matching person found with a new connection to the room: " + matchingPerson.name); + socket.join(accessCode); + matchingPerson.socketId = socket.id; + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); + } else { + logger.trace('this person is already associated with a socket connection'); + this.handleRequestFromNonMatchingPerson(game, socket, gameRunner, ackFn, logger); + } + } + } else { + this.handleRequestFromNonMatchingPerson(game, socket, gameRunner, ackFn, logger); + } + } else { + rejectClientRequestForGameState(ackFn); + logger.trace('the game ' + accessCode + ' was not found'); + } + } + + handleRequestFromNonMatchingPerson = (game, socket, gameRunner, ackFn, logger) => { + let personWithMatchingSocketId = findPersonWithMatchingSocketId(game.people, socket.id); + if (personWithMatchingSocketId) { + logger.trace("matching person found whose cookie got cleared after establishing a connection to the room: " + personWithMatchingSocketId.name); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, personWithMatchingSocketId, gameRunner, socket, logger)); + } else { + let unassignedPerson = game.moderator.assigned === false + ? game.moderator + : game.people.find((person) => person.assigned === false); + if (unassignedPerson) { + logger.trace("completely new person with a first connection to the room: " + unassignedPerson.name); + unassignedPerson.assigned = true; + unassignedPerson.socketId = socket.id; + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson, gameRunner, socket, logger)); + let isFull = isGameFull(game); + game.isFull = isFull; + socket.to(game.accessCode).emit( + globals.EVENTS.PLAYER_JOINED, + {name: unassignedPerson.name, userType: unassignedPerson.userType}, + isFull + ); + } else { // if the game is full, make them a spectator. + let spectator = new Person( + createRandomId(), + createRandomId(), + UsernameGenerator.generate(), + globals.USER_TYPES.SPECTATOR + ); + logger.trace("new spectator: " + spectator.name); + game.spectators.push(spectator); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, spectator, gameRunner, socket, logger)); + } + socket.join(game.accessCode); + } + } + + // starting with socket.io 4.x, the rooms object is a Map, and its values a Set. + roomContainsSocketOfMatchingPerson = (namespace, matchingPerson, logger, accessCode) => { + return namespace.adapter + && namespace.adapter.rooms.get(accessCode) + && namespace.adapter.rooms.get(accessCode).has(matchingPerson.socketId); + } } @@ -237,8 +351,7 @@ function initializeModerator(name, hasDedicatedModerator) { const userType = hasDedicatedModerator ? globals.USER_TYPES.MODERATOR : globals.USER_TYPES.TEMPORARY_MODERATOR; - let moderator = new Person(createRandomId(), createRandomId(), name, userType); - return moderator; + return new Person(createRandomId(), createRandomId(), name, userType);; } function initializePeopleForGame(uniqueCards, moderator) { @@ -299,133 +412,6 @@ function createRandomId () { return id; } -class Singleton { - constructor (logger, environment) { - if (!Singleton.instance) { - logger.log('CREATING SINGLETON GAME MANAGER'); - Singleton.instance = new GameManager(logger, environment); - } - } - - getInstance () { - return Singleton.instance; - } -} - -function transferModeratorPowers(game, person, namespace, logger) { - if (person && (person.out || person.userType === globals.USER_TYPES.SPECTATOR)) { - logger.debug('game ' + game.accessCode + ': transferring mod powers to ' + person.name); - if (game.moderator === person) { - logger.debug('temp mod killed themselves'); - person.userType = globals.USER_TYPES.MODERATOR; - } else { - if (game.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { - game.moderator.userType = globals.USER_TYPES.PLAYER; - } else if (game.moderator.gameRole) { // the current moderator was at one point a dealt-in player. - game.moderator.userType = globals.USER_TYPES.KILLED_PLAYER; // restore their state from before being made mod. - } else if (game.moderator.userType === globals.USER_TYPES.MODERATOR) { - game.moderator.userType = globals.USER_TYPES.SPECTATOR; - if (!game.spectators.includes(game.moderator)) { - game.spectators.push(game.moderator); - } - if (game.spectators.includes(person)) { - game.spectators.splice(game.spectators.indexOf(person), 1); - } - } - person.userType = globals.USER_TYPES.MODERATOR; - game.moderator = person; - } - - namespace.in(game.accessCode).emit(globals.EVENTS.SYNC_GAME_STATE); - } -} - -/* Since clients are anonymous, we have to rely to some extent on a cookie to identify them. Socket ids - are unique to a client, but they are re-generated if a client disconnects and then reconnects. - Thus, to have the most resilient identification i.e. to let them refresh, navigate away and come back, - get disconnected and reconnect, etc. we should have a combination of the socket id and the cookie. - My philosophy is to make it exceptionally difficult for clients to _accidentally_ break their experience. - */ -function handleRequestForGameState(namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket) { - const game = gameRunner.activeGames[accessCode]; - if (game) { - let matchingPerson = game.people.find((person) => person.cookie === personCookie); - if (!matchingPerson) { - matchingPerson = game.spectators.find((spectator) => spectator.cookie === personCookie); - } - if (game.moderator.cookie === personCookie) { - matchingPerson = game.moderator; - } - if (matchingPerson) { - if (matchingPerson.socketId === socket.id) { - logger.trace("matching person found with an established connection to the room: " + matchingPerson.name); - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); - } else { - if (!roomContainsSocketOfMatchingPerson(namespace, matchingPerson, logger, accessCode)) { - logger.trace("matching person found with a new connection to the room: " + matchingPerson.name); - socket.join(accessCode); - matchingPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); - } else { - logger.trace('this person is already associated with a socket connection'); - let alreadyConnectedSocket = namespace.connected[matchingPerson.socketId]; - if (alreadyConnectedSocket && alreadyConnectedSocket.leave) { - alreadyConnectedSocket.leave(accessCode); - logger.trace('kicked existing connection out of room ' + accessCode); - socket.join(accessCode); - matchingPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); - } - } - } - } else { - let personWithMatchingSocketId = findPersonWithMatchingSocketId(game.people, socket.id); - if (personWithMatchingSocketId) { - logger.trace("matching person found whose cookie got cleared after establishing a connection to the room: " + personWithMatchingSocketId.name); - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, personWithMatchingSocketId, gameRunner, socket, logger)); - } else { - let unassignedPerson = game.moderator.assigned === false - ? game.moderator - : game.people.find((person) => person.assigned === false); - if (unassignedPerson) { - logger.trace("completely new person with a first connection to the room: " + unassignedPerson.name); - unassignedPerson.assigned = true; - unassignedPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson, gameRunner, socket, logger)); - let isFull = isGameFull(game); - game.isFull = isFull; - socket.to(accessCode).emit( - globals.EVENTS.PLAYER_JOINED, - {name: unassignedPerson.name, userType: unassignedPerson.userType}, - isFull - ); - } else { // if the game is full, make them a spectator. - let spectator = new Person( - createRandomId(), - createRandomId(), - UsernameGenerator.generate(), - globals.USER_TYPES.SPECTATOR - ); - logger.trace("new spectator: " + spectator.name); - game.spectators.push(spectator); - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, spectator, gameRunner, socket, logger)); - } - socket.join(accessCode); - } - } - } else { - rejectClientRequestForGameState(ackFn); - logger.trace('the game ' + accessCode + ' was not found'); - } -} - -// in socket.io 2.x , the rooms property is an object. in 3.x and 4.x, it is a javascript Set. -function roomContainsSocketOfMatchingPerson(namespace, matchingPerson, logger, accessCode) { - return namespace.adapter - && namespace.adapter.rooms[accessCode] - && namespace.adapter.rooms[accessCode].sockets[matchingPerson.socketId]; -} - function rejectClientRequestForGameState(acknowledgementFunction) { return acknowledgementFunction(null); } @@ -476,4 +462,18 @@ function pruneStaleGames(activeGames, timerThreads, logger) { } } +class Singleton { + constructor (logger, environment) { + if (!Singleton.instance) { + logger.log('CREATING SINGLETON GAME MANAGER'); + Singleton.instance = new GameManager(logger, environment); + } + } + + getInstance () { + return Singleton.instance; + } +} + + module.exports = Singleton; diff --git a/spec/unit/server/modules/GameManager_Spec.js b/spec/unit/server/modules/GameManager_Spec.js new file mode 100644 index 0000000..d0a8b4a --- /dev/null +++ b/spec/unit/server/modules/GameManager_Spec.js @@ -0,0 +1,257 @@ +// TODO: clean up these deep relative paths? jsconfig.json is not working... +const Game = require("../../../../server/model/Game"); +const globals = require("../../../../server/config/globals"); +const USER_TYPES = globals.USER_TYPES; +const Person = require("../../../../server/model/Person"); +const GameManager = require('../../../../server/modules/GameManager.js'); +const GameStateCurator = require("../../../../server/modules/GameStateCurator"); +const logger = require('../../../../server/modules/Logger.js')(false); + +describe('GameManager', function () { + let gameManager, namespace; + + beforeAll(function () { + spyOn(logger, 'debug'); + spyOn(logger, 'error'); + gameManager = new GameManager(logger, globals.ENVIRONMENT.PRODUCTION).getInstance(); + let inObj = { emit: () => {} } + namespace = { in: () => { return inObj }}; + }); + + beforeEach(function () { + }); + + describe('#transferModerator', function () { + it('Should transfer successfully from a dedicated moderator to a killed player', () => { + let personToTransferTo = new Person("1", "123", "Joe", USER_TYPES.KILLED_PLAYER); + personToTransferTo.out = true; + let moderator = new Person("3", "789", "Jack", USER_TYPES.MODERATOR) + let game = new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ personToTransferTo, new Person("2", "456", "Jane", USER_TYPES.PLAYER)], + [], + false, + moderator + ); + gameManager.transferModeratorPowers(game, personToTransferTo, namespace, logger); + + + expect(game.moderator).toEqual(personToTransferTo); + expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR); + expect(moderator.userType).toEqual(USER_TYPES.SPECTATOR); + }); + + it('Should transfer successfully from a dedicated moderator to a spectator', () => { + let personToTransferTo = new Person("1", "123", "Joe", USER_TYPES.SPECTATOR); + let moderator = new Person("3", "789", "Jack", USER_TYPES.MODERATOR) + let game = new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ new Person("2", "456", "Jane", USER_TYPES.PLAYER)], + [], + false, + moderator + ); + game.spectators.push(personToTransferTo) + gameManager.transferModeratorPowers(game, personToTransferTo, namespace, logger); + + + expect(game.moderator).toEqual(personToTransferTo); + expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR); + expect(moderator.userType).toEqual(USER_TYPES.SPECTATOR); + }); + + it('Should transfer successfully from a temporary moderator to a killed player', () => { + let personToTransferTo = new Person("1", "123", "Joe", USER_TYPES.KILLED_PLAYER); + personToTransferTo.out = true; + let tempMod = new Person("3", "789", "Jack", USER_TYPES.TEMPORARY_MODERATOR) + let game = new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ personToTransferTo, tempMod, new Person("2", "456", "Jane", USER_TYPES.PLAYER)], + [], + false, + tempMod + ); + gameManager.transferModeratorPowers(game, personToTransferTo, namespace, logger); + + + expect(game.moderator).toEqual(personToTransferTo); + expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR); + expect(tempMod.userType).toEqual(USER_TYPES.PLAYER); + }); + + it('Should make the temporary moderator a dedicated moderator when they take themselves out of the game', () => { + let tempMod = new Person("3", "789", "Jack", USER_TYPES.TEMPORARY_MODERATOR); + let personToTransferTo = tempMod; + tempMod.out = true; + let game = new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ personToTransferTo, tempMod, new Person("2", "456", "Jane", USER_TYPES.PLAYER)], + [], + false, + tempMod + ); + gameManager.transferModeratorPowers(game, personToTransferTo, namespace, logger); + + + expect(game.moderator).toEqual(personToTransferTo); + expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR); + expect(tempMod.userType).toEqual(USER_TYPES.MODERATOR); + }); + }); + + describe('#killPlayer', function () { + it('Should mark a player as out and broadcast it, and should not transfer moderators if the moderator is a dedicated mod.', () => { + spyOn(namespace.in(), 'emit'); + spyOn(gameManager, 'transferModeratorPowers'); + let player = new Person("1", "123", "Joe", USER_TYPES.PLAYER); + let game = new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ player ], + [], + false, + new Person("2", "456", "Jane", USER_TYPES.MODERATOR) + ); + gameManager.killPlayer(game, player, namespace, logger); + + + expect(player.out).toEqual(true); + expect(player.userType).toEqual(USER_TYPES.KILLED_PLAYER); + expect(namespace.in().emit).toHaveBeenCalled(); + expect(gameManager.transferModeratorPowers).not.toHaveBeenCalled(); + }); + + it('Should mark a temporary moderator as out but preserve their user type, and call the transfer mod function', () => { + spyOn(namespace.in(), 'emit'); + spyOn(gameManager, 'transferModeratorPowers'); + let tempMod = new Person("1", "123", "Joe", USER_TYPES.TEMPORARY_MODERATOR); + let game = new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ tempMod ], + [], + false, + tempMod + ); + gameManager.killPlayer(game, tempMod, namespace, logger); + + + expect(tempMod.out).toEqual(true); + expect(tempMod.userType).toEqual(USER_TYPES.TEMPORARY_MODERATOR); + expect(namespace.in().emit).toHaveBeenCalled(); + expect(gameManager.transferModeratorPowers).toHaveBeenCalled(); + }); + }); + + describe('#handleRequestForGameState', function () { + it('should send the game state to a matching person with an active connection to the room', () => { + let player = new Person("1", "123", "Joe", USER_TYPES.PLAYER); + let socket = { id: "socket1"}; + spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson'); + player.socketId = "socket1"; + let gameRunner = { + activeGames: { + "abc": new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ player ], + [], + false, + new Person("2", "456", "Jane", USER_TYPES.MODERATOR) + ) + } + } + spyOn(namespace.in(), 'emit'); + gameManager.handleRequestForGameState( + namespace, + logger, + gameRunner, + "abc", + "123", + (arg) => {}, + socket + ); + + expect(GameStateCurator.getGameStateFromPerspectiveOfPerson) + .toHaveBeenCalledWith(gameRunner.activeGames["abc"], player, gameRunner, socket, logger); + }); + + it('should send the game state to a matching person who reset their connection', () => { + let player = new Person("1", "123", "Joe", USER_TYPES.PLAYER); + let socket = { id: "socket_222222", join: () => {}}; + spyOn(socket, 'join'); + spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson'); + spyOn(gameManager, 'roomContainsSocketOfMatchingPerson').and.callFake(() => { return false }); + player.socketId = "socket_111111"; + let gameRunner = { + activeGames: { + "abc": new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ player ], + [], + false, + new Person("2", "456", "Jane", USER_TYPES.MODERATOR) + ) + } + } + spyOn(namespace.in(), 'emit'); + gameManager.handleRequestForGameState( + namespace, + logger, + gameRunner, + "abc", + "123", + (arg) => {}, + socket + ); + + expect(GameStateCurator.getGameStateFromPerspectiveOfPerson) + .toHaveBeenCalledWith(gameRunner.activeGames["abc"], player, gameRunner, socket, logger); + expect(player.socketId).toEqual(socket.id); + expect(socket.join).toHaveBeenCalled(); + }); + + it('should seek to re-assign a socket connection should two connections match the same person', () => { + let player = new Person("1", "123", "Joe", USER_TYPES.PLAYER); + let socket = { id: "socket_222222", join: () => {}}; + let ackFn = () => {}; + spyOn(socket, 'join'); + spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson'); + spyOn(gameManager, 'handleRequestFromNonMatchingPerson'); + spyOn(gameManager, 'roomContainsSocketOfMatchingPerson').and.callFake(() => { return true }); + player.socketId = "socket_111111"; + let gameRunner = { + activeGames: { + "abc": new Game( + "abc", + globals.STATUS.IN_PROGRESS, + [ player ], + [], + false, + new Person("2", "456", "Jane", USER_TYPES.MODERATOR) + ) + } + } + spyOn(namespace.in(), 'emit'); + gameManager.handleRequestForGameState( + namespace, + logger, + gameRunner, + "abc", + "123", + ackFn, + socket + ); + + expect(GameStateCurator.getGameStateFromPerspectiveOfPerson).not.toHaveBeenCalled(); + expect(gameManager.handleRequestFromNonMatchingPerson).toHaveBeenCalledWith(gameRunner.activeGames["abc"], socket, gameRunner, ackFn, logger) + expect(player.socketId).not.toEqual(socket.id); + expect(socket.join).not.toHaveBeenCalled(); + }); + }); +}); From b932a06fe0c400eedb3f761ded705fac101b1a36 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Thu, 30 Dec 2021 02:20:50 -0500 Subject: [PATCH 49/60] simplify connection logic --- server/modules/GameManager.js | 27 +++----------- spec/unit/server/modules/GameManager_Spec.js | 39 -------------------- 2 files changed, 5 insertions(+), 61 deletions(-) diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index a89b887..f80d2e6 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -258,12 +258,7 @@ class GameManager { } } - /* Since clients are anonymous, we have to rely to some extent on a cookie to identify them. Socket ids - are unique to a client, but they are re-generated if a client disconnects and then reconnects. - Thus, to have the most resilient identification i.e. to let them refresh, navigate away and come back, - get disconnected and reconnect, etc. we should have a combination of the socket id and the cookie. - My philosophy is to make it exceptionally difficult for clients to _accidentally_ break their experience. - */ + handleRequestForGameState = (namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket) => { const game = gameRunner.activeGames[accessCode]; if (game) { @@ -279,15 +274,10 @@ class GameManager { logger.trace("matching person found with an established connection to the room: " + matchingPerson.name); ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } else { - if (!this.roomContainsSocketOfMatchingPerson(namespace, matchingPerson, logger, accessCode)) { - logger.trace("matching person found with a new connection to the room: " + matchingPerson.name); - socket.join(accessCode); - matchingPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); - } else { - logger.trace('this person is already associated with a socket connection'); - this.handleRequestFromNonMatchingPerson(game, socket, gameRunner, ackFn, logger); - } + logger.trace("matching person found with a new connection to the room: " + matchingPerson.name); + socket.join(accessCode); + matchingPerson.socketId = socket.id; + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } } else { this.handleRequestFromNonMatchingPerson(game, socket, gameRunner, ackFn, logger); @@ -334,13 +324,6 @@ class GameManager { } } - // starting with socket.io 4.x, the rooms object is a Map, and its values a Set. - roomContainsSocketOfMatchingPerson = (namespace, matchingPerson, logger, accessCode) => { - return namespace.adapter - && namespace.adapter.rooms.get(accessCode) - && namespace.adapter.rooms.get(accessCode).has(matchingPerson.socketId); - } - } function getRandomInt (max) { diff --git a/spec/unit/server/modules/GameManager_Spec.js b/spec/unit/server/modules/GameManager_Spec.js index d0a8b4a..28bd36e 100644 --- a/spec/unit/server/modules/GameManager_Spec.js +++ b/spec/unit/server/modules/GameManager_Spec.js @@ -185,7 +185,6 @@ describe('GameManager', function () { let socket = { id: "socket_222222", join: () => {}}; spyOn(socket, 'join'); spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson'); - spyOn(gameManager, 'roomContainsSocketOfMatchingPerson').and.callFake(() => { return false }); player.socketId = "socket_111111"; let gameRunner = { activeGames: { @@ -215,43 +214,5 @@ describe('GameManager', function () { expect(player.socketId).toEqual(socket.id); expect(socket.join).toHaveBeenCalled(); }); - - it('should seek to re-assign a socket connection should two connections match the same person', () => { - let player = new Person("1", "123", "Joe", USER_TYPES.PLAYER); - let socket = { id: "socket_222222", join: () => {}}; - let ackFn = () => {}; - spyOn(socket, 'join'); - spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson'); - spyOn(gameManager, 'handleRequestFromNonMatchingPerson'); - spyOn(gameManager, 'roomContainsSocketOfMatchingPerson').and.callFake(() => { return true }); - player.socketId = "socket_111111"; - let gameRunner = { - activeGames: { - "abc": new Game( - "abc", - globals.STATUS.IN_PROGRESS, - [ player ], - [], - false, - new Person("2", "456", "Jane", USER_TYPES.MODERATOR) - ) - } - } - spyOn(namespace.in(), 'emit'); - gameManager.handleRequestForGameState( - namespace, - logger, - gameRunner, - "abc", - "123", - ackFn, - socket - ); - - expect(GameStateCurator.getGameStateFromPerspectiveOfPerson).not.toHaveBeenCalled(); - expect(gameManager.handleRequestFromNonMatchingPerson).toHaveBeenCalledWith(gameRunner.activeGames["abc"], socket, gameRunner, ackFn, logger) - expect(player.socketId).not.toEqual(socket.id); - expect(socket.join).not.toHaveBeenCalled(); - }); }); }); From c51864659ad5cfe57989b6ab820eeb8cf0f3d26d Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Mon, 3 Jan 2022 15:51:48 -0500 Subject: [PATCH 50/60] styling tweaks --- client/src/config/defaultCards.js | 29 +++-- ...Double-BlindMinion.png => BlindMinion.png} | Bin .../roles/{KnowingMinion.png => Minion.png} | Bin client/src/images/roles/Sorcerer.png | Bin 1903 -> 0 bytes .../roles/{Shadow.png => Sorceress.png} | Bin .../roles/{Villager.png => Villager1.png} | Bin .../roles/{Villager-2.png => Villager2.png} | Bin client/src/modules/DeckStateManager.js | 3 +- client/src/modules/GameCreationStepManager.js | 116 ++++++++---------- client/src/modules/GameStateRenderer.js | 17 ++- client/src/modules/Templates.js | 40 +++++- client/src/scripts/create.js | 5 +- client/src/styles/GLOBAL.css | 8 ++ client/src/styles/create.css | 104 ++++++++++++++-- client/src/styles/game.css | 22 ++-- client/src/styles/modal.css | 4 +- client/src/views/create.html | 33 ++--- server/modules/ActiveGameRunner.js | 1 - server/modules/GameManager.js | 4 +- 19 files changed, 245 insertions(+), 141 deletions(-) rename client/src/images/roles/{Double-BlindMinion.png => BlindMinion.png} (100%) rename client/src/images/roles/{KnowingMinion.png => Minion.png} (100%) delete mode 100644 client/src/images/roles/Sorcerer.png rename client/src/images/roles/{Shadow.png => Sorceress.png} (100%) rename client/src/images/roles/{Villager.png => Villager1.png} (100%) rename client/src/images/roles/{Villager-2.png => Villager2.png} (100%) diff --git a/client/src/config/defaultCards.js b/client/src/config/defaultCards.js index 9e25dce..115b804 100644 --- a/client/src/config/defaultCards.js +++ b/client/src/config/defaultCards.js @@ -12,31 +12,36 @@ export const defaultCards = [ { role: "Dream Wolf", team: "evil", - description: "If a Werewolf dies, you become a Werewolf. You do not wake up with the Werewolves until this happens. You count for parity only after converting to a wolf.", + description: "You are a Werewolf, but you don't wake up with the other Werewolves until one of them dies.", }, { - role: "Knowing Minion", + role: "Sorceress", team: "evil", - description: "You are an evil villager - you know who the wolves are, and you want them to win.", + description: "Each night, learn if a chosen person is the Seer.", }, { - role: "Double-Blind Minion", + role: "Minion", team: "evil", - description: "You are an evil villager. You don't know who the wolves are, but you want them to win.", + description: "You are an evil Villager, and you know who the Werewolves are.", + }, + { + role: "Blind Minion", + team: "evil", + description: "You are an evil villager, but you don't know who the Werewolves are.", }, { role: "Seer", team: "good", - description: "During each night, choose one person. The moderator will tell you whether that player is a wolf.", + description: "Each night, learn if a chosen person is a Werewolf.", + }, + { + role: "Parity Hunter", + team: "good", + description: "You beat a werewolf in a 1v1 situation, winning the game for the village.", }, { role: "Hunter", team: "good", - description: "If you are alive with a wolf at the end of the game, you best the wolf, and the village wins.", - }, - { - role: "Mason", - team: "good", - description: "Masons know who the other masons are.", + description: "When you are eliminated, choose another player to go with you.", } ]; diff --git a/client/src/images/roles/Double-BlindMinion.png b/client/src/images/roles/BlindMinion.png similarity index 100% rename from client/src/images/roles/Double-BlindMinion.png rename to client/src/images/roles/BlindMinion.png diff --git a/client/src/images/roles/KnowingMinion.png b/client/src/images/roles/Minion.png similarity index 100% rename from client/src/images/roles/KnowingMinion.png rename to client/src/images/roles/Minion.png diff --git a/client/src/images/roles/Sorcerer.png b/client/src/images/roles/Sorcerer.png deleted file mode 100644 index a85b4da539bfca29fb5e7002acf530cba00aa08b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1903 zcmZ`(dr(tn7QbnzJme9BBwAP!SZLG%5?&&Yt003OMbvxiO%UYxX!4A8;xRf{m=#p$kPkxBqsl22E zDF9IVF#uFn0l+F|s?-9&XAA&%a{>UOZU6xJ^sU}cVlYQ;+Ww40?8KH`F)4C8$MKHe zURQuL8h*;lewSN};ePJ3dSLKsOw32FbYi^S2SC6s`khk7-0h=3N;$0pKVPap4){Pl zKKgx2BgtbM6IV^zx~2) z%HEgYqxicE3A@-AC#NLZYaCs-(ufyscyj>Y!{3q<;xay;Seib3eKLbkIpj>(>%gw` zvvW{cEMI94_O$V*hYS7bf$U!%k88$nB8!)T)S9mkbz*4mUDO}gyq|dT@3KKji=WUJ zb{EMSUCr-K6re=3{xa@+s?By)n1YO55YRsb79Qw+d3Y3}5y3AJ$Nr`g0tJnVYXx=ufTJ*7!C6n35~z+cOErI* zk077PLM~eY{Djwf+}rXsPsL!&&hLKqiH@cFrIbtnRe{myjop2JG0MSZ|%W0V& zR{lw-U*DX-LvDa3syCvV!eK#V<>&6E5xfoS?yvp7F*W2C?@C+5zYNA&?yZ$exyp4` zdR@*B6G(7nBqWw`%Hii*3z>EB6WB40u$JzLrSf#D~gZB^mS)>Nn>~?z~D^d|lUpKw`Ol%Vn+`wakGzr;^Lt6~jOAcbG%5yhZ&K z&!>H+ux*ddzqT0P&YBwk5L*%r%f3b+S*ZCRvYXI4MQ`u4r4!GL^t!SrBN8< z&Oa_>&ff1HAiU9ZT(vAVvSz+v1^Ad#Yv|^48q4m7IVfV>!xtbmmXm~movQAsk1@uS zi^w3%3w}WROV^$j=syir?O6}Eupqg7M|XvnSK-FDXfD)y@WAmC^}T)GnOYM#0x#lf ztp62P$bI$)zUZ0dsY;fNd=2?seF$a*tJ7NL4wZk^+ZJvvQ%)CTrlJ=XP%1I4SQ?|cPslW zUtOCe$(ekM)2_1e>)J8uvgtpgc`G4JG3woh%wJct+hUfBnprawv*~k6G8fgAKwz^2 zd@eyDJJP`$(_CF{)hIoE`=rv9MX|0H0zI*uhtM8vIy@P{=_j2r;Zu1J6tXgJPERog zP!3bP`eM3Pc_Vz&+)PPW#g-Xsfml)^BI&pv2vn8o8KU*l-=|_USfcVeKA%y!1f0 zzmz|*0d|&9!3UU>)Ozug-9nTx64)$piM5K@qJ<|7+@NnEqmX(|r@S9!9@c2erbA0t zZ7ehuts`vXO@ya<5Mge9YHb%J3pc;K&|2+DVJw@J@#%1xwzLefCMCXamQdya6UAKi48SjyQrV;!=5L`1pTqx^zC3c+-VRr^4`jG_L=a12nBxX4o3iqiC6krI} z`aHB1cO~%1a;$Yo5y8XGjjW+TGuU7oV0Zu9Fsl=-4 option.role === role); - let existingCard = this.deck.find((card) => card.role === role); - if (option && !existingCard) { + if (option) { option.quantity = 0; this.deck.push(option); this.customRoleOptions.splice(this.customRoleOptions.indexOf(option), 1); diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index a208c07..1a9f681 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -4,6 +4,7 @@ import { customCards } from "../config/customCards.js"; import { ModalManager } from "./ModalManager.js"; import {XHRUtility} from "./XHRUtility.js"; import {globals} from "../config/globals.js"; +import {templates} from "./Templates.js"; export class GameCreationStepManager { constructor(deckManager) { @@ -33,14 +34,14 @@ export class GameCreationStepManager { 2: { title: "Create your deck of cards:", forwardHandler: () => { - if (this.deckManager.getDeckSize() >= 5) { + if (this.deckManager.getDeckSize() >= 5 && this.deckManager.getDeckSize() <= 50) { this.currentGame.deck = deckManager.getCurrentDeck().filter((card) => card.quantity > 0) cancelCurrentToast(); this.removeStepElementsFromDOM(this.step); this.incrementStep(); this.renderStep("creation-step-container", this.step); } else { - toast("You must include enough cards for 5 players.", "error", true); + toast("You must have a deck for between 5 and 50 players", "error", true); } }, backHandler: this.defaultBackHandler @@ -191,26 +192,28 @@ function renderRoleSelectionStep(game, containerId, step, deckManager) { const stepContainer = document.createElement("div"); setAttributes(stepContainer, {'id': 'step-' + step, 'class': 'flex-row-container-left-align step'}); - let div = document.createElement("div"); - div.setAttribute("id", "deck-container"); - let deckContainer = document.createElement("div"); - deckContainer.setAttribute("id", "deck"); - - deckContainer = loadIncludedCards(deckManager, deckContainer); - - let deckLabel = document.createElement("label"); - deckLabel.setAttribute("for", "deck"); - deckLabel.innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; - div.prepend(deckLabel); - div.appendChild(deckContainer); - stepContainer.appendChild(div); - - let customForm = loadCustomRoles(deckManager); - - stepContainer.prepend(customForm); + stepContainer.innerHTML =templates.CREATE_GAME_CUSTOM_ROLES; + stepContainer.innerHTML += templates.CREATE_GAME_DECK; document.getElementById(containerId).appendChild(stepContainer); + let clickHandler = () => { + console.log("fired"); + let actions = document.getElementById("custom-role-actions"); + if (actions.style.display !== 'none') { + actions.style.display = 'none'; + } else { + actions.style.display = 'block'; + } + }; + + //document.getElementById("custom-role-hamburger").addEventListener("click", clickHandler); + + loadIncludedCards(deckManager); + + loadCustomRoles(deckManager); + + initializeRemainingEventListeners(deckManager); } @@ -349,61 +352,41 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) } // Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. -function loadIncludedCards(deckManager, deckContainer) { - +function loadIncludedCards(deckManager) { for (let i = 0; i < deckManager.getCurrentDeck().length; i ++) { // each dropdown should include every let card = deckManager.getCurrentDeck()[i]; let cardEl = constructCompactDeckBuilderElement(card, deckManager); - deckContainer.appendChild(cardEl); + if (card.team === globals.ALIGNMENT.GOOD) { + document.getElementById("deck-good").appendChild(cardEl); + } else { + document.getElementById("deck-evil").appendChild(cardEl); + } } - - return deckContainer; } /* Display a dropdown containing all the custom roles. Adding one will add it to the game deck and create a widget for it */ function loadCustomRoles(deckManager) { - let customContainer = document.createElement("div"); - customContainer.setAttribute("id", "custom-roles-container"); - - let formLabel = document.createElement("label"); - formLabel.innerText = 'Custom Roles'; - formLabel.setAttribute("for", "add-card-to-deck-form"); - - let createRoleButton = document.createElement("button"); - createRoleButton.setAttribute("id", "custom-role-btn"); - createRoleButton.classList.add('app-button'); - createRoleButton.innerText = '+ Create Custom Role'; - - let form = document.createElement("form"); - form.setAttribute("id", "add-card-to-deck-form"); - - let selectEl = document.createElement("select"); - selectEl.setAttribute("id", "deck-select"); - addOptionsToList(deckManager.getCurrentCustomRoleOptions(), selectEl); - - form.appendChild(selectEl); - - let submitBtn = document.createElement("input"); - submitBtn.setAttribute("type", "submit"); - submitBtn.setAttribute("value", "Include Role"); - submitBtn.addEventListener('click', (e) => { + let select = document.getElementById("deck-select"); + addOptionsToList(deckManager.getCurrentCustomRoleOptions(), document.getElementById("deck-select")); + document.getElementById("include-role").addEventListener('click', (e) => { e.preventDefault(); - if (selectEl.value && selectEl.value.length > 0) { - deckManager.addToDeck(selectEl.value); - let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(selectEl.value), deckManager); - toast('"' + selectEl.value + '" included.', 'success', true, true, 3); - updateCustomRoleOptionsList(deckManager, selectEl); - document.getElementById("deck").appendChild(cardEl); + if (select.value && select.value.length > 0) { + if (!deckManager.getCard(select.value)) { + deckManager.addToDeck(select.value); + let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(select.value), deckManager); + toast('"' + select.value + '" included.', 'success', true, true, 3); + if (deckManager.getCard(select.value).team === globals.ALIGNMENT.GOOD) { + document.getElementById("deck-good").appendChild(cardEl); + } else { + document.getElementById("deck-evil").appendChild(cardEl); + } + updateCustomRoleOptionsList(deckManager, select); + } else { + toast('"' + select.value + '" already included.', 'error', true, true, 3); + } } }) - form.appendChild(submitBtn); - - customContainer.appendChild(formLabel); - customContainer.appendChild(form); - customContainer.appendChild(createRoleButton); - - return customContainer; } function constructCompactDeckBuilderElement(card, deckManager) { @@ -437,7 +420,6 @@ function constructCompactDeckBuilderElement(card, deckManager) { cardContainer.querySelector('.compact-card-right').addEventListener('click', () => { deckManager.addCopyOfCard(card.role); cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; - document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; if (deckManager.getCard(card.role).quantity > 0) { document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.add('selected-card') } @@ -445,7 +427,6 @@ function constructCompactDeckBuilderElement(card, deckManager) { cardContainer.querySelector('.compact-card-left').addEventListener('click', () => { deckManager.removeCopyOfCard(card.role); cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; - document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; if (deckManager.getCard(card.role).quantity === 0) { document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.remove('selected-card') } @@ -459,7 +440,7 @@ function initializeRemainingEventListeners(deckManager) { let name = document.getElementById("role-name").value.trim(); let description = document.getElementById("role-description").value.trim(); let team = document.getElementById("role-alignment").value.toLowerCase().trim(); - if (!deckManager.getCustomRoleOption(name)) { // confirm there is no existing custom role with the same name + if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { // confirm there is no existing custom role with the same name if (name.length > 40) { toast('Your name is too long (max 40 characters).', "error", true); return; @@ -473,7 +454,7 @@ function initializeRemainingEventListeners(deckManager) { ModalManager.dispelModal("add-role-modal", "add-role-modal-background"); toast("Role Created", "success", true); } else { - toast("There is already a custom role with this name.", "error", true); + toast("There is already a role with this name", "error", true, true, 3); } } document.getElementById("custom-role-btn").addEventListener( @@ -494,6 +475,9 @@ function updateCustomRoleOptionsList(deckManager, selectEl) { function addOptionsToList(options, selectEl) { options.sort((a, b) => { + if (a.team !== b.team) { + return a.team === globals.ALIGNMENT.GOOD ? 1 : -1; + } return a.role.localeCompare(b.role); }); for (let i = 0; i < options.length; i ++) { diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 27709b9..15c18b4 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -389,8 +389,10 @@ function renderPlayerRole(gameState) { let name = document.querySelector('#role-name'); name.innerText = gameState.client.gameRole; if (gameState.client.alignment === globals.ALIGNMENT.GOOD) { + document.getElementById("game-role").classList.add('game-role-good'); name.classList.add('good'); } else { + document.getElementById("game-role").classList.add('game-role-evil'); name.classList.add('evil'); } name.setAttribute("title", gameState.client.gameRole); @@ -401,10 +403,17 @@ function renderPlayerRole(gameState) { '../images/tombstone.png' ); } else { - document.getElementById("role-image").setAttribute( - 'src', - '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' - ); + if (gameState.client.gameRole.toLowerCase() === 'villager') { + document.getElementById("role-image").setAttribute( + 'src', + '../images/roles/Villager' + Math.ceil(Math.random() * 2) + '.png' + ); + } else { + document.getElementById("role-image").setAttribute( + 'src', + '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' + ); + } } document.querySelector('#role-description').innerText = gameState.client.gameRoleDescription; diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index 3bbade3..2874ac4 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -78,7 +78,7 @@ export const templates = { "
    - +
    " + "
    " + - "

    " + + "

    " + "
    " + "
    " + "
    " + - "

    +

    " + + "

    +

    " + "
    "; cardContainer.querySelector('.card-role').innerText = card.role; @@ -419,6 +421,7 @@ function constructCompactDeckBuilderElement(card, deckManager) { cardContainer.querySelector('.compact-card-right').addEventListener('click', () => { deckManager.addCopyOfCard(card.role); + updateDeckStatus(deckManager); cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; if (deckManager.getCard(card.role).quantity > 0) { document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.add('selected-card') @@ -426,6 +429,7 @@ function constructCompactDeckBuilderElement(card, deckManager) { }); cardContainer.querySelector('.compact-card-left').addEventListener('click', () => { deckManager.removeCopyOfCard(card.role); + updateDeckStatus(deckManager); cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; if (deckManager.getCard(card.role).quantity === 0) { document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.remove('selected-card') @@ -490,6 +494,24 @@ function addOptionsToList(options, selectEl) { } } +function updateDeckStatus(deckManager) { + document.querySelectorAll('.deck-role').forEach((el) => el.remove()); + document.getElementById("deck-count").innerText = deckManager.getDeckSize() + " Players"; + for (let card of deckManager.getCurrentDeck()) { + if (card.quantity > 0) { + let roleEl = document.createElement("div"); + roleEl.classList.add('deck-role'); + if (card.team === globals.ALIGNMENT.GOOD) { + roleEl.classList.add(globals.ALIGNMENT.GOOD); + } else { + roleEl.classList.add(globals.ALIGNMENT.EVIL); + } + roleEl.innerText = card.quantity + 'x ' + card.role; + document.getElementById("deck-list").appendChild(roleEl); + } + } +} + function hasTimer(hours, minutes) { return (!isNaN(hours) || !isNaN(minutes)); } diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index 2874ac4..c3b69b3 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -153,7 +153,7 @@ export const templates = { "" + "
    " + "" + - "" + + "" + "
    ", GAME_PLAYER: "
    " + @@ -253,6 +253,12 @@ export const templates = { '' + '' + '' + + '
    ', + CREATE_GAME_DECK_STATUS: + '
    ' + + '
    0 Players
    ' + + '
    ' + + '
    ' + '
    ' } diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index ebf29c6..d8537b0 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -395,11 +395,11 @@ input { } .good-players, #deck-good { - background-color: #1c1a36; + background-color: #26244a; } .evil-players, #deck-evil { - background-color: #361a1a; + background-color: #4a2424; } .evil, .compact-card.evil .card-role { diff --git a/client/src/styles/create.css b/client/src/styles/create.css index 5dbd0a1..67aa635 100644 --- a/client/src/styles/create.css +++ b/client/src/styles/create.css @@ -4,7 +4,7 @@ cursor: pointer; position: relative; margin: 0.3em; - background-color: #393a40; + background-color: #191920; color: gray; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4); border-radius: 3px; @@ -83,8 +83,16 @@ width: fit-content; } -#custom-roles-container { - margin: 1em 0; +.deck-role { + border-radius: 3px; + margin: 0.25em 0; + padding: 0 5px; + font-size: 18px; +} + +#custom-roles-container, #deck-status-container { + color: #d7d7d7; + margin: 1em 0.5em; background-color: #191920; padding: 10px; border-radius: 3px; @@ -92,6 +100,28 @@ position: relative; } +#deck-status-container { + min-width: 15em; + height: 13em; + overflow-y: auto; + position: relative; +} + +#deck-count { + font-size: 30px; + position: sticky; + top: 0; + left: 5px; + background-color: #333243; + width: fit-content; + padding: 0 5px; + border-radius: 3px; +} + +#deck-list { + margin-top: 0.5em; +} + #custom-role-hamburger .hamburger-inner, #custom-role-hamburger .hamburger-inner::before, #custom-role-hamburger .hamburger-inner::after { background-color: whitesmoke; width: 28px; @@ -360,13 +390,17 @@ input[type="number"] { padding: 10px 20px; } +#step-forward-button, #step-back-button { + background-color: #66666657 !important; +} + #step-forward-button, #create-game { right: 15%; } #step-back-button { left: 15%; - background-color: #762323 !important; + background-color: #762323; } #step-1 div { diff --git a/client/src/styles/game.css b/client/src/styles/game.css index f69fad6..f53a7ae 100644 --- a/client/src/styles/game.css +++ b/client/src/styles/game.css @@ -512,7 +512,6 @@ label[for='moderator'] { .kill-player-button, .reveal-role-button { font-family: 'signika-negative', sans-serif !important; - padding: 5px; border-radius: 3px; color: whitesmoke; font-size: 16px; @@ -529,7 +528,6 @@ label[for='moderator'] { .placeholder-button { font-family: 'signika-negative', sans-serif !important; - padding: 5px; display: flex; justify-content: center; border-radius: 3px; @@ -538,7 +536,7 @@ label[for='moderator'] { font-size: 16px; border: 2px solid transparent; text-shadow: 0 3px 4px rgb(0 0 0 / 55%); - margin: 5px 0 5px 25px; + margin: 5px 0 5px 35px; width: 103px; } @@ -613,7 +611,7 @@ label[for='moderator'] { #game-player-list { overflow-y: auto; overflow-x: hidden; - padding: 0; + padding: 0 10px 10px 0; max-height: 37em; } From c0a18a69210f1d581694c6d68aaac8b065caa8ea Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Thu, 6 Jan 2022 11:43:36 -0500 Subject: [PATCH 54/60] fix logger reference error --- README.md | 2 ++ client/src/images/screenshots/moderator.PNG | Bin 0 -> 85569 bytes client/src/images/screenshots/player.PNG | Bin 0 -> 63302 bytes server/main.js | 4 ++-- server/modules/ActiveGameRunner.js | 2 +- server/modules/GameManager.js | 2 +- server/modules/Logger.js | 2 +- server/modules/ServerBootstrapper.js | 10 +++++----- 8 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 client/src/images/screenshots/moderator.PNG create mode 100644 client/src/images/screenshots/player.PNG diff --git a/README.md b/README.md index e9b1b7a..b4153a4 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ smoothly when you don't have a deck, or when you and your friends are together v
    Ultimate Werewolf and by 2020's quarantine. The app is free to use and anonymous. +![player](./client/src/images/screenshots/player.PNG) + ## Features This is meant to facilitate a game through a shared game state and various utilities - not to control diff --git a/client/src/images/screenshots/moderator.PNG b/client/src/images/screenshots/moderator.PNG new file mode 100644 index 0000000000000000000000000000000000000000..aca32b05677551e7809f3fc1b61cd469184e91aa GIT binary patch literal 85569 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 literal 0 HcmV?d00001 diff --git a/client/src/images/screenshots/player.PNG b/client/src/images/screenshots/player.PNG new file mode 100644 index 0000000000000000000000000000000000000000..79d25c722d99b1030accb2d11460f9fd971a9241 GIT binary patch literal 63302 zcmeFZcT`hb*EbqPB_dKpL{LEFC<4+!I-v+rkPcF$s7ROIOA=IiJu1BgL^^_W2qhQ< zl+ddXT0m+{=!Bk|sONdkbKdWc`+o1Z_x^M59)rPVkG0pHYtFf5`OUeA(9r^)r(vT3 z008Ij-@B^^08n88fRiJ@Gvp_{yX(|B#<#Fc@pPVND zeb)7!i3b2c*Gl<0(cx0?3;@`{+`p@2;A`ORQzUXW|ZpH#p<73nSjPhB7Y zxF#Y`Mw&S=n0%&^le&AN&G%2k*S@X-;gLbY?we<%aG*u=%IXpZpcE2B9xI&PT51{2H6GXWRpg{5}eqIVM z8R&l9&2axe@z5e{ZF%rgUyT4~UnVHs^`bLVt>&oB?~n7W%LL1c1o2PYF;6@3>sk6K zuB*T9TEN2W?0?<5THX2m4yO{3`q%9mW3cOg_hFfe6o)QFX5`D&x{(g6MLR?DcS`;? z>jUt=J}{Ad{?|J#UX@kNP)>u@tiNZy|F;PS z`~UR;NgJwP1O9iG_}^#7|7{}+J?7pwoc%>Apomjh>ep>h+|b!`j#|H;>m2=Uk^dI% z|0f?_>w6}U#SV@ej8$Du-CmBb+>~OUZVu8@FZn5Om7(dJ%QS^?Cw0c(9u(H?N;#$4SVFstats0;sJr9Jg??(Tts-TbyCUc8 zItri4RC}Cgt4q|**X)Q|-fdsrsi4SZ<}ziopK#c3pEEFPgu6FfDYN33P|A;t8%WjN zaTifp>_gPnKQG7bJ{hlaO}^1sDJ?q0*>lClq`iHh;#L0A+D17CnnF;P=ud)%3nV-Z zf>&~iOPR|Cd5KPpAE$eA#T`=!(p72Tu4xN(eMFm%@dFvnuNWcJKJWOEU0#FPw?9mY zx+3f_zcZ$fBPn^u0jEhrHvpqXI3+4yZK!C!30ATGjd4~i6B$ut5m&c#@x8IphpYL;KtYZ9W z0@A6-N_?$_m5WSHys>2B$kq9dJFl_YYJ2?79iPm<|V4C0p$PKy(f zt&gy2w@ZYND^;4Kqd%Ho|447F*q|LbpSD`_Q)ofz6bl4&wGLVPsEb|}wiG&E@l+R1 zy7AzVt=#zY7>H-UooSJyA&yS=lzSqRqV}C{WtWx;pYkd2|3C})c}SAK2q+N#Nw|>> zkVSOW1RSRAo~)pnzsp0t7UQFzXFGO6+I=W(RHK79h%jlr<IHrhTasw1(ctTDA08%UzmIDSd!l@MT?{Q>z*HZs;_CE=CuL2x+%sI&# z+Y>@jf*?4r- zHMQG6mZyWMr>Ya2P z|2;QZXhWxdD#{oClNQaBTg&t4EqnmKeU-QTDH%HJNk)`o`g^+Np?>b*&BbVnB7FO$ zAls)J1J!`Y!#Qx3K?BFh3|j2+Fca!kS?s$%Pi$A=vi!Y12CtR!1vhXQE*u~Joh=oa zK}qynjd{h*3{&CCwQ*Uy`0|S-=Jdn&LtUGtuTV5Hh7x2k1eNg2%&P#)mKjjRMy;Xe z3ss{oUA%IIA9m*SX?yf=nI(L@%AOmodiU;^QH1-HsIsp%MK9UT{KY497=y(iSFjQM z%a?nMFJ(1&d3oRX0zGu6A7M`tM?cU}M-U-`FD?;=Xa=U~7MQ*Vf(9&p&>B#pf^<<8T zt;eLg%;BE)*4~S@nt5X5p#o%o6uu<7ifPq`$%Dg2h19Nm3`%l zBt>4XbflEMRpI^K>`T)dm7m9yrlUjUXwnwNFm)~b<(XjJoI>3pU$v@T-ksNG6=rM6 zw;cX_=w7eqqj*YUpqjgpQ6hOfF?HVS-N(KDp{&|t|L1;ztyk(Tqr6Ykvv@^%hr}Z^ zmr<=T4Dt>`xBS;WQQP;VOPA_=klXAM+d>N=`@VmDJCAFo3e#(M;qdqXPwP*p5Q}D9 z|MO1YdB}OS1^Hf{IEF?% zE6aU}j1;x?#*M0k__Cn5Z_Ne)hlHwg{{;K%1svC^nn8JJ?C0t=S-Io{qf-S}Rq%2v zBDC)c8U$X{X)!s}=--|m%Um(RxfX&o?}Tq3yeWKP?KfHebx3VQGI)H!Ru3cJ`WYFf zi!l`7{U%ltRe^6qJlt%9V(#krDkMpa56%)IK~DulM07(J-R9cAGW5uSTRUReHIN6B zs}nic)(#zHNLj;3*~+Ny8Mk8}PtPiEkTu1h6{P%ocO^953_Pg6z&#;T%D8!Bp(1Q5 z-=)nP#BOQ%uB3Zat8ei%|28g5neQShmzD-T9=vi#V&ru1lDf!KzX=V2!cwO+@{Wiv z5JG~geJcbcF6+dt6zQa2ReRFd{i1Z}R;PLMu~o`blbaG(W-S&j48OVVM?!N?phce+ zCKz9;EEO0Q!+bS(gpnKhZ2r(eQB!gq+Mqua`s8A5rC3N0FvmY4vBoecHL^!4+!C@q zTsk^`zezqsMj`R>qxR8I+>ZXGe{3$JuMExKx%YLS6XybYaieJ?%n=A7cn-Tm%6MsG z5M)%{bU)li+Q7I?*_V^1d(DjHE*CoNGS1&P$!H^W?Q4drIa!c5(G^_lLy4`M+?iOD zx;>OF`cX{uO9unQ` zMZ*IG%a-Cj|0`|KQLg=oC`LRx+&_2?*L8H{8N1WfE2zs+WD%n5YyZOZ%#+?iOad)d zK3ez3!%a5hM9FNMy?q4RZLARB?##pN@vE$Hmdw`t`KKl)&Eq-Muya4quNH}nXffU& z4hI{NdiwodgvL!RynD0vVXQ3SmdEGDclraAAj#n-g&x2&`4d8xH&p7z34;}-7>oO- zLH;8dPFlK)u01}=q&Euk9{W-^r#;_(psUj}ynyPSnHag#jcH)8kgVYt zzQUv$lgBoQd=*+NsVrp)cjC2uaG!Y9NwRe>)Q(0E-TsGuiA_LKn#@;5mtM!F7zTm~ ztj#3<-6J=^qGPLDGMwc(FKOL&CV}rP_WZl(?GaoB`YhI~EH?KR`h1^U0FYt&i1IK(CE7Mq<`-9URv-!x5IhtN}eOAf1P=M+vf*X zHfOUN)0yH8ois~5ARFwp@BZU%yR?vlw~Xz32sb(Y!%oBdKy5M9$5E~c+TQg5gx(-_ z#D4K=&m6C53BU9kcC~I|gwvgBuNeO;`e^x=m8>XCsRKW!$V0YXaJHc;P{@%_1~=8z z0Bp<9Xv2A7c$rh)#QxJ5aiRjIY|U7pdz&h$x{e-v`$D#Mn_ii?PPdHu58I~h1yy8z zEW`}{_*B2eP)hJFZ8|nJXDUt6ayBDPW~BbHfxG5#_@)2^%3OJbPH`&NjuEmn40fd( zK&*B7#+%i~@X(DT@FoX4_-f1&C2$}5n@sqD*iu{r?47ocVBc-zgJ}-qrzSm@249Ai zlx?fajnndd;8XVPOV7OCLYKW@Rf5)_$8w?DHmEXgeYUEP?{AKO?fRrdP*)eKI=%5K?@~)6i+{Qu^ZP&+-=Y07IR}B09+@Ke!_17uC7#kv z3~^bvcjGCn1H`w;ibbW)1vb?cY~DZeU!!awD7C|5KSIrrS_y`G1RtHhUjq(E;6TMN zI(2&>F~Pc<8SP%P;|x2G>^|414Q4>$wM&rtkS4RHMmeSe_ft~SSu=K2X#I_C>GY$4R?2M|yK%#Pcu`1GZcYf$)bRO8%t9XQ z3p>=_NMW;0c+El=P%=5rqFvO?&iTf z?T5iq>iijzkwGIhU)pggSS6?1V(`(pb;tIS*5$E|Ae?_>%wl|p9*!xLtFGuV~UwK?6FK_>` zIxbO_k+I)8I{ek!b)XcrE63?1No$Q|jl#mC`NdAS5Ul6W?-2<0_k@ca+z;C?2f%XB zNhGz?8e6zeyi&oN-D79;{Y?|o5fix{ATkl)GtwNo^Ew@)mp8kcTT*s6vAq!>q2r0& z&AHzG5hLKQo45{xrfDj8a>o63BSx>Dq$;ev(s)?4hY6}>Q2RuUBAJZ z2Pu3c1&$L$JbW;_e=byQHUEww7V7J@` z+Jzb8#NN&J?F5tI0v5u#zfl@A`_?5ooN7qxur>}rmQ6Axvi*7eZvevoedzfAA4%rz{|t5i ze}Q$k_vv8C&UJGPn#p@y0L6qEg^+_w=5T#h_6rTV#H~+5l^wzW0DQJN?BJUD^riB&-td6I^R-r|t^$hai6V&R{OiIe=>S?S zkk_SgCs!$^IN6h@8vro8KT0$RCOo?gXyNpPT=b=cGKw^VnZi^6gBt|LEy$lfo;)Z3 z;5ZAH(;^GRLdB2v`rq^T7@w>K zR^~^>2p1e-dZi87^5|51LTHE*2!$_?5KK=109fC) z?hJddYlkojyPm{jKO*h4H4Mbwz_6+4TgVHZBHe4d>d=M7pZzuJN&Z{e(><^79f0Tl55V(5g3Iz6^hk|STACvNa~ zQGP^#jEYZLg2lpmvb|h3fu17FaOU~MP0zKvsaxMUq}$8|L5Noi^dFz|-x%03JXqc} zsq7ll_w+nC$#!YZq(`5XtMNcj=Jp7?#T(~GnZi!z@gQdcSgH-r%4WsyVcqfe-e{Mx zdfJqNdcFZG)($FY?joOQlD4$_Rp0XvKfkm5eW5v@NAukTK5V(JXl@s3dS`61XG0-s zqejxVc@=C=WO{42Pz+vPV(Si|QJ^@s|GId8p!Sz!WydMzC*7Me-@(Li1|Rj%p1D1= z{mus~`<;cHr(<~Uz1oZnn<{+ z-F0=`%`6^m39c7wANa`Fy0l-P_8sbkf3TvW|ByY=YrOQV(SZW*3Is+ikR}@vI+>~> z8yiVHnw%H!x@|t7gi{PPp@r?NN3OgqDOBlCn(?pj=DE_(RNSsp4>Wc zTw)s){WL+s&L@Iq_@y)Tx+ZS#Ks?WzyO;~Ql;+W68{n614&GgNI!nDHA8%#yaKTo0 zYKKzLkjiXD<5wGMG-vcFdOl3nb8<*BbP=II)A)*ka5>>Tb|=W#K|a%Z(fyl(x?{UZ z=f}(3eE2-psnj2Qz7ya+fiC^-tDX&+RzHtT-4{nU zk#38lS=Mo_ThU%;04hLYC8ab_;Eqse17Z{4Z%Y4zHMG;AYfsQIyklYJargA;&|p_T zCRChDI(NSQ75yTgFNM?jHNE9Ar@~^41}>o#qo%(CYGMV4r~riT9?iEq7YGc)(v*!cKXB?B(SY&53+4{r1^u< zPG0j2s3RJ(1uS}1s!a}>gHo0MHn*?&RP}Ujf0(OR)8=|i;$x@8YV!m0RDP+>un< zzZcM^oEc(XP5*COwB`ns%#T=w4IJMRS&hGd8rJ-#<@_$C)VkGPqBinYd2=`9D zyp1EFGr;X1VE4sRA3#4xt+0~ofrR!u(v1?q^ zCNSa|(PSY|0BVX5*1dp87xevg(m4`mj@#@lym@_4XyP}zCq)&`smRF8N8)kq&sIi_ zx}KT3C4-8&t7c^;{fqm>7ileh4QE0wWV%5=aQyqzju^FJ>MvN zYmRUCEqT?F`u%AFjL{GA4skspjcIFgXGME=&6t366)1 zDAMjhb=A|9Hbm`G98C0>Q)azg6!Eo&UD>Y2POTS7M8wF|OynsjB6WU> z4-x>K78@4%B6LPPE}9B%H&VhX;S|YWQ4P&<=^;hY#E~9^aGC^e2<^`vaEi}Ub%nr} zzC0r3<=B$4ErpKWYBe9Y_;2QDY{huX4|yWU0S_C&!xQpak{oeZ>texIT0}Xsoi;R{ z&>4;3d>K*vRx|o8{r2n4M{cdR@%xdYYFTZzoAHvP#%b`euz(+yHMjh<`{FyJd#0OX zj&H3TJw^*|o_mqI@2Z zz%mC|zStfqx14wgV#^hA9JZF(SVTJ36yIjt*CwSUE*>RaLhkTq96U72qdA&4GClrN zDlwhZ*DnMeO4>WV&{Y2*zJ3)}wf)TG3A+QIIUEH!Pn1S9f4PES!dAqyqE0hkjhe1H z!xOv~*^n{P$TKBYmKPh4^Dbv)cr%67^3>H_FMXE2JJ6~h%e8j*3!iWxC97k82W-0r zwC5WLrs^LBqgeUNm_XGYO2b`ugJ?ucsljB*2@DGBwIp!a^CNnvi9=4)H$+fs5o%Y3 z1g4HG<_nKvfF6Rx-{Q;zQ?x90UpMby@eAA>2kTbQLw5W2lC>7aH6YJJ1?Km%PUga|xOI1bcuC>EN72mR5FzDW1 zOAv?AXE3aeMX+lxjT#xK;?k-$63fN`&$Uz~VUp3)xfN;)8V61FFWP$_sq=4SzFRJx z4(S)4=UPA5+-)4lACKzwUav5$TD#PH#0sBJ6>y2@b*KMOIef19{V!VpP>=}oUfb;I z&3vcNqZznaHz#X%3^4bjG&v2ovZHLBj>b4%k~YMgy2W1ZmUMIANp{X+$THVlKp z##D6`kt(O>jaFokcSA#S&BP^6-|t|A8q{MCbzJ?+GeW?d8d^Vy@tJ+>Rednt~U5avaUzjAMZW6A6ax4 zVgm$~=hARm+i0o!4BHcOwj77Zyb?*Oq`%XDvHrQZJn@bY|J-Gd7!$Swcx~`n7uar+ zdSTILe8VJD;JtaADU{g4^E**!S-en44CfU;h%oD9cDM13j((+GD4W9_Y*a0(yxYdP z97Ru3sg3foN;1n6V;Pc|h2;xrk(6B)Ec0R>2@Q@wka|zby*!EF{e=?Xm@Vo4^hHus zY%|ei9AO@hIpEJYoOKbM;FQ{`EQnJnzY)BtAg5Bt`mJ}(du0BNU9aV?{|>C5WjfLQ z$Uj%P&JHQn%*|M6hjLI+9S`FgO*k~t+imw~kfYO0z~f5gcwKgp#Iy71{3|SI zE!fAHO9(Ydm#hN|x$79eS)%qB)in?}jTmrPFhN?TpMJxgBbHZG(|sXZvxMb|%jnz) zqYp=hxhntjPfnsAq*|;)Dyi)v9Q~wA#gJd58b`?CN8eeoCg1l)yh9@OCyQAOfYasi z?iLd&2lQ8SnK;?Aj~aaXJ#xFR-wInl-En{Xj)nlgi@HDA2DT;*SnR2WAGAN8H&;O# zLm9a`8+N=JfP}^$f8ZhQM<#TQ*1Y&P#5+>zjyLOT!&dTtZwQ^J5OS3`twOoZb!R$W z?9r*Aj$@S&%Ls;pw%)4kdJ^m4kkc_ZY7=%Td$?qfHvLAYz%f09*qOk2+v zoOBC)AmdW0~n^f0|f!)Nr_? z2xM*J0eV9eWx%@NvMl6B={h~yL;Qt$W=jL+!2YE z6^om1GHR;h`X=Y!y}fl|b#>OMv19f!OQ8_c-I?zU=AVe#td$$#&s9P)&nt|k$M$sf zLvi7-ud*{q9kdfQtQY1r?+YCe+qeCStyj>Y^tJXj+;!6`qbxuNn~+5o5mbw+VW2d? zSM^s!c_Ql)xo)pu9pb8?cq|a6rRxo61n?*vTbecneLVfHtZQjRRgbwB6S zBxcnuA*%v6VRmyPq&|1XsSbrLnCDtDBP!;#izAT4*==@cnM!po<0;8XQCH0TQMfdwO+oapFua%ch z9r3dtt+B{_B!c2ATMBHyzJUMOwwuCm!<2w!v}8*c&!FIm;@+BY-rVcu#J}^adEMPf zb|Af+!aE7JCN{BEN$EG8LT@`tJsi!)sLfC2m570RK5U;;tK)aNI3$-ic)c+|L|x{n z(7Y?epHJAtgslF07o(aPpAvR9{#f1Y_*fBD{3W|3W?-1QgDy0m}rh4<)dn zNJ>0DmvPRp2(TwfZISMfbF%r{rq&!O3)S7ev^!giSL%Pb7y`dLbHtJ%Yo7tN=<(cg zfxVmTjcD?mA?ABXDz9A=KV{!0_^kN4d{8ykxL=`u+&+xn(Fc#iX%^8jaqN zuIcw-?3q3SbzVZp^k)4a)(jC(biT*wmyq+B+5O`}af)h_O;YOMD*j#i1?*57gCA84 zTqe$aV|U$1?kM2qWYzfPPu0{=4|POGcoB^WXGvvrOWG(P$=|$^b;Ah*ywRF5Y?oTw zc36S^aetijba?8s2FL+&nrotw4%1-!D!~2?VIjcUBZiQa{7Gjdek(F`Z6N>sl^2+O z)z1$$D_#yL*!nbFavW75Cl)_Z6F@;Vx#{GsY_PWZk&DcAa2PC`sv)M|kX6ez3UGKf zOkel77}o!yR*H6`2GrLkbgT*{9)(>}usbytvPZh`>0?RDGAn{y0G+mc$1UV8dkiiH ziDRxE#q_X?qL#ZVoY`#8VrxOiAgitVU{b$9IjQJ8mO%7ce>4^1$~>O*rO#Oqd2PzY zquwojewZpD8>fHBjM5Hl@i{H|!?eSP-cnV1P`$yXzC>6e)#kRWB9ED*BL7#9p~v6=@xQ3;gWA}~?<$@d3K01k=?dqtE@Sm`}o9rYC zLh2>BlTH+H{K~`0U1pb*eZAbnR9Wlf1$Kp;qFlVLBAz)4Zi(-QF=a2z=S*K8%D9#Jm+)K}MbFYnqo z8M%XJYokeB>FkP;sq-p7plc!IdJ)aSc2JTSZossBIjt71fmWe!zK~A z(3PS>FSiCz{O}n`I`qjpI4H1aKPyZYwp&=6yGjPInELyWtM1OQWj8o_UB-vqkP~&9 z(Nl6GX7KI|RCVg}4;N2@IWcEYy-7^wTpdH{_+xV-z0Bs0wal6=^t*}QwxadYemuFD z_Ec#Ch@GNdCog)z?iugsC3>@mo%iZ+XUxF%lLpKd68fdXF=JIeXhHKfmAX%tUu?U3 z8b_5y!SCy<`wj;_8!IlgoW2rtFzROdS#m&(X1|0!?=^iK9Hfjme;>vc*KKk?hG21X{tD4@(8@BKnH`cF3Fi@C;kg`IZ$% z`|DdhogajF#ZIWA!VR!ih$5!BN(hhYyN5=NN7) zuSO*;e+dH(zOL;CdqjNXU;Qk&V_t_Yc#C#>Y|)rRwEplYM~};_s=x8oK|wt!ci81f z8Qk5G!ajDunc9Ztzlsv@iafeKwq9JYNp{F{r0;W+6-K1AW6vd0bVusC*RIQz1G)WA zNc$Y%jG6Y11KlcFgi53N2IfGbV7QTBaN1ypHfOHW)q9-qMHhOG$uas zFd|f-G8zN+#@=pyA{g8RBf`PL%^ybI_C=?F@5a~9|wwm`?+h<{e{W_ z?Lkf-mv?QOU5tBv#df0sSDo7s7vi!9#ZER5N|!RbaHD$=_u)tTtLOPQ(HFLFH84FT zjmwr*VpQ4N*VB(5(|<4)*XCGkoG`M-oS38AH3TJacE0-+%0fTuyLr7hm33zpNj*7z zf19jK7RZODO@4(WRGUJX`L1d$5X(Ghm%e+h-!*Q3Ap$>NuwI+A zwDeFcc$x>baYE>%6u-nDkJiKwhB@>A$6Gm!jg94#AN%;-(DH5pme^b@;e zP_S6Iq#=RROxgVG=6`rxUdgk(7HjBf$*cgS>-?PqK*=qfGocI?ROY)8vCUp7-mqs) z=logk8sC=lOsOeKtHaklpjOxoiM_fn+2l7-qH+rJmRh}dbJdfO0~?H`vT1pUM((n( ze}jw_cpO-pB+;f^Z*&JFVLkgv{e-vk50q;6UN_&`Pw@UxW6?@jrbhT4+xm-pFlYaFbk`kIrQrB$la?C|HJ2RX|xDbQ9t=Z4eW&VGGNhEb1XU zpBz|?7M|rmj(4YivZJ4#aoT%~0XWgm-$a(dO3k<7hb$iND1OIp*GRBw7a} zof4{jQS^5{Esz+d3<6LkvgM(vE$QvuIp41bU6JG&OrR=|qrgJl3BhX|H4KF5YZh(` zcDB}K0j1fE@RM{iw|`^yU^0;Je0(084a8RWOfPpiZ>DbJlcDE_$)$+PTyj+#b2leLBf^vZp{3=*9?G}99hM0TBL_%^mrzts#mV({(O-ocA~zX_mxKkoQlfk6 zpp|!&*gBouFsqm~@GF`Hk$Z++$tBgGR5h|Gv<7y>dji(T&Ha=z&#$+q$;|FqqAY>@ z$w*sZ*%CXYF|p;ZBHw=!z=k^Q0|b-L9ZdhPKJfQ4;xGO*FgTy0bZHbgBNS9wQ2ZA@ z|DVoHAwmCl^ubA9Lmg!unmIWABAH;aIsj&b{*s~=9rphH`_=m9HJ`;202CGvlUYng^;0C3u95-x^xxHwqc0I$W~Zy^ zMnTH5ah6$}(E0~SLJj8o1lp#f+1qSG*(&ufv_=M>Fm(H`JIQQL-gqRR1lxNdcy;=I zoSDmzzJ)n+_KF4@A~g2MHA3*{=t73Ei{R!{kXrpGaa>4~=Pa7@2mTUtcjFYkixuw$ zk#WZ~HhC@#v93a@QWlabw1*SvRBZcGeR}%nlYTfQt`ZAt?Ykkb%&g^cYmyYMk+I4D zsCa?3&S6DAN4RDr1OyBEhPuS*_!Rom%ZnOtW(`-+vFG*Y>2qKY-q>D7Dt17LA>vER0P5_LRg*gQE*Ie_56W)9bz=Q48*b3Nq-SY!W7i+IR+%l8W|n7g(H zb`74H-XM{P{kC?!+YL5?O^1Zjh-YUI8axm6A3l9I@$j_F_Uh-)xBX|sWuIB>&RC}g z#F%J52c>R(#(6GKdw`OJ`Y@ZF^k3)QR%KtGS!mV(B_G(>-swQk=i*zN@Gf?PCfll? zZpe^I?TEopn})J?^^Wv|entgNci|pwM`ZS59jGUclCV& zV&$zsbPqQ?8Wh8frt!xU!Nr`=3@uB~+Jmn4w6fi!QU{6x4w6e06vqEc`_nI=%#{S! zZ5R~|b!4?+brPZDHBPu}A^ClRs!_7+&V!e3K|fNp~1wb&XZb; z!{Gtzbcp)sKYyH`ip9LCca6Jo>q^gaXYKWMwV>hQvhkyqG?r*5D5KKOI`ksb9F(41 z9=19Rh|Ha2*%_odd)A`%5k3s_32#5$EMM(7s&&S?7gB96mJSUtZW>Kmo^6QJvoX7R zBl$po_gvg`hK^EGRj+2_G!;i~D_=P2NX1m|h4%sFXw{H7Lg6$;f;4}V87s}|v^I;u zjBL8q8KoGg$-goZkzD!vQ~tl zwMF1g2tXE}`(Lji*wqK1nbkl{$!WQs#qR$Nn8w#QM)lOubUlJ~Ul5)*Iy?I;S$}p8 z28I{>3ALfx-YEbN-=M;GNiI8@^jhy@XH-i6{NZ%#yL&tN41g-8w=O@+QbrS3$)v+BaAuz64#;bDh7XUm=lFR z@zY*=kv$I?Gka*kKxux?|BXd}+|_}L!W~as=UBPFm>=(1nLat|zJhq_bMLukQ?0R@ z0bDrG-y`d7PD5O?21~cj?)DX*uN#f*E@dn3)U9AMrM->(GW7Du1c!@CHf^;E%OD0` z@3QN?93^SLzV>kFU0bVXA2Vw5L`@ZgAjVmXezC?BgY!G=zoK2g?{`w`wv{94aIc_K%6)`UO$vVoLl`-0N=pOrdAX(@Kt)6DEWk{el$j2O? zisAhsNo#!bHuT=7)rOBha}_T$WxhN{tI9N4E1YM3QEiP*yvV%Gm!XIM_RTc3zd~7R zb#z<-S6)1CM$Orq5il(7&uGJ0uI;tB|E3hKjh1vv3o&xH-_YR>e1s9e5Xz^aah%?e z7oXs**BM(LRXf~xC4mexi$y=Gjf%;*?*esl-Xxyv#2IZI-FfM2X|i*@WBXth9jY&^;faeE{LykE2j|1EcugS=MWPm@9Z`@kv^?5MV+L7 zFauwKTt%A4xLW3Ez!wHljuUWp(s%DTYG8p;m?rKu2S^*a&V~6P_Kf1u@Gu3t8tsKl?8?55y9#EoKe`?roiUt~Heg z;&gLiP`XlX`g2T@o8R$rvwaE>8x^f39!;n7%Byva-$cPG{y?7!J$=E`U zhk||`PNd?Mclwidj&QA%{@c{QEPx&`E}}em4Ma}hWYmi~-TTCqr_UI*G;KY}r1fk} zhm9-mDieWK5{gBN==n*X6Ckpa>c5;Zzj537^^xCnlJ;TGLl)9^#O`IOXC4r~a4@)x z$QN8pR1SHnDxi<{1J(4eXmr@ChKguTu%X6Os3&{x*}_ZQU}ryV7eY30pFcO)5p4+- zGAvOR`C-(o9UsP@k>q^OwCHp~aOsc*fWG9{hQNp(WP(QP=K!mW<*b#eX3k^E@!_AF zUlpj0Y?r5@Y=LO-XZ?n)eDe89)yus_Z?2X8&RoB*GL*i|WemlWyK6rUuwC8b2~)5- z6`&IF+JnyPaF89P-G7F0N3OMuQI4s5&E-5fejhf=92Id^L7uJc?+UP?dAsc3LAH9X zSL&gL7S%akjzpHzD}<#H@1-dw3&Q$IwcO4Djt*FHmKjygbn1AF@R&s{Rrr(d=A_=j$g-cO~WJ(qvn+>iN| zS*sfjVHKl!mlH6^&QISjy?vci7(Us_Ist&e+JikS*^<@f4}h%ni5J+>tE7~*7b$km$FwX7_Hr*?y~cd-g#t#mR>(e!biGxxxX~C}sMw9i z!bgN1Kg5x`ed}e19x4PE%P=v)RFM2SlBrCB8Tp6Q{!C%k5;Uc#5}LL~`d;r%$6@Dc zm*c{5G_JlxBu2s1Vz6g-`h(aA!X?$`324&sjI9^>tYv+fKJsqJ$sJ6;6CyBhmC^-hvDC+vA>;i+)m+B54%}Mym~xbs(oK4xn-%J6 za?D7~scJln9r>7#%BR6z8QT0RuPha&&#c32(c@*(dV8?CU#6}3I+xk-Td8M^c;1?c zA3tz8?tGHe*32!2850~+YIr#2tJBQ(;ajxvG!+hN;3tUo+6efvy$Ov!4|7L?%TiWf zTu5qq)syDb=;_J3T@~!&19aiauM?8_&S3~bTIognTc^lvSH8eDTDX#*y2-ZK1{hlizZrGOq&7lYoj$h+XAL|Wv7D<~2)K9d>qNQFOX4t_X}c))V?gHy?K zppn-fbS4j0Erc;Q%=WkFw;qSe25q{zLssNxoi3WErHq%2*E(9-_&K^7d>(M`|giqwtu) zIsJi=OmNuez9m&m!OU|JW){bb%zgXyCKrn^mjG7<6ciHP$ap5j@oQ+#^DYOzBA=&# zk=xU_Z9~@9<6%>@wk-7hIrAZFqXvD=4>-%jOJlr4)@)-DdGhe-f!Zzx4yMfl*+baT z&F+uEu7a(Yf-DD!xvAZEua#szHq@|4C*U|^>?U?LjDN|-5~Qew3GKu3veJcq3&>S!?q&7T?I7;X-{C*ljhls;Ja5i_gf7x+K`*ibazV3Oi5ad%y5CM>OuWmcN^pZQYL|B zER3i@Rp6?V1xqP`d=`!1NN!pF{+^-dNiX|WG&sbqMW0JAM3pZ5D&6F$8$9Qb3<&sW z<`QZrw`tv=fhH+jfi2!d1BXpr+X)WGy5W9y`by5XM6PtpP50(AfrBQ*i=NOk{w^9H z=XVk=wS4eeHbxs4Zkr{&o8p`@!qh+}Kc%SD?QyL8A1||__QZ5Xv&BkPLj34vaoWWj zJqSTcUQJ05XO|c_C${>_(tK=73sEn`_5Qsu3h z&dwIAxYdfCdYtHJZFkoC)3G1*Vs_)7X(QK5v=dVJYSN@*D(Xx7?sl@JC`H6*B%Ti9 zZ{tT$H{BSG@fmCv zD*J_?g^I;?j@cLd+78%_4*reI~l@BDUmx1x;41e4V|0^+0_pinK|Z!PDI*a zs92LX=ycl5xEi^Y>yYFY(${av2C4pz)9ula&0F?)vRQ?Hc3Sh48u`p#dID*1lb52F zD{U>vKOf^^&LY_Pyh+tLf5nZut7GY7>dq1IJ=y%m32S|Sv^Re~t*#rzUoY-gARoPn zr0(s^oiJ%%_jTv&xH#_GE-%_^X;ARm*_j?`IcEu4HHATqYRqh%cu3zz8|4C`a)0cs zmsVu1Mu7f`j?%B9^z+K{mFZeLMh2^f7@SyZbvjMswN*<2<_|)`cKT})#O#(nj#c54 z%S#@6aCBQHgE6tm)_1zHw)$i)CP=_Hx&aou3wKPB&hmJEVcf=o2KhgJe3YWYM%V+f zbU_RRu+~}Z)d$jjZr^@1ZCDw#G0-HjI5`7se7>KhB*oUUDAES7#d6 zaU3q1FyFK=^RjzW*BPdPysqP+*1zB?Y8m1{i8Uau~XY_}y-O_Osvjd%v~(leN6(ysqo4<2a8~r3&9Zv~rKw zdXCpuFW%lU{wj{|wRmj0`=|8U>ymN?iXbntUpavSX!&awi>8*}ElfEKJG1v)(z4<8 z?4l*_aA=k7ipCJF#6{XYV+#pQ`up@b?F>J?{m9%;^@tR|H15Bkd~NWQhBv^=0_ncA z{#X4`q@TF#d{jYznQDqSRS!YkVpK`0hi9C*^YvaL)p7CzfyBUCIa{r_hpM$(!P4!PCqfxa>yAMpb-=t` z^M2r-=E>xah2?oeA@-=D+7{2`dstgIKs_89*=1jLJmJddzdi*>q5N{@rT}C&s8H(| zA;BlQy+6us*shSwjN)8Yg(NUzMoboSo(8oQhFfZ1E$5s zQ+q5l@aYWlI!nS!Spj_%&Sm5T#Uu)~?m+JGzC}??FYA491(#Ihoj_ENqQ?x~8aHp6 z>`z|{gs_-<(yy2d+ROf7Kt7C+Euw^y%Ub4+m@&m)G7t4JAP?fdqw2yOkQn&c*?ry% zG009R#@yyeO^!+V@x&>LYIl29!4aIZJ5#TefeUD5EblvElHCa|n|k0l#Wqr`;eQI> z8|)-sZB)HobBy)}c=BP7MUpB#!l?ka-Z482 z?cC=_)or$dS;czg9HRz{D*>X{n?QWF$v^^We9gm@H=nShE9G<_4W1dmi)KU6c1zNN zq1_&ES~-IxIFaSRlj8K~>1^fe$8nn^{aF2Q1N;}nL_Om|P>IM6VoIyix{k>?1KxKU zXIbNj%D6w(54q*s)MKdiQ3tmOlv6ZPE$P>R93+}!a>)NTB=K(Ecuq}Z@+xwG~ zP$^Bb(+{P~`(*$~%{K(y?9ay}2q5c6@rC%at$Y8;0^$_C*)0orR7RqN zdHq^_`50HZpTE-lRYZ;MCG)=&9FrD(y_>FmO}}KSV?8D#Zs+^Po($6{>)24olpm9Z zq8}J>UYt}RGOH>xuW8*cJSaT;YHDPUvq2sdyBRtJIm|X{z*A}trJN1ICbb|7CK;yA zWs$OiL9wp||Mer%3b_RtA_{kcz24q;&4ja zXA@>Gh0a-100DiqmjINd$H3T%4SCWtO0EC(TQcE}B$fl3#s69wKSW*Km$JI@8vh2O z?BT)~`(_|N)%JK}Sy}({H_-H3(ULPN-Sg|>LP6cW65n&?vI9e0Uaq~O*kJgTh{RL$ zm&%)mN{|I|j&q!v{-pp8*V`|2DST~Xzi-@DRIG}+l~;YIr&sFGz>olr%rTBdPa0~Q zhdH$D2RzX?(+8jGG3LM-_;#JDmIFr*kO#ia+V%CN`$Z`0vBhKj9Ql8G)3#wQEMgHV zQlDo4SAOG~{cK#0dJ8}Ob+z1WZ91<7Z1bM-Qh1`HWnIf-qlW0*)vbmp73oYb6!c=qr_i1p{`mAJcAO zJ1qy#^c47hF8G$FpHQsZa!fvz`{Oz@&^-&$ zlpRBQbcL~0O6Os87KP)dcei`$KdE*bJ=q`}u{-TsN*lk9)b+dp+?ju(iY~~=gJmcp z@#8I!G*$;z1o40s1^6JXre{pUY>$U~d^XfMN|>P7r^Qjl>Tty`$h`A1)O4y#7iG#0yL@om7`E{fAjLBiL_ODRk==|?XUql!lU%w{fA@06ZiXeM{ z|G97zVdcHn-FTymb}~25pB&kI6oDWEG5qS}Pf-X9Bpxtw zDHr*A+@ujC1jPV@=kE}rr&z0H@Rw)-kz7GxPp0`t*fD?yJfvuA`LTh`Xi=Btu(|yN2u&DW^p_ z5fT^?{dUys?|@T*0gZut1#dJmtx$Ww(J7mCQqS0~x)GOLsS@cVB4?Nm8$rV*DDd(9rx zn#y^&)fPLHXqkW*ke!_D@`W)wNxq5y8F(+Isu3t4F0#Uy@=dC2Muu{Z7-0gxM>o%3 z|9;uOxdCLQr4%y_U5_vhi9g#dsAy-9_v)>s(zs*_%DhnJvQMzcs|LIx_f532#x@0K zkuu0K^#t7Qqa>+V;D0zjJDk-+BlxLq42L5i=zJc+{>)z{z4XW$F*Y`pW0c zR{E-uj;;b<%3itRVm)sf(G#bX{?U5t4^wXnykyTtbxjXiADezT^{Rf};gC2{#gzFy z{I&z-M9htw!$pb;hr0JV3v@KlFH(*Uz>D=UYEA8dg6!g7!gMV4B~2@TildtoA_Ls% zH=1nAos8__4BaNhYv6u0&nj_XN%Cq&y@K3kKpI0who|GJ0mXD6o-sc0kh|&Z06keb zXZagKIt6auukZ|FVfYGwf=RYOZpX-U%W9;~DyXj9=&(eokdd>%gZ^2~y?b6OkJCPk zbaX58ts&vD>TbHCw8>_O@SLa_R6>#VK#4N@hW&^jq|jW{?yGUV?|5JQ1hl%8k5d5A z-dyi-3mq=f4#&O-6dyEYBCh$U+ERzx#tl1ljS6te^H$gaz3l9J9i51RF6JNwvs*Fcmb>58Pv6`W%swBmhszeY-6lWLDZ zTjl*hINSY^cdtZivC%jM9J&r36%hxq<82kKDbe&$MOC{qU0Ozz4Ef#5_R+{w@YdB8 z*x#5A+o)T%m~8a9j+a@i#%`GZ&()72 zAp-aqWY3BZ9CSdaywZOo%P&OJrc;P}+K*9#>w42c^_{g01OIl{&jU zVYt-AYvWQpY3On$#U+`1fY2J#iv)%s}Exq2TR$?%yW? z1Vg#7|M6rrLS2~TH;~rv7=)LAaLZmZEK;Ato#QeN8DrIvu`*vB<_{*?Z)w&rIqUpR zF;mb5S{p8^C@g6qnR0Sjg5~L=$!W#p>4cs#Sns#USJ!1si4r?I;*DQ;OQQL=5{dks zcyi{!)~#)mJ+PS&muB^o7)D9E&ZtlENl8j53&{vF1~I$c??ro|9?HWB8qE*l!e&eJ5X|E9x87ngzrKFVZI+{(7IDDeEHmXxqQe@$`=z5s+x??X&N1+HOqVR{ zJvbJZmWJ74r4-zketeXlT5BVZwg3HX4!WR>rn+zh4}+$-6`y?T#<@i{oC%?bw0oCN^XK@G8^As~hdbSP| z0?D1R8FGhH?&Q+=^{Q&WLucQ$q5^hS)?pho0_U+d%5gFu$ydEEzYj29ggHV62549g zoGvZON~G&5JtSkSdwnf}T-PF~DoQvcET#b7r6gyOv@vN>P9tBtl?E5G^3Dcr>InH2 zfY6*mD7Atqul5tz1l2uDRh*xmK(Q`9Ik}khE&=@N1>@Fi@Wy8D+PJm{twQbY&*ja8SlZs^V^M*3zQDWm z-(_po{eaiC>D(6}AZK=qBbh^41eAz8Gy}VjQJIKT&EBSM9nlqBbQC#Bp0%qP+9ts? z`I+Cl4CCxT;7TE)BcWTw%-)ZhiB)6Qf-cTaP%Xp5c|LpXHzQY*9n149nvP7j&~_aW z)B+*1KFeT{)7yg0o>H>1mS)WQ%m6)OMB%7eIV(Ycp_<3qoJA@=A3}lgDWFZKc0cp( z6(AcfKRX=A31%H#zF&tzsr7v9jmngbMrKgp>1|6F0)EPJA{*9(P=3_~qi2^!T|k(A zAj4J7QHjn5NV2Tqie;U@c)(wS$m?6;imhaa0I}G9xXr%$-@o2W`QCUd-@?pj-A zAB45uZ0(g3!m+&zw5|AkS|0Rt-NMTos4A{{ioo@8X87jY{=uxaj<*IB`amj*Jl zwPnwQn)Z=qB2o_h)?(GzwU=uER_0&|8nCqE5v+d!jCjQ&p-Zc4c=nnKcxjzQbUfGf z-D&)CkY9PCd`mc@?`Kd7+w_1TrWAWou5^l6ZEe?s3&2WssnobJZuZ>SxsV}9<420Yi=W+Wmo;iB@jI&eIsz1 zIy%IXr)Rf$42}W1M^bcwXZu#GGk%*n9g*&fIX^sL@?#_W9B}@S3gdXiIxLy4j*d)U zO^kyPCotjF9?1OA;sLMBqYg=BA0`$WYoB<(&I8suix{_E2{?~}*r?mz)&|J*A!ka7 zK#r3r3TPA}0HHtWh?7J9azb0FvqsiSsRm=h7>4n>QxIi4Fhgz`bOxhse4v$jw}P!@ zF~JP}`y;f-F#@AZCh{if*f+OPer5%4{HR;OS)%bakuK)7zSY1WUY0Ri zdrwkUUPRbWt6f<-7g*oX9DhTWe0Lw~&SSk-mmhNJtaI@5w>Z0_cl7EZwoFY+z9$EC ztJg4`qlvd#Rb!G$r^)HxJ@e=ID*6`tULu6DbUH*BOp0cuG8r0PuIXAn>eB4`6V}tWB!h!6><JCR3Z3$z;zsbMxx zv;uYBI@WUmquePdy=Oxr$z=+#x-7BC9kT7{vC+OM1^d&r%bYUtw#Ofy5<$2m9$`hP zCZS(F?dTzrHg#&LrT}6Izx{U_O(@6n?Dt*IyN+RuJJEFulZj84NCGlR7S(U!>tsw~ z@-Z(h-N$%X*CR5~m5NtgarF_9@@yGt)dbM!$Ez4cV$H#gup&NUq62!aVDAf=oWyG^?6&7Sc8DP9ET?dk$WYHmF6G-T8M=B6Lt7Xldu&KC@E91F zTSEsib@2nSTk~D7(0CE`pEUqjB zRquYQva-0kY(&~HLH#+*Mw3F`Cmz(@W)0e$tZVV_8=W3I7N2YE`~2-D2bVkd{<@3? z5^em*?gqypwf&ow7)IY-&$=X3cvbo;TcKY~R(yhl@f&D*aer6*piI+ah1%9<{#ayL zbpYPecOhET=~-$)X+f?>0=jX#Q)U$q6dgs98v)JsD+(Z^Y+XV2oS--Ur%BFDn<2W- zwTBYYPmTGfeF?UR{`avecvt0@6)L}I)bvHM2n_D*xs!FRX{{hBWTb>%J3HUf0@I6R z&CL<9J`~MKc#cN$!~tba$zr$|v=_ad(+%9+Y-g~5HAq{t`&|WXY`OcV(o0ghfuwn! z{kaEUz=g09B@<}CGjlulx(aW~cAwca$^AH314+jEHH;KzKiiR%W}2cmkoo&)=uqNH zghKd?Kfg@iHc=Rm+9knF!h3JEib7|&H{;K@+9K`7KVycw;~v2G+dcxJ@f8(tVm5$k z2heLJD7^Q_m5De{X#mML2Y+)US1Kov-p9X6fWGX!Wb)U>`f7lPS9)dQSyI;yh8cmL8M$DHEd&CHL`+jSwJeS(Y@=O3f<|w;~L{k}q)=+;dC>Ej3%a zVsbLCZ9t?3i4xBXJ2+|*4m!W=`kGn^hwe%QVf-OS z%>j6GQHO<~&6%bY3*S}iYgg^>Qd2REA+P_s|3|DL1(v_WeLTO(e-j9K@RlMSC};u9 z3}D>+NnK17z1_krc6=S1Ma*bgecC?}K1_V;nE033*M-1ol$qVr+qHo}ZY=ml zJe-RCa071_EQ%dxQB1&KyX#09Dt*x3mw>PSF|Ms@gg>HlcWm(Rr)^E-F;j6wmbylV zSAZK5^0m%LHB&t4`;vvtB>WCa5f-%qf0H5_yW}Waf!d7lrJ0?bSVvbu1jP(EWJ@MK zxme+dVj^}$!PDoT*{eU1N5&Dv{6W}ah8<#uRDytP8`I%8F~-rUyRbR`#QpDEVkt3< zNB5lwtp|~RxghiO^0`Nfm9w1`jSF?OLqHhwif`lOvTUh`WgGPtw=4n8X}yy+a#@Xv z#+?WfjJUY?yjL^ry$4>|?#umZvjK-`|9ot!c8uh-2vGA9u(Pu#C5Ng!L5nc5^Jj4Z zBHR=g%X@j{MTG3q5DH*!>KBOhpKxJIiU}r;i9_k_x;KT9HtU?5(YTn@@Dg&c@J{C` zDv+ao|I zQM8IyT`_sY%>IM7IxT%y@6oo3$D@k$5+~t~6=m0d#lmd^&?Zti4v_iIjA8Wac9gq0 zug6gyVCyhjeiI&vWAC^u7oB(Be+*5(IH)6^YkrBpvO!-P3#b)JD%Ads!Tsy=&rYIl z$os!0nLRyqP7f@?DHx+l6q@MtzsDOMAA#e|jfSMdF=hJ8V_%ZWZ z(v)7h|1SG&ZPi7r0NaI0{k+{5nIT9zVi~@k1DTmp7;jy z+T$X{a2b_nUm_F$3kX*ITg0Tnr~&N_P@_5&dE_ey7_(_)NhF3zJiX}zTwXi**|cC6zj&;Uwg7>>U_b^RrK@}7 z^SN8$!{95G7zVYG0__6Iwl}&tbM;irBPue?so+`{c{6fzS^&E&0R}Hn{vY~sz*4w) z?Z#sjjVSG3>js_#9jY8L*#YU84}tCf$LPW#Uf2)H(j#Gd7tK~bD7Uu(RsKDXfzN1Z z$V1rU4FiR)-1Jcd>Pp1oRfH~qs*oxANC6^%M$GBSsk zrwtI5cLhp=_XwuHJ)sv%D$13@E6kPco$Vz#o}BaZI=+YbnU)hm9{-Hg|LY)tzU9t+ z`vB#ai$gBLto_eUlS>N5f!LACur9ibfL>G^c;5EKil zUE|`^{@-^9C~TSR+lstmONuNvbp!^~nmyL#m3DK4`ns7io4OkjEzYF!#&m-)U7kF7Eg9qMI}3GelM3i3$Q-mYq z_uD(~cKb%xk3DMm0!_cRN!v!4_paS!b-RQ4HcQa}ErCOZ@BW%z?=60ya7c>z8;!aPtg;}v zbfnWqI)88z477Rt^Fm#aP3VU{U3=A|^5u7au#FCt&4W$hPJF?xB?+!nBZy#z4l>f{ zcdvuTqQ`qspIO54K|rl-BmK7gYsJ9?YY*sL?d|m9^ytUtrf9zF#wwdSm3>p5mSI~# z{*~$H`^Yrjpb6jW;EW+%&On?Ex;}^@$i$3TKMMF>fVIT`kgy53c%Z)?ft%=|5osZP5l3wfD-EW)N zS$O*ZI)AaZzj*}7f|PY?+ZW%yZ=Y1lj)fPB7J*r5cl$}9)Yg=m?4@TsPQ4~rJ1fcZ zI4tI2`5NZsEJBkQ^}S)ZQW@ZZB=F4tbpLitcThdbg0R}M1ziN9%BnHx{lm8O&Y7ED zGa47;lA7iCZ|SVYSqXP zwE1o@&a~zRrp8*~Ek!@eC)IrMJG+hxzFRNULYDzYR|vz3B_17cu--i^(0Bdt9Na(Z zSPIOBt6@l|4k7l*wP?`9E1A_g4tsx^b+G|gg!IJ8b{7O=Uv!oOe;2G zZjEmEyvtrUc^`mZEJT5Jq~|C{QN=r!%ja?v2k|jyKfkmiaPw6ZflFxy`kx)7(k!4? zaD0dNFX3TbD74nUd=IFbLtT8*ywGIj6hFk+cLk^uj^XvMk~tUD zXj;X8?0Pmzw=4y0VUa^u%LN$r#9c}Pwp+>uZY*a&&>rz({_g07NmyLh4O%8_6-D#u zYN*HbOYajLj#l8Q<&T!=18G;d5LAh~@Alw_9h!rDZOi6lUZ=FDQQ<9uDd#>;pY{l4 zWzF^Y;4i1YRyn{6P9DS{&oN@0m%66RWOHmQP`1e!5@++n7O+5KjXx?*JTN` zGVuYV{(qbZP^qEK|-c{IMW+mvj;+94`Urng@pX>s61IidO@H*?=ai}X#EpG^YEQ!0UXlie$aAZPhZ`q`hS7u19Gpj~HeS$C)Y3kgD-gP7! zK226caORAw_C~L6Ib=Nc=vA6H2wwv9oB`<*!PS#^xvQAJel6G<720+MD}l6WSQ=}5 zz_s=25(>2yzbJaVrvzfJ{^ea(fp$g~FnPl?)EMoVQW+v*uhyrKb8q@&XUnb*PI_u; zqy@~_+W=&mR}8I+da6$j`qdwaxoaNR`E?u~K! z5Ym{8;JoACG|B(tB$=iw3E^+GJnbo|AE@TGDbIze8>~*aO=d~0ptoMN zlsPeLgKH44EK3l65fOG!2E`Yt{-5_1=bJAW4oaOzOS(A5%Boyo2@~TiekIAERSp-qT-?E>Z zg!N8bHVv|s2rYyVeC7KW6Y_tX`B$O)9O@$tzkxrDtszO>)UXL! z^hjt{Uy4ORcHHvnXogG1T~!*`qb=oIX}vJk4qWML3Ipu zQxmTK=hmQZ*;gwLfAnXK$GgI%nsNa1s{%_ zSTQt47XG3+2HgoWboyQpyy=v4+?K-)yYy1F-4&#*0rX@EfXrn$l$juHv_o;_8$|oj z01nI(q~&vLL3e(77jn#%_urRn4z!DNQ6kRyjd^^vuS=HQTdmOfx7kn_N})=l)V{&$ z1fg0-Ks6L~nC*p-(hl<~&uR%gya_%%w-ka*-KpF7c+Y*cx3$I&8=?9WtJbFb(^16L z?m?Xsx`*NTBt`Z@%^9z}ZTTVqFDk7r`XXX(?aRfmi&-6zFvt+?r*74Hrv2Qm9FV;* z$EWKfna{U~efP(V?At+XQKYL=5i;KEoSgpkKjU{vs{~iiOC$!f=|9-5R>xFtydRpB z_$CR!jxLI|ped7Gj*Ae=T6a~qLb9yAPuQY&!N-z59@Di9lU+dFwSR!S^tI&=cKXW+ zM6@9wk25+Om|OS&dQ=7Xzw(Yr#6D~d{UCC?Mht^3{$mN}g!-Rm;SAMV)X?Y`4wyQ);TF<6Bf%s^^qu?@r~R9FTT*8E-btbf0r zvxpraYhkT9J6X?o5uj=`Er-6f#|v1)!|o<0dI|yiz!S0??be zuPYbYe5_(7`zw}R@c>oad~6aK#j&dF=g!Urkt!2wy&$F@oHUDS=_+KYvd2j>jE-Gs zhsL+uP0@7{btfbMh64VkqW*d!P?eTmU21vrLpOq{f%(U~(@8_=>%XqGE=!}ez7#kJ2hlxrZtEe%T=-l0zU4p~ zUQBrrr%k-$IfH0VSeG@A2bN4Nm7oe7P>G@Fy@HRyA>2dAClfZ*0C2~2FBVtKB5LRd z+MK-;ZXXaX-M+ddf^f)rRK4EQbv)?4p~M?ftN zMgy4{RBwt-YmZEDEfnWT&uGXmb1ZAr^xvGV)vRHLt^`^VX8gv=jDQ}~W}s}ea|1$9 zv*CVdf5ekAuAHjpo(WS+W1(%;*xsFyl$w#yvYR2>@d>wi(d)4kGL^24@#^z;{K8F7twlP31mKAJpJ!(G=@s7@+@N~n>q30_<;*a@nf?MA&+xA& zFTMLpv9Nu^d+>~9=A5yvKy5Q~#PEkSuO~>tH06{l(8-*h@Et6#d48`YWIxbM+aaZ_ zM9Iu>NO}$CvtKAsIjucJFB{=`y342UfuXNI+O@Ppz_1 zf7uAwu2o1fm%&Z{#2Iwai{zkkBI;-Bg$^bReR^0MJhfDg)rDi5#sup^OS@=TPFq8- z>&7xk|CD(UB9bXQq-pMbCAA>0>T`YPRnsQl{X+R*qS1Uc!k%zDIWQnG%au?SD|>E? zJOt=Yj)%neerIX`kI%wB%G{B$CubY?0~pas^sw+`%~-PCCW(F0lLSw{)^FOwrHUMh zYVPEwAlPF5L)ifzaeSjAB2UZhO>D^$6(i_zvD>F&JX*GvIP5B-5{H&k(gjJY@om*} zl11JtJm|w}Q2u4BOwTuxm%&?i#yQFQ8cQE^+c}=AMA{*ZorY!2><~VFfGyjZU3A8F z>l*#kaYmV#c3m9gGr<{|N94DgFYeCtPEkc9vHhUt97z}OmLzRrWl7y$_#t(;Ncyig zRs0B~txZxeAZqadJn7XSa>@z67ZL;nu=uz*MRM8Ghj0kmaAPEeq8}AF>$lk??v??MuIM7f$6VD0f`% zME~K+{9?;sxd8{2xb1BoZYecoThT$KfzK;yH3Jb(_Fd@3QUmX_ZphB$gdTe4Zuwm~ zKR#lZ+}XcJ`9e0kOTVEEvUPW|x2tQvPC*RShc34IvSHSQghyiM5#4;mN+2UVUl~&< z%8zLzAdUTb!b3D@9^p#`^!2*Y981&&6GJZl(_oupUqTr!@jSiLCMxd< zLubrmoOflKu3`Y-zj|FV-TRck_(4J_vP8X!jqZ_7U)zfSGy?L+Q~;!H@wO)$i15kc z&b34okN%15U7dm?5iVs4_Q`bbg{M8|>FZD1Bt23<5tu58+XNUQ2~13~ znP`)h#Ylr@NlFDjaF~DMSfAs&8kk9lR`%O8x`Z4@YH=^r9~~*-qZg*1%iaN0Uruoj za8Xwm6Olszu`N3Q+W0L9FIAR$I94S8aNps!#z?Yo=B$d4hY1yvLNFa5=$OLdf4sBk zhPO@kQE7H&1vbCY_tPb9q8%%m44(&O0^Fy|A|aw$S=*6@gAp`_gOfPo=!SG=Q~8CP z++NDW1gifHCG?K(e#)`G-d~Xovkv$w{0zRlmGzucZFz2rvho z$Rnfz%$qe2NUKBrFXB>p0sWJ^Wit<_NOI^`jw*m$U?^i(B7eEwo&tq-j+!Pj2qSD)W-RjJ?|mQDEp1Z=?dAwZMH+bdRx%aoj%w$ zzFj_fR#f-{&&&W``!tj{-AuiPQA$oZsO4ulc8hVzGj@o67M!gvOOLPfP&-XYvxHvR zmeRz})_(KO(5LsvC?Rfsm2B&R{PM;gT98xKGnJ|NKeGipCPnFyH|o>%E6h5#zOzX2 zz&=YM%F~GJ6}%JUnc~wE6I&)oUrK~_(J*ZM3q~GKb^z_c_pQx4s^ey+VjpB>_$MG5B{Z7*C);w?6IX&o!%UjFDK;6yT+Vl1&$0=Iw6wBKnAd<7t{)#MziZhW zsPdR=lB$QCG5DJDN~#UG8@Jf9M+dszkDlzbyb}(AGAk}vCTmU%kWzNlXYL(9MP_NJ zJ7F1R%F5-NT3{(9=LLw1?HV9OH+a@eB=d)OZ;{DzeNCw69ax=_99MA!T5 zj>CP0W2;#1?AeR+Lw#doSF0C@^DMl$2{}1Qqk$Ny?qR=1A|;Ow3h5b%Mv?%2Ng7n`jYzififz*2pZE7 zE*9oEFlSCDTnv6sVettNu>Mz8W5UvC5b5nOc~>FiN9Rs%HW0KkNSl{eb*h((boToC zXYG1d{HtxUr}A09fmK7d-VsH&^9vOi@+|!1-wr?e8fcaKeako_Yzxo_83HJz=CLf2 zK0CSoXXZ1y*KT4`U?gR#X(?klGSl^gUtbZf0*q3=FP5JHrWeqaxpe3AA2#}GYizVg z$OHPEoD zob9%I(#8H2>4A&p6x8;LFB#(zy*Asjuf*_3$8$^8yS+m&vPM>?9!fXLLF@33cxril z&FS+*P*=;j9P@hLEx=pe4;QJMY?DuPmFyS@Eok3qVa%$Vhq=2llvDI$CvLf$;tjW< zs4q$AZ5Vorm-b~HIN=c3K}R;$7+t9D+y>hf*zLWJs`jZ0bglxpl>$W5c4-GB6{Lpgu=BRgPq4b%DDb;#@&SmE>db*hlstj+MY@XK##IUPFJ2So@=Hqy z%Y2XtwEsZz9jG(HL{3j`*1Sb`Le?2*FcP%S``S!+8#FAb0n+pyCFOTMy&=3}v}3loog2Y& zh7BkYzqqCowU~T1xng%vrl~DE)c^yXGnSEbrw}P2HJ-b1XZ)CowzP z(pluSVAyk?^?R}M^SB?Lw3cMX3bnqnwHEoWB~g9yDQ&_R`Ye`>H5h+sNj|U8Ev1|& zV`s;f!hxCdLpzp%`%iLq7J>B&SJrw$T;Q6@|H>%_ia$Eqxg7}g^e}8FgG66@>l<6u zyV~8*WEy(kJdlx3+Iqe>y;-1hu=Qf1v+%P@W#40iu&u+{pp5$(Npe=5QCXRH*tl&) zi&V))O0>mULzY!SYNz-tN^S}QlDB`4fBbh=;g@B}TmS>0M1UpHr&?B5Qi8vbgfk>q z*Z!2_Lb~7aN0Fp?-Nv}dzXwGNFhrtQ8ANGV>clV5sm~dC+ZMSe{k8?DnfvmWhzAB2 zp$CKPI?hzu@)zbIV|lDemgiBPO?M8gEHZF_6BYpm)huVoiMNXK;m{Ey*1I$t%rDl} ztH;Keb~_u`A3qUROs*PS-tw!T`>GR2MrJ*U}A5Xc1Ye65^By0skdR%-0Nu^;2Hv;y%@a*fzE7uk%dS2~D-|WV`IhW7i#hj#CjcyqHuTc!&2=DlBCqM$we_3-B7R zhJ*1jMIb;7PnHd89%ZSdg+r(`_!UYYK9?iv`y?1<;f?tVhoDGe@!wlh;GC3Fta6rF zq@fv}#7ZS5V(Q`g-cCHx$TC8LjQ?i5JSZ5j)pJ11?&AD5xZeL&b3Oaz)rR1oRGUjn z@G*g?lJ_o@FM!c~>DuIL7qCyff%VCqZ?}5bzE7uHXjD+)@(O5@FC_-^661j7vaOMc zD`#n}FWo^c22ObFUz}1Ggy551fx3$u&k|Kr;Uh!Q@1)iUo6Qaa6o}9A=Wl4bh-BCiZf%}etIn;>`&{X``pVRnn z4(kJKJf%p~%Be_yY((A*&~XT;Tj@v36yubS(pg&%p+D%q7~BV>ea{Z}785NW5Am4k zzAH?G4kAUfFLj7IJ^}#G>@3AFhnmo@q?nH5U10!Py>yxJpWMaHBtN$o+v?MEyeBN6 zPyY>h`>zC+RA_Ohtij4piheN|8W``_a!83t&&&+D!3f(MFW#WbPR6A~` zBC&9#O|>xPL_zL0Y>NbZ1K-50TF@cj%H79ouHn~0x#&|jZg_r*Hu1GOBfRn*2Kq3E7(i%G`|M;uu*=3 zm3?eW49#_oqTj$ZeE)7k#a_dxZBJX@Y4U962_(hPGc##%Et6!+mNlp~G;;?9LMo>` zyTNDRf%}I}hacZ;;m|cB?gP@x0`5y1bs2A1xd$8#GnUM@gfWq93g%0`u9~$5C5B}$ zCbU>XcHtP@5Bdgc8ykmS!e2A|FRT8HAOfjJ0_<^pJ#T6-H?i*hbDoN@X0s4YveZ-W z3flPsP((SUGjG=jq98io)zN0Cnr1Uwhurl$71qa$miI5Rc^ea;R8mc$<)hi(KDQpH z#=W;B+hT7zc`dh*f)!j#*iXq4mhy^X5KeeCYmPjP`6lE5w;NCwv>R<(k`TNx8->Se(fAka9iGRk$hNy{u z5Bq-jBj>C!wYOH~XH!Z{Kc`z-ji^{D_F&=P{T5eK(6VhrSBZXQ((Uh&jcj>aex7;)~@R#xhJ($#1G7c!CpHtoT%SM<*2PTYQ; z9M++}bIjF0E!C^_{4k}m{N~kFp;B9JoS#jUC%W=f=FoJ4cd41J-a(MB;(>y9H40D{ zI^L~@VGUD&B1tC^BnrbmL1)(UzTICNE;igKT3%-Xv;<90qDg~Siiu$MPXmM{Ho+if zNl$K4CaI+Q8b@Ug*6>{zrVmT3vsn&)Vdl+w>NOisFpWR}U9{iBK^M!ZJejAeU3w9q z&MY>gC-U3!Jy>W0z;_ zQesiodhz~@$`e?nJ2besZ)O|XA$C>GPbhmLW2kkr9Pv*kI)fwoo>*)ElO%xB(BB*u zAUIPKi!)6dJWC8_Ay%X16n_~x*d_YdswzrAKq&c=Zp_c*EFFQi=VH(Ym#vNh%)>Yx z%Z;oJe86A*<*L;K+IBJ8b-vyu7HK*HyxMIf=`zA6fMKR z^5Fj>I|XhmQc17ZL~`dcRYps!)O%JKG*uI3Gf6uov}fM4)C^4=jF0UdRH25J(fM{Y zJnDfPb`$0GFgoAr#1DPTgQ*+>AW27Kfb9ZT8*3I?#7)vcLnEu930mCb} zS(D4P!XdFFMe01kYfo}BwSEBOH2A~K0%^a4!LQ9t9Qq;t&GKdrRVVRC2?azb;YsLq znrbxC5O|gOp<4lw3Ov?gc4j@b3f}CR=sT^uT7ptsODEe2)(p8|{APwKv2x*nrdvft zTs&7It$Yw&nJmkJXuok6{(F#qDX{dZ{LoIj2v1lD>?-|xtQJUB^^pKeuD(QpmH3fG z=cRZ>iHj6hil6V@E%<`-jzq(hgp;}2SfN}Ay*u*&Nf8SRHIPI#mtKks>iNoLd^m7{ zXclQfZ_BV#sZ3!q>GfR=l`Y|M6e=zdmmWD(+PRZB8F-g z^L+BWPF069A1eefP^VWhqoEokIt0ru*fV>)cn^ik9u(xr+I|}u@|hlE&2(o32#uNjX_D%pp`nul zinajWI=Y=U>|^S?Y_tCpYtxrJWMmnTH=Y0Yli~?k=BrMi1q*~^jH_ZrcRlt4;?XEm zq z@K0J0-%Zyp-NqZHO%`xJCu)0y`iFJq3kIsbr`=d3aUV9p8I>^;IyfJ-2esfe#W9O`=cxMXW5YF(DGk4l@OGc>ukHhvm7Vb^+ z1=ysYUtH*acp9Sz(8lO2Ja<#3N=GlyT?yMPzI}!m{%?OMy(cCFRiC&Md4Ibb4^ z+jnB4Nee9#-w3%hXohS&U;{ePBH3Au{3!-n-h_1eQJS={6_DOrAQVdwkY1!0rHTlV8aj$dhe#LF3@t*W69RR2#`98m1y|36pk8;;75&-qO?9Afc# z+VRtvSR&LlMO?Xv;p{fP+|`(QA8A%=WT{^BVY2)TRVeJ0M(u}5K5u}lT=;r*_~Zu; zba(vg1remx?xc5mtl2=7TTR)WC4fzCSC)%EXW*|we6xqajjr)e&o-=?YW?Is0w2}NH%#_l|7D?t9`Hu@)RyKoxvD%E{NJA9a94&>{YuVauO?)K)#f$$ zDjyCRNjYBEaq43^E4@A?mSpmRVO3!EG(ny9)m1pZ1SoIy#VMDOs@8MXPgcmJ@nM@d zu===_WR|(~$s(zkMn ze$4%HHU7ckN2y7_KCdL18bHU*N%Zf#t;A&PfkUAHN#L2Qz8$jENzYN^-0&}Bt-Jdi zRLOqjr5od-U^p5*UBb}^@$&P@|$v+BK*XQ_uf}> zISC)){*>p+5-MujwPd-=o^XBxh}scC$)M&A?7)e!`-VXF;O&b$@UJJya;_~7Q4T8a z6emqDPWtG;p=h2D%CiyvKwZA9$J@tljRk^b*6#z>=?BH{cXSq%v#shfSGl~JTKla# zRZ>$kZQ@_N=*v}1$!;3j_NPC}ZBJd;3GA)YlI5x;zZQ`-*)6`R?*kNi4g$k$RV@ZL zCD0Yg0K7W<@bEPEbqk@B&KF1g161KFiFO#$1baWr$4f6ld8S#GfbuJLgto9sj?)0I zM<#sbc|%7e4%ZB;L61m%XV}-Bd6sd5c~5+1y3>ag%%6M)_Oy0mrt|6i80`b;zwf$V zL2kv|Tus`=It$5JH#{q>K6HoKf0k5+I!y13$W=y;k?6<;NdlEu?Wk=zybC8B-;Nip zN9xUUDU@5ia`FBK{OG(5U`KBwgt4PCpUO0^f$@0^Lhciv!rU9~3wa|iSM^oqSPd5_ zO45~U$2^Xv9N}u(`38YI4iJp~7naaHVEidf6=x6P^d*-i50Y3)EK=Xqetvc7X{Gu~ z%~0-xJN@-7;92Zg#qImn;}DDDcU3|+j6`Hq9c}{RATuDvqn;$u0)KYpZ+u)ewd&O6 z8_Mt1lh2>gdCxLgoKtnKj0IhkON56fUlz{~0QJ-NK6A?xcC!YUf{rs?2|m3-NCzca z_f@OW`kdD-%_TMtU@bTEIBu51x9V1)13YV!s{pCS5m_emLvDXdTOoYq^!$I6;{J*7 z1nA6&#``QWY>H-D%W@;nUt9q0yYqk-1$zM zE2qaDRLw9%y=b9!|ca>cgM0ffXzuMF1 z69673Od|IA^ZTwZL!g3TsviuK?w8GYl?KjfYFaS(G=7jCU*O=pDp43w)Zsf4voJ}D zI74;5FsQeb54jVKu}^yP!pq9X!-^R`&3f2S%i)smdG5^WYjGmRFhN0u*JXm<&d|%V zH4c~DU;5>KY8@57b!t{F3~>xu-kPs@?a$24;%Zkkz;s#GtGeHRb+yGh(yGy~dxsQB zDRNh(m6gnRffVBb;(a;@9V9+rY(|@h`yh z!X%}olDphM8E=QdX232=uN1R+Z zKHw`;0#E1KWtR8nJ&I;bny^Wl?;7GV57UMXc4{{ckIoY+UF;cz1JETgq~1N9<8jXm zG*P|5@Hk5y-6Kk>!Ez^)?dITPNL^-o><3MQ-j0N`c)l=z;p|xBc3!C0a`fek*Y-Ek z_Ev5B)*0;9Rnz$`B>Vy{y<+$?(^{5iRd8 z8o1Zz9UlW{;xK>+<|g`$9l1!;dZsn?r>tX@KT0uhX~NE@cc#Rs6{2$7l|_?wCpKZ| zHd*?sAtU73uTlh&+0032iliKFz-Yr%aos2EAIEu{MGU>?f zF$T3(1bElyx@99?z%!$Rp^RK;kTUWo)FuIMJk)uirNPSj<9iAmC2fu>Lvx zwu5ZBBl$|>#@w+_UJ4Nut`*1pE{JMbe+Ki^z0b`t!o@?e4gVOkmP9l>`_yUeo zr14C*X0l07Z||_1#V`Q6u?|}S1hz}Gi6;TTdTP@V2`-0ajJfwn{+ZuV`R9ZkLEG+KnaQHw6?N9*0;d+gaZBbglEoDoRZ81BY+cl1>u zATn%pE~llRT3uOgzgu?==jRet>!k#28l6S8y)k;6^cC$C0)315;rjwDkj!zi3!78* z1lv1g#-%o-6VJ5$BMZ`2wnrnV-tdE5VWaF`qZ_uts5K-w6Yu~HsX3-K>qL~{`zIZ# z=KvMpoD^J9f~UsjV7`VkvIKw>f76{emtLg|7Ah4MY`g#%3A5eOagSDgj9P?~SErxS zJimWCl(;L63Z;rYi26o}1Sk>@=g6N`Eceva=KoU?ulr&wgQv$ItFSxXv@-u;tlX(} zVHPs4?y~EstMD;S+L3r^#`x3~U9XdE&Oi8Nu77?q;--_LN4XHZI4&su2$@5}2H;|k zn^@RMf%3|^o2~}8JL8It1A{4n9peG(wZrIY0ZBOBqQuPS91p$C++Gsx=&KaN7z}QYNuM7OOrUSQum!Fd{%^qy?at zw|(HTDzVyOzR_eOB75o?;^-`^`0BCxkGbBDJ(p^(N;|=-`+y1C`>5hc2&`!-2&U#qa5ea1r%?$*I?t89_2p7e<6t4S5ew{M!D zR3#Hh5QkCK+JIZCJQ;$IUX2c&S7-i)&$D1xIL={$JRNrDrF=7mG#;n8w_(VdWA_|} zDrHfIYJO*uW@*%I6=|!}FN+lCuK7L>QsHY67gNfM$US$GMlyMemDuXLpGj;DUZSlN z`KuU+p)JpvHK)mE9cx45+gD&eu`({a-s}pbEOEc)5#$ZG2^#7YZeu=EUv< zJqgO*=o#yO8Kwc^9J28#8Mnuyt8*3`7LKaE-4qnBirkxY&S4qo<-E!R$H&VVrI?+u z!`$g#Fk^UAMqU>UObNIF4|PuOKzx}wYh2i%z@1a;fm*m2+IOSLzG}SMC z?_Ll$z%_lm(8ow_6+W&H9?w}b|ZKt%AXx)Hv}@Dl>etv zJfcb zadB>TqU@gbDFSWM(QYxe-7>V;e%o7frK6!JvEA7RH7AdT);9%2G##=)f4*o=S<~Lw zAt8n*s_bi)%K$-W*8dB|@o$j`CB=hX?{5$9i`YifBjRHPZtPBcC+UtQ8oqtT{75oc zoI|%(ZyYjoHJJ~tF<2Loa#j|qD9O5i`9m7&XztMR5wkf*hI?RAePRh=JasjqvNDGe zl5k~3TJmI`XB&H7PG>&L;Oj5fv~J#fewtmbi?w_nXHoo_)K*`XozHW*IUx8cQzx9> zZN(V$H>I&voaSZl!f2_!kVShJYxP~Z2JfF3_4}Scx+_D*lRJi4yu&x~Evla5POC8X z${+l3E&>W`E~m=+#NkRD!x;@e%ql?oEMIC|pPiv6&cj=nEjTlY8lu3`Xc@Fwa zjrm?oOi-*+lF;D-NF)7C=cXRTb7ks-7LJp3$D^KiyjwrU;8pf;BY@$NJ40_-PK3kF zQgcbabk5Yv@7;&F*doTeuCqyRtKB`NJ8(gucg1%MDSf(F1Rqc zJqe&cEaSQ>`y>||9Z8o_502be10R!Kevp%sqxY?T?Ck8UBhV>yOK_u0V+sES69xQRsFvnD&m*W3b9)Ss8p%xgPhpV1#VSHy(&KKz`>t?9#Y4WP2m>oT3TGLq zVdc1OjeDcZjOH#<1;t%R7qGU!bP8}l0Te^4^Jx2AOA*G;D{2V|G zuGB9cSJJjLEiVG@)bBwSdlD?;MpJ86M!EWXfNf9r(@d86;{v`vMK?^x%FlGu;G<(6 zBaHRdkF4p<^?ZHvJe2a_*M-Qu8mWfgvU9mepM@U%dDNX8u;{6&sh0qZYR&ed#LM!g)%0^pmA!%f8<{Ml-eD{1^M)@y@r_rw zZTM<>(g77T1^3^PdBDLE-22}1Znc}Uv;Y#~k(;I^t zoU)suwg!q;xo1FJ3Rj(3W?esN9F03`a7ey7uD1DFZY8zTAfl+rWW%9H-bm+z{dWoG zdO-<+@Xd3PcHgDv;}g*|*K4$NzG!u8f?s-TSubSBy|vM5P;^NBCpjNKmFcspd0mA; zY&&4QoBRk!X-@pX&}MwWZkpwYAy)2GVF$B+;~&?;U3i@~hRZL$uD(#!ZcCG-$M<*oimuXIT`>Lwt_x>ED+oXG#3dLcjw_Q>c9XO%}^ zo;<<9S1wk>yzuPM0V+H^aTaAvZEVNYRoc?Dcs|oeH|Fgzg0oH3j+%Pq=4|cwkfh`+ z5rRsN%r(maQASGbAXyOMp+3+%04#CIcK9ie`e)%!St|KSGMB>*`c@p18~s>aVRw%d zhA$w60L$~%b85OFf<8xB8S1{?;NhF<1NZn|UdFubJ5MuMJ8Y%ybAylm@Lw6Dz+HSe zZBzxY7jnFfo^MDqH^No`vo68_rDfN5CfeBCh~b%8<%S;xRvVDD=a9pk>{g>*?#yIZ ze(A#k{)VnMOq0v0JR|sSGLxR;j0!WrZw$g;rb_sm0V?Di&-7lrOPQtdP?LcJuZ-Eh zN)AzgS(29o9FQ~c$`&iGrxtK3etx7wl?i z80uV+)<2=!Thz38wB)1rA25%W)%M zdBu-A4JGkdmd5DV_ayVt2mf8}6I@L`AcABPFqJ`|ROQQjz=17C+oyX$RF9hKdE?W{ zfLlN3nYO@Kd0|c4PJfkqou=uB zito>}nPaIG!9hotaQ?|CvfB~&wk8(KdS9VD3Me9zoTH4Bm8dWw#?V=8+bL z9K{KLy0QcBHQ#!64DbccCrV_@A;gQqy`MwmB@>hVnlo9}$*%lU#R>w(oxb~c$|+&=4avmNTYP_gNm z7ftjWfP=7oa`od~S;*>~s7;P}R6L?on}2N*??n0tRCiAQ1{)9aC@BMtcs+%RvRjI! z96{mkM+Y*^87}pAu=iQ{n;`xIfO;%nrTP5yJ9HJO zQuQAP+~O*{^C|s+A!^yhMMoPsq{NSr30UC+%9dEo9Iu*<%&u0p&^Lp^ElgAOwz@t{ z%K6Z@IrIHEaFgPhb0QfxcxKk$36b(N43;1*s~MoqxR~Nq{iUQA^$t`Q@4!G1FYX?0 zzA2B((j`+2o0S;i!nv3v9fa1XUl1nI7}$MJX8&ZJ;44F1@Npf$|L*1rnw0Z?DvKuv z0q{`^iOl;7@!HOmSF>!fXoDR=$uli+YaD61>!~jJkJ-utCL)6}P^b$4j8eJBS}e0t zqsV~uT$}#a{Bv|V`Geq?=j@4cF=L$KNc-@$=*-4|YC4+oA5|CrmJ@0LRC|M(8+_b{ z1103EIl$<=g#Paa$gUE!urLEyucNmYAiPf|3$&z~2V3xAK?EFelmybT^ z`2iOB+eJqLK&_FB5&NMq$^n~3I_D1L)0=x{)xAtF?3fVOKeb79-FaQ}`9mc;{oI4G zqDPDU6Qf?rjubdco$nwSoLnxWdikPBXJna|7Fz(%D|l)$8`X0As@bJ8UGWBXyn3h3 z$=oiBEIM=bmh!7D@WET6Vs6fS`qCgz3AxY$JiN>av2$*)FOnQmAUBO#&xK+Fn|CoX zca<@u(ah3pyYd5S(bU}vRhga`SDi?2xV+T3);gVBA!*cbz!)|9vGj^8#>lI14NcJ^ zmZ}V_8G^IDUJeH7%b(UB*Ut9(9J?zgE!|teM4cU(hiHOFza9?Ko#xU^?^GlQrn12t zvUaN~#JnFr^jd^lK{3LE<`^OO%T;a#b2oT6nZZs~W~xJ~s9mv58cxje zI<0n8Sq0vCl;QM6u3bheHMm9;$eN7-lI~)2oB?74yU4ree!9Cv8r7ZbeHo@M+~jlJ zNs#o%9r<6I#(|Wa|FBR>{94S{)GX2Fo9;Zzu-_P#W;Ng+;ylZFOC4^*`>n+sWIz;~ ze711b(Iv}(Jsqu#Z5NM=5~|Q3dIHUvHl;yhRcJK^)Jkm7U0dk4_S~hoZ|yDV)}yTs zI>!+LIvgoc9>$cByAo+cQkvx&R4R^dQ1#?HXOsVegTzM}7Q zj)6l+x{V}l&C}}r{(b<#Idit(9rtBpd> z@$~zV<=k!gv+B@0xr|GNuy*%=wU|GhLRY*=I|7A+F0df-qD`;6^_BnAFVES*okv25 zO0ABoN@XNO@)845X+N{R`D+SQFbssysTB zd5yc&>i7qh&Z%`_eNERofyuq8_H%2Rfj>W;K;(*-3fzeST7q^!zm6zIO@Lb zeL*|3F(F28X6Arb8=JfelHBP6JYJt_PhOw-wTFAS<-HsU35hQ0)@@yTu@z&+X`Bjb z)LfCjDT{&dtXrxmSp|;L^|44{AY8BK`DHT6K*8zMweZlb@HLD#N;c`?XNHiN#~_XR z3Si-79>ggBJOuY>rTu9oKR#L(0xE6AL`ia=bSiU(q60rbxrdZ}HfAJ@j=OkaR+{k% zteXeY+hguIiH#v^Cx}xHwa<7 zAcU;krTKa9#8)VhaqkYL3yA|nb=#{+jR%(Zl^gttsikOn+b_-0eSLj0V#}vAI~VKi zEFkHL^^`!n(#ECtdt&77Xq~3Y8jpi!C7|^Wy9Dx$9J-f({d{1yIy`uGDo!JhE ze-0zyQ46Xy9w5&`gPPX z9ZF97Dsk6xy**wBRX98He%rT+0phKrUY>S+$x#@CH*-^#S$`K%z=3v}YBp);&~<4X zv>X-O<4l{)XTMDfvJzm==j-at(>Pg0)+TV4!7LqR3KRVL}@z;IFGn$!&9*xP|5Py$IAT4d>7%gwu@(9$(NHUqMvi&CJ%CqwXMljCzv`F-de$fmjjiX$VcaJV$ykEqhlH3@`_Y{RV z5V2Z>9 zz=V&V{{-XXOu%~icJybKCm^g1jaOoHjRzK;yhE_M*7a&qc#m*PYK?ohucYre!eoZK z96o8s`e6Bh*ra%{p?_c-yl0^3Vxx72C+N#F$ULWRZ(kqW2S#y1Tx}x*GCVEG)Ibu+ zF`ZSKMB?zODqot_v&!;Jk%!g$ro*hc*^d3%wbw3E0!QgMEGzIbTWPCxgZHfo`dw2smEq#r_|Hw2khUBPdp*+C`;*$KKR3h6B2*I zRDmhd+nv8H;g>@y^WfSdvGPU=%_Wm{WHkowB7l!I^; zjKE<=2Z}Z1Y@-16Edu(LCX0AmrsxoWLIj>HleI`EVWc2_K!V zY`PYuP{l2tFi)F87n`$>xc)J5YIRG5A+VJ$C0L1R3T=_~-hdor(0naPuZ!KnpZ2B1 zHU-%-P6i{EgV|}l4(;zvK~~yce5wDxwH75jtGs)cxMB#VGZN`4FjXImyF7$Ce#GEK zAIuU@+Q}KGw8S2gJO3rkpy}$)0-c&^t?2Q0fBR)Ww#Q3uOa5`W?y+X8z)vN5O=M*q zE&eH*#CO4X;uI`2gl=n+)^g%smMY4_s)!2`y=ocHNfUoX2*dToS^gx}jy0eD zUH-rQ#;2i&)yF*mI@!zyD<7B?z^6ic+aN zcE~7Y3a01}l4Iat`rD;DeG%}6cwd3sce14%w|Z4#U%k3YbIcb&5olsvEwWBjd&&g< z&jtN4)XUtvja1zS2WrA^BRyyq#w=QSEz^Yl=THB4_u_VKJw;>~PG6q~#GTCL0ERQ& zKfe1EG~O-BaQgYkKsH?l{Xq-kR6TggSzZt;rOtKXU(OkNc_N8!H9T8o7~4!xg>L-1 zbB{Be{wv=FJtl}oBD;bqOF~Phtlx9Z|I1=|MXXHHW_vB98Yb>Lm=}$P8+s9RkUk1c zYOjPyw3q2DmT|lki4fBz>~{QDr%6+|6yTJwud1QJlAVfWy5vFw?nUyCiwTx=#l9KToZO9Boaa zVgGGX2h_ESEkugQKqt(eMU)TYYf8bll zP)-b6zSt(s_K$t)3s)KuV$A)LvjTW&fp=<+EXGQ~m-d$XXHM%=b{uYoOyanTg^PF% z17)bsP7G1ot-L-zJC-hLjcP&*gu96-&W+it+nSG4KPNBy(U3~%3Fyd`81-Mx+!PzZ zSo&-7Kvn(wKI6G&@1X)~ z6RNxGi)?xqBDMaL&_PMFX_3U5eNn+dB2bYP?V{fFjxnlY>u3&Fylksx~g z*AL%gCoF8z4*5zgey#Ac333ZmegaW2ie6Pi9Ydr{c4Swop;%E$kVV5{FQ!1M!_8RW zS*)Sc`<5bkhpS}NcS$ito^H>{T#p@lyN>e#`4_+7$2kv%%fGVgeW*9jnZ3qluE;Yl zEuY$Y4SmXDb|q?0OhSyCr=wnU{X*b@0P#{yqsi%wnbVrNFogAKtXmE-TxGy`jHsx< z+qnRaqx}oZKv=!;>i~Lq+M-^u(rHs1#n!%>lt2XANOYRZ(4|MNCuA5PLXvbu zlzeMO%6@Ui?5BB*O|d>u^7s0uf4ND|6Jq60_mof=6baI8YNssru$kmrc_FI1KVXt*-3nGbczwgI_5U&!N`7w8L2llA%uE++3 z6SryLkAAzY&k#Y&Z2g>C<)+dHvk_T=h1$18$h}>SON-m38P0Gb9Zzn6|ILP6QgW<@ zUk~LttgU!vMB8d8cm;dTwcvSwn|b zTZ^p)&1Hvm?_5a`k{pK+J9?^ey2!Lx->iBOT{4E~;p9A$vWcvMdXd1zlJhg_F zH+vyyF`9&50^RT5Xa~2^<@R^=-RY@(ZNWDuCbniYzo-p$QWY~*pLyfT{GjXiq4jXJ z5&0T~dLulZV}AeZzYqMjCIe1ViiauiwD$5=bsylg*4N9XhpeZK)KRWPu4Lq^B<3jc zm+b6%f$eu6bIk|2sTPUVQ&Dz&1u*}R`i5qeC%E3OzP@OlbzHm;CdZ<7_Suzm7{au+ zfux8LG0g4jY8S9LLBUhkf!6ytA)GTngrokm7yo@iN(w;BIoB9qzUF)oFrU2cg>ePZ zOT*rdvSg1Cc6azVTT+4mUpy@11jRoEpxy*s<1Xo}&441Fm3uOx-6TvUwGWQo-*yNe zk+UAx>U??3t14IzDvz?~mOFq-^7Yu4+g>uP3yrCYnIB)j)$(=y(6gA|PS$Nl21T+~ zj6M4)SCkx(WJwsX&VX{DI;#X)chiVcr{>$%m4dW}_!g+FQN5)s)NHiZ3;Y$LqA1E= z0;!X0Gi6-Ww34r3tP zwsv<8hGzA?pt{0{H}Y^#FsLO8WJ-M?>xgxvyR&O2P{L@7;eVixr6SnC>KA68FB*fO!d#dP>(- zLPu_MTKa%-n)x*0m`+_uCdM7UQhzNe#k;j{OXTe);m>-H58eqf6h9z9{e5Re2E$n; z*XaW z^u5}tNDmsZk*)2^VK9IF@!F??cEvQ>*~6}fwIy(wOx={*J$Ny|Kjhi#^!QdYCF_bq?lB-S*yxIYZZ2to#tF`a4rc3!Bgdqe7W=7 z=27e^IdvI^eVX5WO(TXD>i*tv_d^Q`+nE$KbT zthn9@$83M5t`i?C-M%1g((}7}eg-%!9KzV)Q6D_To2#YygX~FT)X~us;`4A!g|GAj zt)$RU63hy}zJnVVy~y+#FS7Kf2;0*f*8fXu&0lT{w{!ZPRvKLg$Q(L>cD8#F+5I%5 zO0aMXg-CgT0Ka|qFH6+`8n2>h3y#5`TW;5rXqmNi>y9Nn*}=h9Dwp#w1rJf z{V$#VBN}HQ(1pZ-j(#qNwUeFZgH+8F{t?Dr2Ey1*VAtLTwuOW8E>$mrxEX<1eB*<@ z5cb!vam9~ttJPj#@!KNl`cs}T5(fW~=XiF8P!m@$Gm@z?M4isQZDV%&Vo0s4bIKyx zVx|Q~IZt_lAjJPm+gcNo9HQM)M5oriA`7dU%q&u z$$|ub4ze^eOyWo^XL8_alm6q^VCG632=Pn>p)Y%ADJ?y5pWg}G8|m?!O%mgq8@Y0y zk$~^bWmoQ0rd&Mr$hm#J=VdzGR$E)Aeq4Ay1PZ>h63{i*^GU;wmS`-vY zb8;d{k%tj{w4kGLz*46y-)7M@xizFbS1hb^^8!A z2v5Gs(B4_%ll(PDmxC#$)_CiKo$)o!gH_HGyZ!jxn5NyeUN2&{zxi_O`QQBts0MV^ zV57cNTx@U9XW$toXJv|OJ_Ur&PV_dFc*C?h7{BWW3&}1W7Tb6)#EUv*Bo(uuw4J0> z@q}_uFGt~uG}iV}{~EOP_nR-$7e3CJV#t}??i~q)thXCww{kp8iFA}6ETx`gYdd4M z8OS5+vrK8-L${i#z+@ZjgPN7Ie!Fvlpa{PSL`UR_f_A3aIrqm6TX>*_9sErP|VAFM=(yAZIznFrQH@yBqL%j~0xz z?>pQgGLa7bV%ymgk(U32gzFF$iB1tlP z;$;uK$gvUXRR+bq2<^Bq&T}Rt;He=L7Ao8$`4aoS*InrncDpo}+Lv}Y6ezYcC{RQi z9TOti;@A^M4pDeMKIrk-TDj|NfEY9XW(xm~xj)FZiQ7*Wl*5Mn(YuAhC83^{b6{lI z2}Xi3FRI3^JX#Yk&rGd6obk0hG3N@(bwYGrLFO_uN)9rgN-9{`df8{55@rMGXtc0< zO9_AUp!}qEr?(A*oAWM^I0pv%f(Sf&ldi}bErZWuje&fTw&p>);n1w*2Tai3&0?Qi zw#5BZxtX`vZK{!bf(nY<`>`t`IyzaEfGX>TT-l2jA5sia1u8j*H(8ydPpshh8>AZ^hr*Ekzb3Pr5whZpX3 ztxOT*GE=(ah>N|-@y(xjh3@Cgp*t=rwhBJ(Bo#Q7Eb~4D(%V@)Y;SkgIGzG2BDBf4ZBgb-F%(s8X>(hDSS+Mc2^4QM;X~b|%PMoWn>fvu;b4l!ww!>~^(0 z<+V7+w_9+zLT*utu@q;Nv*dgL+iaA_UV76gZ`sjCTO(_Vwqv@`?x32`E(cjrFFEvX zitc={R_G@0`Rxfo36WHkpB8C4SPX++i1WY>#A$^J&uxc33$;?pOwQ@FC|v*gz~>A> z@%y88;(Jy?Sy@?&9n0jB7=oA^ibLi+K{iH%5N!W(MU62$7*<>$G?1$+CV+9EERNFk zY2&qR(UC%L{4M7FPtES;I~kL9TZR;8n-cI)gNuG?H5kkDo^R|(=oq)qeXO!kh#PmW z!v2F})V_^0MoX~aw#ObfQqZo=x9{-umtNP~2Vc_|?H_Vmw#F43ih20;y%u-kXoSe& zE5hnR^h>4^4sp_H!>qFHv1ZI*w-(~AbG$6f$lRJOWuuchUBPB*ZOel|cVPl4Uofwl zyP+^CT!K?%Zp=g4+ipQieX@16=cYB+S*m*mfyCdJs^v6lt}w@KrGPxWh0P63xdU?Q zGUX;Va@U#EWa2vHjf01DPs>n*VpgDg#dw*kzPR^;WP9HN6}io7?YMyc zjy}rR6!tuVH$>{hm?Rn;Bt;3&{1ZkZb{3Yt(BQ)&j2lqf?1KmPIOb%}z4v`yFfwD8 zR1rPlx)<)E$WWvqitp2WSr))qP!Bfh?MG_J=XB+h@HlV&51wk)b6rK6JnQ%WF&mC;{!j7+rK z4VzNymxwmBKT`PKY8-y=)fLWsmY9}n{rK@y+A$v*^qAL|uq`9JLd`i*!}HUlv#oS{ zAt$(8T3UG?i*2n9wvjSrvF2MgZ)c^=3l^h^Y+I<#f~xYZDhv8L>kNbPtH&%6LvMV! zgAByNz>_;wZsa{-!SWgVs)#VO1EiIS6pSpicD`cPd9jTNc)L}ViDz4ae5K%E?(Zs} zGn8ZO0$)g@*Pj0rk4?<*^7-LpBth$d#JH!T4x?;+AYis9wx+xpe?V zoi*?#^|q)(K779#eBDcq`(hhHvtG46fjD+6rV%l$iesce1-8gIm zs&zykN!^cl9$tckS_{PZpla1v2)%p2dc}FXSMU#}JU=O;!F4Bv@xP14Azk2#DJ_B_ z9znE3R!nx%es&R!TNvEF7}s&SCgh83XpW1!4sB+^pisTdpyCnf>YJo@=N&}{C2}~P z4FuDAO;qW<(|F9+(BGgT$4hp}^d2R9E_dDwPEV-Ft+~0EW3mn{8>%eUz3txkE;nJ2 zQ&MYMv8qpIr=u>~7pb)nq^N^PyXNfRvo!9DW2`BU(z|5#C^axq`)8>&?*p3(sLAWZ6l5>j8Z2 z7|43zW|$rDhDAE9ltjf@u)I31M0&Uzw&K-SJ7%nzMAVLco1HAS6C<=Xi)@{%XMgLpk>^x+mp%E&E}*yX{%bSbZQlc^WNkQ| zTZ`uX=I{bjJm2~$>F#0a3=n=wealzOkEoaKzFV|mHH~Z4I?_^aI0Wd~zsFBrU2mjx zrP5(Us*onyduJ$)uUgc^4Rv+qv?%U|-=u928c6*@?}zB}LfY@QfIf-DYwtQ`bT z2i}qf>tHwkHQot^Tl+`Z`Uh+8yprloBgXrH&jxSK_22f*kMxM~^xt!Kx>RMK86C=d zP$OFRL3OR%)oYtHI&@#T5vMTsHRlL3x~VHzDp*B~&P9M@KSZ}i$IUshSK96Pe%f{B z01DPW`{O<(^uT``z{wpY9)wc-Lrv=z>GnUJWYUk)Q$5jT`-;vC68snB^tuGMLGp5D^4_0AKq^;%kjs8<>c6-tVHE%+mRfpu` zn5ex)$Eg&Rewv06S$@&5g+z+VC)+v0V2)3(b z^L+Z~L1TCx$(4mLkmE&B+yu1-*{qTay0FooIMOQc!mf44^l=aW#kSixfGc7URgDb5p zwyo0>US0|ese7`Sq^~&t@qkmtuC`C9-u z$TQA^zrnrr1r%N6#N1S2K6#v!tWKHaX=xB?o~2CKb4Qv_hb{wXChY>l7?M zfYRz-YOt=vH@#!rzrn%y7C<%p4QK_Nt{+hd?gn#na>_(Nvn%92o>SxrarbqB$f=te z#^EO|SyERi_I5ao;(??2P;TI~(K1m{ePGu<)oyJlEjpW%QkI#`kvhm&HWYZzsH5Ko zvfqbs8)I+vF{|A6Jx+o!rxK)(*lslixGZyZG+62NWe{~U7scX|a>#<#j}%+iv$hdr zm}jd9Q7YJlNP2DQheTNw;gedVtkdiacbbwCSC;}HUIE?CtTU@4O!r|pj^u|`HucM% zo0YS1=NiiLhkV}{H{gakyIWJjK-N3HSDOY>#ZH7AQS5c?B{Hv5#oR*XY{X<24{TKY zl?Fi=}KHytCh*?ybl3 z6DjBes+k^em@zy-DppmRlL)NfVwei{&};tx9r#yJjjh+w9PZBs#BJ(50$^`d{S=+> z^NHsv@$+ij3RJyfBVYX;4pbwP-)8@5M_8@)5xf*dR_Ss!%)CG(+v0xQO7%%L#5#Ly z&bLq}RLbF=tBG*#azbq|SZw}CZpZAq{GnBcOrNH^97NZurWb}CP4AjO*v+xw^tSON z{)GZJed|^FRSK+N2_E8ZrrGLgw9piuuTQ=?ns9pl)37j@PqlNvM=OfbqAe^E;QCkr zm&(knh`YSlykj1tg-g2-Upfn|m^sGesD+9C5tX zsvbnySDfXxL>h;->TaPRDHW;cg3;ZUhERv@7A%)8|4spM&`*=R+5PF+P^S>x@91j) zPf?Zc`(JQ9dcoKBkwi z3R5+HhezaNz&q0;x*zNBW=o06UVbdXDdYm1tB#bG=Fp}i`sD=xaZK~tPB60PIjd){ zkVvr5|JTlS#x<2}@nlg%0fmQ%N|BC&fS?p90TmSKB1kU^sH{YKODI{41p`DWA_&0( z7K)*0=v_fT$jVBIp#|xIfCLg)1m1*&U0mJY?|pweU+#xHckU@OXU?4SKa*tZ>|FG; z*FQYjZu}#pnOM!xNShWa)z-N>&?&e?S6)7zHuozn5stY_ivp9Ls7B0x`r@7Jm#EK>JsrP>hM3H0s))ST_>@fMpVCR z$|e2g-2u0!zdmX#4N?vxG#4!w@V>;ZM5;ZWDbm(F=X*cEr-Uy=3hVJq;1{EzpPctK zpdYedROl~MX5@@4=IPHzPLcUugBywT=l?^cGC$7A1tr_*qwSq!`WP@x*+No|8yy^r za`3JX;mJWeviBu5V%RE27~1$Vzia08kL>dLbw6aM2!&*Hz@sNAu7+MBDu~Pt8{(xQLr*YW z$|$d%a&%T?nY2D4vQ8m~4r6qj{+ttN#h;I%vg0@+ zrjh@Ip3Is*6?|scxa=eU_C7~&Hd(2-=^`jaRTQKk_WOBAE@c^IrTV>YS?SzK87}G7 z7*CDBfkTHL=?yc!E>4L^9$2bWRj-|sX#0xv?>t%y$J52{k#~hT^v%SLIGsV`T~_7x zh?ZH!iP#-}-VDZD`Fu0OqBO& zztWQ(e!-kQVvsi10XJ>zA7elAjN2(|LM-M8V@ zwV$~i%o4ZXp0>=)rPvR~dU?g?22I5w_g2Xox-UPUtr1|tg& z)vXVTJ$pWg=Wc14X*jEl@UV%6X^1A#u}4?r`24v`%N^NuntBML88DH=?ewlRncpP? zGW0+783+)0i}Vt_rk*)?#+WAx|Cjd9PD5RS}uF0|K#3RCHO6?l62)(*mr`flRS z&X`wPJu+i$*wcQ%Wd2V7M1i%Q*;9N(T6@t#`JPV8`4Wy!`b-60e|oauD{Xgjkw(VQ z2h*`}lhcd1z&!?vUm6mX-2qVd_Vi-V>n4CzOavLRR6pIRtC!=-Xyw+loIbI+J`!zy zJL>jOC2VNQ8rDF~3!V;+*5h#v6j`fgUj)az1Z^>x8I1!`8|Ft-?3E{ z@iI=ta`^p#z>KUWo&NUh=JI_$gsZcc_@@J$eC>U)XXjlTQ*vF(JX8B>=DKmgpcdqe z{yc4wsW}o#>t1;1S#=3}c%h@h1)-jW6Udqc_uE)XGs9LBubR-S^*;&o`HozhBBt+H zHaOpHb6`V`Fbg;%hAQbIcb*WV6=p&@-8~VycAU?+J@eGzM#n+m8e~kF-eb6zPr;Fn z^UI`s>-pyh*mI}*r)36XbIYKXDPI@u|HR&wKvu$i6oV<|v)#q{7T+XO=hZ?{f%9G*E9Gpb*u2>(wjz#r3st>HCksjr)6Ft8t=5nR_C#Sj@)kD z>&jC;VfYOR^W$H#MzeKxI)01PwGlw@0%(=}j_lr19fda@_AbIeb>3+XR7rbg;MnJmiqc38BH_BZN?o^GUR zjD_@79z61Ua!bQ0OgahN5e1P0H3ng5HeL0NSnCV>DAx5EQ)$stghra|Sn^B`UnHq0 z)-aJt&r~VJaxF@9FRp$H*7)CqW24B)oLH+q;=ml=0q(}fR_ z-EeA)nQr(5Pi6s`i&awb9nC{~DU&+mckW3X;LCAUo-f?x5hCwc-e-w@C8EYmZhbdI z*KQ-=Pe~hHuy=2Lb$XFxON86*Svph3bp>7Zo0PoVQ6a~4`d9*2s_zvp4|i>!LW?GG(B=D`!Mpch-WGTC#NS88C%?=MPG5i+m)m3)tc2M~j3)CtWr z+%%|hBwiYt`B+@o89p>a7mv<;$S)o2fQrthiU^JSO^G| z-65}f-ZgRy?wQTjr7gO9{wP^9Jih^sZRNvD6S~kV=~y+h=}YLj0*uq(!J;I@w0~1X zD`T64){<}9$g*u=4A|ssUn0Bh5j~iOmgb?~PGu^Lsz!ihkz%HMAZ$g&{NxX~d<(0^ z-|vE7PUyIn+ZF&5DB%O*!NBQS z$eJ@xf53#5FS>#Bb-~nQCX{g0{KXG?@KQd!tL!-dCk76^M1(&+@kwOzZleLSjZF|3 z(!WnvPz?HrSa_WXb)LBs@Gx>UjI5iP7juT=#<$3V?>}5cm;%7(x5fbYO{}^cu%&&& zvOanh@diR{mfJSiv$Y7!chh{W00s<}So=Ll>AR)>M@R)k!2c!v|5WOmK?RGA*$-yj zEdb{E)`XVC_ot*dpjUbp~lWwdDQ@KOM zSBdB*=T{_6db(aqi&fI-$O%FVun=E7!W&md1bT#VgDM%~?teiz6@Fp6sEFqjDU(!> zemk~1zHGlBU=4r={;V5QYlxX1;BT`wmFcJr3j*HY$wSY~~Ut{3VI2v1+v#WL+m#6IxVg(goFPX7tV$RuowiHCF|jyg{&zy<5N3R#2!lsxK4^ z^r5KBUA^()1m^5`2s0A+F?wjW{Q~RG$OVdwuVNdihr*2xy-lqt`=nIS-uY0Gwf(5>jkxfwzQq!bobBw4ra>ib#c=3_bVfy4Tq z%Cmy*bblbaSi(0ZMAk!w5KhBT!37mJlg??Fb~VI_lWkb!$gGp&M#-=yNx!4ynS6x4 z!Aw?^Eql810xhJjk9X&Ke6P@= zV;~U0I)(^07S3gnecSsdp=$R0HHnHcqVUbCwy`(lw?WX&3@5I1_=FEIicL}La?Cdi z_nU|xjyaEEhh)1VvHD*5b-!BOfmj1Q7q5%s5M!(N-LM%h$d!CcRCfExU?on(?(f?6`cqySpo zEUY-;8YK*bAhbReMHltdiwE?`s#~|*;p)w=hMn6~IL;wc4fPaVAHhAHi%mD-+<*F6 z<(GeIy9p-7j@q9qRf~ZFRNpP!8nc@U&2B?Qx5@pDii_#H*>rby?4)c`M{!?V^CD_| z<6X#uyCJoMu{w-=51)z68^&*i9b-ow%9XC>2M9lfH6IqmvxVnTJA1KR@O}+YdFHyN zO4MO7g4sQ&i+0K9n6QI(BFGx(n>9xdqR!z1q1atP$uT1TlFI%(vsBPMN2R)*tz}Eg zU=n0YUcSiXWp3&pil5Fm!$Do-J0z;x3AxM%TMw~*HfH1Y2jLLTLFxB|s3VXZv?i{r zA*KC8Bc67>z$6+fYOKNF>A!Lc&4%I^+wKCdu90EyA&_c-Aw^k9 z)c=wpUJiw1107eVgvbmswrZkg&ixK6!fu8@4zl_LTzZg4mSI2!-ZFYy@+&%_5Sydo zLJ)D+2VHEp1jtAM2x<-#uR=iM;RP>1L)`*@WHmjI#9}=~#X+ct)p!B5tmiEb-tDZX yt{xzm)x^QZ&U*ek#J{ot)8PMUUJOVttUw1vTty@MvT&?5a9ZC~@8xlq$o~SpgW|vd literal 0 HcmV?d00001 diff --git a/server/main.js b/server/main.js index 22ada28..94f97e5 100644 --- a/server/main.js +++ b/server/main.js @@ -14,7 +14,7 @@ app.use(bodyParser.urlencoded({ const args = ServerBootstrapper.processCLIArgs(); const logger = require('./modules/Logger')(args.logLevel); -logger.log('LOG LEVEL IS: ' + args.logLevel); +logger.info('LOG LEVEL IS: ' + args.logLevel); const main = ServerBootstrapper.createServerWithCorrectHTTPProtocol(app, args.useHttps, args.port, logger) @@ -66,5 +66,5 @@ app.use(function (req, res) { }); main.listen(args.port, function () { - logger.log(`Starting server on port ${args.port}` ); + logger.info(`Starting server on port ${args.port}` ); }); diff --git a/server/modules/ActiveGameRunner.js b/server/modules/ActiveGameRunner.js index 38b9851..ba0d32a 100644 --- a/server/modules/ActiveGameRunner.js +++ b/server/modules/ActiveGameRunner.js @@ -64,7 +64,7 @@ class ActiveGameRunner { class Singleton { constructor (logger) { if (!Singleton.instance) { - logger.log('CREATING SINGLETON ACTIVE GAME RUNNER'); + logger.info('CREATING SINGLETON ACTIVE GAME RUNNER'); Singleton.instance = new ActiveGameRunner(logger); } } diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 36822aa..9c838fc 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -450,7 +450,7 @@ function pruneStaleGames(activeGames, timerThreads, logger) { class Singleton { constructor (logger, environment) { if (!Singleton.instance) { - logger.log('CREATING SINGLETON GAME MANAGER'); + logger.info('CREATING SINGLETON GAME MANAGER'); Singleton.instance = new GameManager(logger, environment); } } diff --git a/server/modules/Logger.js b/server/modules/Logger.js index 50b09d6..49429be 100644 --- a/server/modules/Logger.js +++ b/server/modules/Logger.js @@ -3,7 +3,7 @@ const globals = require('../config/globals'); module.exports = function (logLevel = globals.LOG_LEVEL.INFO) { return { logLevel: logLevel, - log (message = '') { + info (message = '') { const now = new Date(); console.log('LOG ', now.toGMTString(), ': ', message); }, diff --git a/server/modules/ServerBootstrapper.js b/server/modules/ServerBootstrapper.js index 3233c15..a946de3 100644 --- a/server/modules/ServerBootstrapper.js +++ b/server/modules/ServerBootstrapper.js @@ -38,7 +38,7 @@ const ServerBootstrapper = { createServerWithCorrectHTTPProtocol: (app, useHttps, port, logger) => { let main; if (process.env.NODE_ENV.trim() === 'development') { - logger.log('starting main in DEVELOPMENT mode.'); + logger.info('starting main in DEVELOPMENT mode.'); if ( useHttps && fs.existsSync(path.join(__dirname, '../../client/certs/localhost-key.pem')) @@ -46,13 +46,13 @@ const ServerBootstrapper = { ) { const key = fs.readFileSync(path.join(__dirname, '../../client/certs/localhost-key.pem'), 'utf-8'); const cert = fs.readFileSync(path.join(__dirname, '../../client/certs/localhost.pem'), 'utf-8'); - logger.log('local certs detected. Using HTTPS.'); + logger.info('local certs detected. Using HTTPS.'); main = https.createServer({ key, cert }, app); - logger.log(`navigate to https://localhost:${port}`); + logger.info(`navigate to https://localhost:${port}`); } else { - logger.log('https not specified or no local certs detected. Certs should reside in /client/certs. Using HTTP.'); + logger.info('https not specified or no local certs detected. Certs should reside in /client/certs. Using HTTP.'); main = http.createServer(app); - logger.log(`navigate to http://localhost:${port}`); + logger.info(`navigate to http://localhost:${port}`); } } else { logger.warn('starting main in PRODUCTION mode. This should not be used for local development.'); From 82e86f4fb67586739e7b7daa1bd7850bd9657414 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Fri, 7 Jan 2022 00:39:37 -0500 Subject: [PATCH 55/60] role importing/exporting --- README.md | 11 +- client/src/config/globals.js | 2 + client/src/images/add.svg | 1 + client/src/images/delete.svg | 14 ++ client/src/images/info.svg | 15 +- client/src/images/pencil.svg | 14 ++ client/src/modules/DeckStateManager.js | 92 ++++++++++ client/src/modules/GameCreationStepManager.js | 157 +++++++++++++----- client/src/modules/Templates.js | 40 ++--- client/src/scripts/create.js | 27 --- client/src/styles/GLOBAL.css | 2 +- client/src/styles/create.css | 104 +++++++++++- client/src/views/create.html | 21 +-- 13 files changed, 378 insertions(+), 122 deletions(-) create mode 100644 client/src/images/add.svg create mode 100644 client/src/images/delete.svg create mode 100644 client/src/images/pencil.svg diff --git a/README.md b/README.md index b4153a4..66bf448 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ smoothly when you don't have a deck, or when you and your friends are together v Ultimate Werewolf and by 2020's quarantine. The app is free to use and anonymous. +After a long hiatus from maintaining the application, I have come back and undertaken a large-scale redesign, rewriting +most of the code and producing a result that I believe is more stable and has much more sensible client-server interaction. +It's a shame that my first attempt is what ended up in Github's Artic Code Vault :) + ![player](./client/src/images/screenshots/player.PNG) ## Features @@ -23,10 +27,9 @@ The application prioritizes responsiveness. A key scenario would be when a group ## Tech Stack This is a Node.js application. It is written purely using JavaScript/HTML/CSS. The main dependencies are -Express.js and Socket.io. It is fully open source +Express.js and Socket.io. It is fully open-source and under the MIT license. This was (and still is) fundamentally a learning project, and thus I welcome collaboration -and feedback of any kind. After a long break of maintaining the application I am back to work on re-designing and -improving it. +and feedback of any kind. All pixel art is my own, for better or for worse. @@ -65,7 +68,7 @@ consulting these below. ### CLI Options These options will be at the end of your run command following two dashes e.g. `npm run start:dev -- [options]`. -Options are key-value pairs with the syntax `[key]=[value]` e.g. `port=4242`. Options include: +Options are whitespace-delimited key-value pairs with the syntax `[key]=[value]` e.g. `port=4242`. Options include: - `port`. Specify an integer port for the application. - `loglevel` the log level for the application. Can be `info`, `error`, `warn`, `debug`, or `trace`. diff --git a/client/src/config/globals.js b/client/src/config/globals.js index f4293f5..47b16e9 100644 --- a/client/src/config/globals.js +++ b/client/src/config/globals.js @@ -1,6 +1,8 @@ export const globals = { USER_SIGNATURE_LENGTH: 25, CLOCK_TICK_INTERVAL_MILLIS: 10, + MAX_CUSTOM_ROLE_NAME_LENGTH: 30, + MAX_CUSTOM_ROLE_DESCRIPTION_LENGTH: 500, TOAST_DURATION_DEFAULT: 6, ACCESS_CODE_LENGTH: 6, PLAYER_ID_COOKIE_KEY: 'play-werewolf-anon-id', diff --git a/client/src/images/add.svg b/client/src/images/add.svg new file mode 100644 index 0000000..b84df19 --- /dev/null +++ b/client/src/images/add.svg @@ -0,0 +1 @@ + diff --git a/client/src/images/delete.svg b/client/src/images/delete.svg new file mode 100644 index 0000000..9a8137f --- /dev/null +++ b/client/src/images/delete.svg @@ -0,0 +1,14 @@ + + + + background + + + + + + + Layer 1 + + + diff --git a/client/src/images/info.svg b/client/src/images/info.svg index c997041..a09de5e 100644 --- a/client/src/images/info.svg +++ b/client/src/images/info.svg @@ -1,14 +1 @@ - - - - background - - - - - - - Layer 1 - - - + diff --git a/client/src/images/pencil.svg b/client/src/images/pencil.svg new file mode 100644 index 0000000..355b4f4 --- /dev/null +++ b/client/src/images/pencil.svg @@ -0,0 +1,14 @@ + + + + background + + + + + + + Layer 1 + + + diff --git a/client/src/modules/DeckStateManager.js b/client/src/modules/DeckStateManager.js index cb6376a..b0bf0a9 100644 --- a/client/src/modules/DeckStateManager.js +++ b/client/src/modules/DeckStateManager.js @@ -1,3 +1,7 @@ +import { globals } from "../config/globals.js"; +import {toast} from "./Toast.js"; +import {ModalManager} from "./ModalManager"; + export class DeckStateManager { constructor() { this.deck = null; @@ -15,6 +19,16 @@ export class DeckStateManager { addToCustomRoleOptions(role) { this.customRoleOptions.push(role); + localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(this.customRoleOptions.concat(this.deck.filter(card => card.custom === true)))); + } + + removeFromCustomRoleOptions(name) { + let option = this.customRoleOptions.find((option) => option.role === name); + if (option) { + this.customRoleOptions.splice(this.customRoleOptions.indexOf(option), 1); + localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(this.customRoleOptions.concat(this.deck.filter(card => card.custom === true)))); + toast('"' + name + '" deleted.', 'error', true, true, 3); + } } addCopyOfCard(role) { @@ -54,4 +68,82 @@ export class DeckStateManager { } return total; } + + loadCustomRolesFromCookies() { + let customRoles = localStorage.getItem('play-werewolf-custom-roles'); + if (customRoles !== null && validateCustomRoleCookie(customRoles)) { + this.customRoleOptions = JSON.parse(customRoles); // we know it is valid JSON from the validate function + } + } + + loadCustomRolesFromFile(file, updateRoleListFunction, loadDefaultCardsFn, showIncludedCardsFn) { + let reader = new FileReader(); + reader.onerror = (e) => { + toast(reader.error.message, "error", true, true, 5); + } + reader.onload = (e) => { + let string; + if (typeof e.target.result !== "string") { + string = new TextDecoder("utf-8").decode(e.target.result); + } else { + string = e.target.result; + } + if (validateCustomRoleCookie(string)) { + this.customRoleOptions = JSON.parse(string); // we know it is valid JSON from the validate function + ModalManager.dispelModal("upload-custom-roles-modal", "modal-background"); + toast("Roles imported successfully", "success", true, true, 3); + localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(this.customRoleOptions)); + updateRoleListFunction(this, document.getElementById("deck-select")); + // loadDefaultCardsFn(this); + // showIncludedCardsFn(this); + } else { + toast("Invalid formatting. Make sure you import the file as downloaded from this page.", "error", true, true, 5); + } + } + reader.readAsText(file); + } + + // via https://stackoverflow.com/a/18197341 + downloadCustomRoles(filename, text) { + let element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + element.setAttribute('download', filename); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); + } + +} + +// this is user-supplied, so we should validate it fully +function validateCustomRoleCookie(cookie) { + let valid = false; + if (typeof cookie === "string" && new Blob([cookie]).size <= 1000000) { + try { + let cookieJSON = JSON.parse(cookie); + if (Array.isArray(cookieJSON)) { + for (let entry of cookieJSON) { + if (typeof entry === "object") { + if (typeof entry.role !== "string" || entry.role.length > globals.MAX_CUSTOM_ROLE_NAME_LENGTH + || typeof entry.team !== "string" || (entry.team !== globals.ALIGNMENT.GOOD && entry.team !== globals.ALIGNMENT.EVIL) + || typeof entry.description !== "string" || entry.description.length > globals.MAX_CUSTOM_ROLE_DESCRIPTION_LENGTH + ) { + return false; + } + } else { + return false; + } + } + return true; + } + } catch(e) { + return false; + } + } + + return valid; } diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index 2ee447e..a67208b 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -5,9 +5,12 @@ import { ModalManager } from "./ModalManager.js"; import {XHRUtility} from "./XHRUtility.js"; import {globals} from "../config/globals.js"; import {templates} from "./Templates.js"; +import {defaultCards} from "../config/defaultCards"; export class GameCreationStepManager { constructor(deckManager) { + loadDefaultCards(deckManager); + deckManager.loadCustomRolesFromCookies(); this.step = 1; this.currentGame = new Game(null, null, null, null); this.deckManager = deckManager; @@ -197,6 +200,35 @@ function renderRoleSelectionStep(game, containerId, step, deckManager) { stepContainer.innerHTML += templates.CREATE_GAME_DECK; document.getElementById(containerId).appendChild(stepContainer); + document.querySelector('#custom-roles-export').addEventListener('click', (e) => { + e.preventDefault(); + deckManager.downloadCustomRoles('play-werewolf-custom-roles', JSON.stringify(deckManager.getCurrentCustomRoleOptions())); + }); + + document.querySelector('#custom-roles-import').addEventListener('click', (e) => { + e.preventDefault(); + ModalManager.displayModal("upload-custom-roles-modal", "modal-background", "close-upload-custom-roles-modal-button"); + }); + + document.getElementById("upload-custom-roles-form").onsubmit = (e) => { + e.preventDefault(); + let fileList = document.getElementById("upload-custom-roles").files; + if (fileList.length > 0) { + let file = fileList[0]; + if (file.size > 1000000) { + toast("Your file is too large (max 1MB)", "error", true, true, 5); + return; + } + if (file.type !== "text/plain") { + toast("Your file must be a text file", "error", true, true, 5); + return; + } + + deckManager.loadCustomRolesFromFile(file, updateCustomRoleOptionsList, loadDefaultCards, showIncludedCards); + } else { + toast("You must upload a text file", "error", true, true, 5); + } + } let clickHandler = () => { console.log("fired"); @@ -208,9 +240,9 @@ function renderRoleSelectionStep(game, containerId, step, deckManager) { } }; - //document.getElementById("custom-role-hamburger").addEventListener("click", clickHandler); + document.getElementById("custom-role-hamburger").addEventListener("click", clickHandler); - loadIncludedCards(deckManager); + showIncludedCards(deckManager); loadCustomRoles(deckManager); @@ -354,8 +386,9 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) } // Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. -function loadIncludedCards(deckManager) { - for (let i = 0; i < deckManager.getCurrentDeck().length; i ++) { // each dropdown should include every +function showIncludedCards(deckManager) { + document.querySelectorAll('.compact-card').forEach((el) => { el.remove() }); + for (let i = 0; i < deckManager.getCurrentDeck().length; i ++) { let card = deckManager.getCurrentDeck()[i]; let cardEl = constructCompactDeckBuilderElement(card, deckManager); if (card.team === globals.ALIGNMENT.GOOD) { @@ -370,25 +403,23 @@ function loadIncludedCards(deckManager) { create a widget for it */ function loadCustomRoles(deckManager) { let select = document.getElementById("deck-select"); - addOptionsToList(deckManager.getCurrentCustomRoleOptions(), document.getElementById("deck-select")); - document.getElementById("include-role").addEventListener('click', (e) => { - e.preventDefault(); - if (select.value && select.value.length > 0) { - if (!deckManager.getCard(select.value)) { - deckManager.addToDeck(select.value); - let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(select.value), deckManager); - toast('"' + select.value + '" included.', 'success', true, true, 3); - if (deckManager.getCard(select.value).team === globals.ALIGNMENT.GOOD) { - document.getElementById("deck-good").appendChild(cardEl); - } else { - document.getElementById("deck-evil").appendChild(cardEl); - } - updateCustomRoleOptionsList(deckManager, select); - } else { - toast('"' + select.value + '" already included.', 'error', true, true, 3); - } + addOptionsToList(deckManager, document.getElementById("deck-select")); +} + +function loadDefaultCards(deckManager) { + defaultCards.sort((a, b) => { + if (a.team !== b.team) { + return a.team === globals.ALIGNMENT.GOOD ? 1 : -1; } - }) + return a.role.localeCompare(b.role); + }); + let deck = []; + for (let i = 0; i < defaultCards.length; i ++) { + let card = defaultCards[i]; + card.quantity = 0; + deck.push(card); + } + deckManager.deck = deck; } function constructCompactDeckBuilderElement(card, deckManager) { @@ -453,9 +484,9 @@ function initializeRemainingEventListeners(deckManager) { toast('Your description is too long (max 500 characters).', "error", true); return; } - deckManager.addToCustomRoleOptions({role: name, description: description, team: team}); + deckManager.addToCustomRoleOptions({role: name, description: description, team: team, custom: true}); updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")) - ModalManager.dispelModal("add-role-modal", "add-role-modal-background"); + ModalManager.dispelModal("add-role-modal", "modal-background"); toast("Role Created", "success", true); } else { toast("There is already a role with this name", "error", true, true, 3); @@ -465,7 +496,7 @@ function initializeRemainingEventListeners(deckManager) { "click", () => { ModalManager.displayModal( "add-role-modal", - "add-role-modal-background", + "modal-background", "close-modal-button" ) } @@ -473,11 +504,12 @@ function initializeRemainingEventListeners(deckManager) { } function updateCustomRoleOptionsList(deckManager, selectEl) { - document.querySelectorAll('#deck-select option').forEach(e => e.remove()); - addOptionsToList(deckManager.customRoleOptions, selectEl); + document.querySelectorAll('#deck-select .deck-select-role').forEach(e => e.remove()); + addOptionsToList(deckManager, selectEl); } -function addOptionsToList(options, selectEl) { +function addOptionsToList(deckManager, selectEl) { + let options = deckManager.getCurrentCustomRoleOptions(); options.sort((a, b) => { if (a.team !== b.team) { return a.team === globals.ALIGNMENT.GOOD ? 1 : -1; @@ -485,29 +517,72 @@ function addOptionsToList(options, selectEl) { return a.role.localeCompare(b.role); }); for (let i = 0; i < options.length; i ++) { - let optionEl = document.createElement("option"); + let optionEl = document.createElement("div"); + optionEl.innerHTML = templates.DECK_SELECT_ROLE; + optionEl.classList.add('deck-select-role'); let alignmentClass = options[i].team === globals.ALIGNMENT.GOOD ? globals.ALIGNMENT.GOOD : globals.ALIGNMENT.EVIL optionEl.classList.add(alignmentClass); - optionEl.setAttribute("value", options[i].role); - optionEl.innerText = options[i].role; + optionEl.querySelector('.deck-select-role-name').innerText = options[i].role; selectEl.appendChild(optionEl); } + + addCustomRoleEventListeners(deckManager, selectEl); +} + +function addCustomRoleEventListeners(deckManager, select) { + document.querySelectorAll('.deck-select-role').forEach((role) => { + let name = role.querySelector('.deck-select-role-name').innerText; + role.querySelector('.deck-select-include').addEventListener('click', (e) => { + e.preventDefault(); + if (!deckManager.getCard(name)) { + deckManager.addToDeck(name); + let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(name), deckManager); + toast('"' + name + '" included.', 'success', true, true, 3); + if (deckManager.getCard(name).team === globals.ALIGNMENT.GOOD) { + document.getElementById("deck-good").appendChild(cardEl); + } else { + document.getElementById("deck-evil").appendChild(cardEl); + } + updateCustomRoleOptionsList(deckManager, select); + } else { + toast('"' + select.value + '" already included.', 'error', true, true, 3); + } + }); + + role.querySelector('.deck-select-remove').addEventListener('click', (e) => { + if (confirm("Delete the role '" + name + "'?")) { + e.preventDefault(); + deckManager.removeFromCustomRoleOptions(name); + updateCustomRoleOptionsList(deckManager, select); + } + }) + }); } function updateDeckStatus(deckManager) { document.querySelectorAll('.deck-role').forEach((el) => el.remove()); document.getElementById("deck-count").innerText = deckManager.getDeckSize() + " Players"; - for (let card of deckManager.getCurrentDeck()) { - if (card.quantity > 0) { - let roleEl = document.createElement("div"); - roleEl.classList.add('deck-role'); - if (card.team === globals.ALIGNMENT.GOOD) { - roleEl.classList.add(globals.ALIGNMENT.GOOD); - } else { - roleEl.classList.add(globals.ALIGNMENT.EVIL); + if (deckManager.getDeckSize() === 0) { + let placeholder = document.createElement("div"); + placeholder.setAttribute("id", "deck-list-placeholder"); + placeholder.innerText = "Add a card from the included roles below."; + document.getElementById("deck-list").appendChild(placeholder); + } else { + if (document.getElementById("deck-list-placeholder")) { + document.getElementById("deck-list-placeholder").remove(); + } + for (let card of deckManager.getCurrentDeck()) { + if (card.quantity > 0) { + let roleEl = document.createElement("div"); + roleEl.classList.add('deck-role'); + if (card.team === globals.ALIGNMENT.GOOD) { + roleEl.classList.add(globals.ALIGNMENT.GOOD); + } else { + roleEl.classList.add(globals.ALIGNMENT.EVIL); + } + roleEl.innerText = card.quantity + 'x ' + card.role; + document.getElementById("deck-list").appendChild(roleEl); } - roleEl.innerText = card.quantity + 'x ' + card.role; - document.getElementById("deck-list").appendChild(roleEl); } } } diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index c3b69b3..06e231d 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -227,38 +227,40 @@ export const templates = { CREATE_GAME_DECK: "
    " + "
    " + - "" + + "" + "
    " + "
    " + "
    " + - "" + + "" + "
    " + "
    " + "
    ", CREATE_GAME_CUSTOM_ROLES: '
    ' + - // '' + - // '' + + '' + + '' + '' + - '
    ' + - '' + - '' + - '
    ' + + '
    ' + '' + '
    ', CREATE_GAME_DECK_STATUS: '
    ' + '
    0 Players
    ' + - '
    ' + - '
    ' + + '
    ' + + '
    ', + DECK_SELECT_ROLE: + '
    ' + + '
    ' + + 'include' + + 'info' + + 'edit' + + 'remove' + '
    ' - } diff --git a/client/src/scripts/create.js b/client/src/scripts/create.js index 7c7b458..5ddf3db 100644 --- a/client/src/scripts/create.js +++ b/client/src/scripts/create.js @@ -1,41 +1,14 @@ -import { defaultCards } from "../config/defaultCards.js"; -import { customCards } from "../config/customCards.js"; import { DeckStateManager } from "../modules/DeckStateManager.js"; import { GameCreationStepManager } from "../modules/GameCreationStepManager.js"; import { injectNavbar } from "../modules/Navbar.js"; -import {globals} from "../config/globals"; const create = () => { injectNavbar(); let deckManager = new DeckStateManager(); let gameCreationStepManager = new GameCreationStepManager(deckManager); - loadDefaultCards(deckManager); gameCreationStepManager.renderStep("creation-step-container", 1); } -function loadDefaultCards(deckManager) { - defaultCards.sort((a, b) => { - if (a.team !== b.team) { - return a.team === globals.ALIGNMENT.GOOD ? 1 : -1; - } - return a.role.localeCompare(b.role); - }); - let deck = []; - for (let i = 0; i < defaultCards.length; i ++) { - let card = defaultCards[i]; - card.quantity = 0; - deck.push(card); - } - deckManager.deck = deck; -} - -function loadCustomRoles(deckManager) { - customCards.sort((a, b) => { - return a.role.localeCompare(b.role); - }); - deckManager.customRoleOptions = customCards; -} - if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = create; } else { diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index d8537b0..17957de 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -121,7 +121,7 @@ input, textarea { background-color: transparent; border: 1px solid white; border-radius: 3px; - color: #f7f7f7; + color: #d7d7d7; } a { diff --git a/client/src/styles/create.css b/client/src/styles/create.css index 67aa635..ff28b03 100644 --- a/client/src/styles/create.css +++ b/client/src/styles/create.css @@ -9,8 +9,6 @@ box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4); border-radius: 3px; user-select: none; - max-width: 15em; - min-width: 130px; display: flex; height: 55px; } @@ -28,8 +26,6 @@ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - max-width: 9em; - font-size: 16px; } .selected-card { @@ -83,11 +79,19 @@ width: fit-content; } +#custom-roles-container { + width: 95%; + max-width: 25em; +} + .deck-role { border-radius: 3px; margin: 0.25em 0; padding: 0 5px; font-size: 18px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } #custom-roles-container, #deck-status-container { @@ -101,7 +105,8 @@ } #deck-status-container { - min-width: 15em; + width: 20em; + max-width: 95%; height: 13em; overflow-y: auto; position: relative; @@ -122,6 +127,17 @@ margin-top: 0.5em; } +#deck-list-placeholder { + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + width: 290px; + height: 20px; +} + #custom-role-hamburger .hamburger-inner, #custom-role-hamburger .hamburger-inner::before, #custom-role-hamburger .hamburger-inner::after { background-color: whitesmoke; width: 28px; @@ -189,6 +205,8 @@ margin: 0 auto; justify-content: center; flex-direction: column; + width: 95%; + max-width: 38em; } #deck-container label { @@ -322,6 +340,53 @@ input[type="number"] { #deck-select { margin: 0.5em 1em 1.5em 0; + overflow-y: auto; + height: 15em; +} + +.deck-select-role { + display: flex; + justify-content: space-between; + background-color: black; + align-items: center; + padding: 5px; + margin: 0.25em 0; + border-radius: 3px; + border: 1px solid transparent; + font-size: 16px; +} + +.deck-select-role-name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap +} + +.deck-select-role:hover { + border: 1px solid #d7d7d7; +} + +.deck-select-role-options { + display: flex; + align-items: center; + justify-content: center; +} + +#deck-select img { + height: 20px; + margin: 0 8px; + cursor: pointer; + padding: 5px; + border-radius: 3px; +} + +#deck-select img:nth-child(4) { + height: 18px; +} + +#deck-select img:hover { + filter: brightness(1.5); + background-color: #8080804d; } .dropdown { @@ -367,7 +432,7 @@ input[type="number"] { #game-creation-container { width: 95%; position: relative; - margin-bottom: 2em; + margin-bottom: 4em; } #tracker-container { @@ -378,6 +443,10 @@ input[type="number"] { width: 100%; } +#upload-custom-roles-modal input[type='file'] { + margin: 2em 0; +} + #creation-step-tracker { display: flex; justify-content: center; @@ -466,6 +535,20 @@ input[type="number"] { padding: 10px 15px; font-size: 16px; } + + .deck-select-role-name { + font-size: 13px; + font-weight: bold; + } + + .compact-card .card-role { + max-width: 9em; + font-size: 13px; + } + + .compact-card { + min-width: 130px; + } } @media(min-width: 551px) { @@ -476,4 +559,13 @@ input[type="number"] { #step-1 div { font-size: 25px; } + + .compact-card .card-role { + max-width: 10em; + font-size: 15px; + } + + .compact-card { + min-width: 155px; + } } diff --git a/client/src/views/create.html b/client/src/views/create.html index 290e4fd..db7afe6 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -22,7 +22,7 @@
    - + - - - - - - - - - +

    Create A Game

    From 7ca184cfee73c214c4503fc968aff5993bdf52c2 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Fri, 7 Jan 2022 19:00:38 -0500 Subject: [PATCH 56/60] display custom role info --- client/src/images/roles/custom-role.svg | 107 ++++++++++++++++++ client/src/images/screenshots/deckbuilder.PNG | Bin 0 -> 61433 bytes client/src/modules/GameCreationStepManager.js | 15 ++- client/src/modules/GameStateRenderer.js | 19 ++-- client/src/modules/Templates.js | 8 +- client/src/scripts/game.js | 1 + client/src/styles/create.css | 4 +- client/src/styles/modal.css | 23 ++++ client/src/views/create.html | 8 ++ server/modules/GameManager.js | 2 + server/modules/GameStateCurator.js | 1 + 11 files changed, 175 insertions(+), 13 deletions(-) create mode 100644 client/src/images/roles/custom-role.svg create mode 100644 client/src/images/screenshots/deckbuilder.PNG diff --git a/client/src/images/roles/custom-role.svg b/client/src/images/roles/custom-role.svg new file mode 100644 index 0000000..3184bb5 --- /dev/null +++ b/client/src/images/roles/custom-role.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/client/src/images/screenshots/deckbuilder.PNG b/client/src/images/screenshots/deckbuilder.PNG new file mode 100644 index 0000000000000000000000000000000000000000..f7310b0ba7b03d1803c34990bda73ce1993e6fc2 GIT binary patch literal 61433 zcmd?RcT|&E*Ebv$#X?aT3nEPv1O*fnq=urRbU{F+L`RARh*G4ap$LqlLX;*_0wPVC zQl%tOQRy{Egg^q4&;kTf20v*D*ynHK7a1C3jnZzBYs`qyh(g#`TdBd__8k4#l{Ly(X)S6{6-?c(!mk{ zc$c(|e{G}q{gylDE{6gDGA(Pbb*c8I#Ymrd0W9mMZ`o{-hQC9IUxcp1 zz3c9K{r7J2-z5IEX}j;m_;r-CPsSaV(g454T=0GRbF=ENk2XC$`(#tt{ii$Jb%UQA zh~I2EdGzIz{-bZ95rf?if`@42OiHWI7Fp2*s+X4a#(3aExY!*hE=Qag5 zzTw$tGb%#g%()h2-)j`h2mr)jl8~}%4Ga72^mS{EU$$78uU!GhE!!m5t}5!A#WNTG ze3npEUc1J;P$6$vYiyT$2wl4Zetz)ZcA_$Mwg++1$1L(%WPWxlB^jug?pUiWNgUBs7-S#o(nF+V-(7JT z&Sg_>Bs4EGT{2F5FjofTBGkwmEWD&|q*WpUs++lWG8?tVZ(^y)m9b39{WIlyZFN#V z-y8@ckY`Zl&z#ITr;Q^jpKBIuP1tb7?^~x0x>rDUvhkZ9D?eoVG1n>BR1?f6l4xOy z1do*lr0I5=IT047+x#J^8T9UMz+3F0j!8j zBo$#u%Q!c^(g944Pw0wv)dKpe0s?E}-mO#Ln~=roL?VVr!qE!ht62XIrI)T#U=SlSGN}zl?e3ZQRYARIG|R6FO%cO=vjcH%)u6phh<7 z*R_VP`u1ONODs$k!T}XK#J18PwgQVMQccVYQTh6t*CFPRK@pcK?wj8(35>)uXOz=7 z^d3!pH_KqETVX;4dSpXwq_qF(P8xxH-w1}?|3?>r7dv+W&#eq7BO+pGfph&Hp~H6_dw(dJJFW6BIy_GT6&c8(UDe*Xu)i#S_qcw+eiKtV%VvWV3Mr1 zNjRvfC|2|RGu>(1@hYKU$i=z^qwz66v@40?rORl|4;g25SIrG$PZ+^&`G>F+jphiP88y4uL8Hy7YZmK_`0Kp+xV^tfj))0X3pz4i9sPMKb><8w{6<`O>;ua z9yEa5oYVTPd6tAbbs1Bjc3kjbH;^owS#5jo&!}IfvwQe+!Y3zh5A^%ITa|VvJ-bqplpyqs zRPQ7`C~_sPUZ{d*A9r1dl`nYZ^ebOyA@l)6GSaXIYDjphpX%JkRw`%TTTuwA6J4!{ zp!A~Dh%SbMFYYb(4c5XE@3 zh$dB88U%vv(#|W6Di5jYihYf8oE1hXgYLd0Pm6c#nIXabmojJNHh4bIzp*nhg4;_I zm2iU!OXz#dhr_+lqI*sIBXvzbojIRUa;x&_q56DlqBG)%6aPl_{6&NvvD*MaQg`YC zF98uvM)wOSkSqXzxpB=sKdw&esB5vD^p2Yhs;5L)qxB0*mY{!)TfSOurY8iZcb4taiUC8N)4HTcW2Pr>KtAF|DM0E;jx?QB{+{M1^5%e)|bT6xY zBe=#g0`C;Cm1D%0ai4V2`FIL`EKL3Zo)N5EfvN)CQt&tw-&K^oxj)Z4>GV1IpuF`$ z#t}Kvvb=Vl!q}{W@f_{rPI^KdmX{*8Ib#=HPz1e~h>%TYZjlP5Tf%zO9);xT#@)0N z$hbwB<TjeZxIHzMmd_PNTqV^HEK=t|Idhk|SN>rxMxQh1#hS{(WfT1f}VMs>o!URiz;L z#Bjw^_O2B{Q8UW5%}JCssAn2-flhziW0sffV!##d1Uf86yw6I^svMHrI@d0DUpWSF<+2r+@0(U<>_*8R2eD1UKiWF&J5F_}2e=xl8P;^eH6oGvEls8KxUa|C`dPNma<1=Z>zm40$u;$#OUaa-nH*Iz%D(x5jAQB*3pn$kI{ z{P66a89?!;yksP5k}<|=dw80LU7U547azD+`t*mh@hi$GHJF`K;!~SvjW=*p( z??wIG^<=gI4<9YMT4LXiMO*;@qu;6-m3gsKGBb7jUB}s|h&CsMf~ytR3~`d(&1X_4 z%;TH%3NT_UizjH?3*&RY+RdNV3#LMNgLmP(*3w z9V1GjbDM0iMEXFe{D81a+b5C{Pu2*2TV&Nud!RroU^Pzfr@I}KP2dL-`ubO1L(!TI z0`Hle`OmC&?&~(MjGCjvY9k!?z9v*a+ll=3(b{BB$6)eKdJ?s@<}${{X=(M-9r?ot z$<*F+rdAhl>8Ydr-A_qd`9g*aN0Vf8>sY-D74NipX&R5}&uCuG8OVwJ*;3XD(}1;5 zfn>N)hmxEI4Rh^fdAEp?R!(qnUhywPt+rsqk*xMg4#qjWmjcliH$FLvCSx{nqGyZ+ zJ@qcu5vN{E?W4O^$Qc?&Y%8QxdQHJ~!}M#-k~%s*cT=qlckkbBF5efvH-gKCht(sx zBQ>qz9o^in%@6ySiwV?H0rx_hLfyu%Ix^DRXnJ0x1Bb|x*YFUg*Npo8vFxLVmw3G< zK08Yr7KSI6WIEC7r&g?o}x9WXZcr|NItXt+sgD*Gl_FBl*3d z(j4wfiUv{7>@~M%-lMQgAEenLLXdPSUj`(X3JA#er45*TgkH)Va-Q{5CaXBeHr}t+ z0l3r>(h*L#`38T{T6{Bg!e=}ufscE+yDdI8Bf1F}-RK~WjJq&uhKE3u{R-upcfowZdSo zfr`CeCw-k=ta@^hqRGylp%V1mkmy4UT5 z`a?E~uOIT6nQaA$GJO+s&-(O-YGD%pk&Zwf#)Czf#j~p}E}s)gyZMgj&OV_Zg!(+t!s-BUIqKZKPChfdRObbQW0T zBhnE=I79nm9-po`p>CWHY#RBRO*)^ptI}|FHhVxAoEdm&N6NF5qMZ_c;p3W6nKVlf;-A1Q%LSay2u);Zc_ z2`1oD@3^|a!Tz%#*<|m=OE|IJNlb`Iv7^{p-dIH@UTn6=9^H$>DXQah~FGb z^av{rBRvkHf6eKpfB3QVbY+kSlzKY9yy%u2D>Aq#@-Eshx?Y5kKRdJh3=k8!hM>D< zdbLhk_BiRI0PW}ktkqPs9@w4X`X z9m3U5+~K@TlIGNv!+78kFieKprj8=>u-ET(vn`Cli!70 zxNtuXD%$vvuY=9d@totG6O28ma#K(uRu+>y(s4TYmLuGE2>+DRE67Txw9)5K+xWsx z2QIsGqnb*5;n& zq@bK~NMHM7zd{Gr)dz-l@PP?0aJZK@uBoy4Ui~t zL}r&`qS69#L$m_C#P7Koyi+^NGL=(< zKCXl}6Vy#mQ5VyCgOQferv&#{X6EHqx45LkFLz{TZB@U84ZZM?6`qSTe+D&@KgNFfdIG-?w?({X^h_JJ$vJ`T%nh838k=~nZDOAAw7l3x`YN9^bK3`- z`V_DPHCZw=*+ksgjRR|6Oubg^(LPltBYFo$3?>2Qkt^w_6iW4$RdLreF<-aF8vak| zL-9xdzjKiPw|1gzZC$$0H*C`P=04n3fJJ40__K=S!0=rFz+o9Hc-PF=l=8fd01L3{ z%m#q7@k9QL<7TTA%E^$ajM#O6nUjq=1L4mrG6TbR0|0lDkjnan_|HS%*8_3~?a{ua z)na`^#NSaBEA-Wx5n0^aLBAd^Zv3)yjYTdv)0QN^MmgH9QOGH~Re}EEM$F;0&pTiF z-}^-Yt`h!D2L+{{KR?r36%;EiHqe-X1LRgIaf7SjxmQ=6VDNp7A%=4&@E&6F>Yq)gdmmgDSn zc6`NQw$XU2vHM5Zat%Q}=Xh1a_U|vi`MSbr6SkRFXz5V~z+z3X-z5C$Cwi;*kt7=L z@S(PQ@ZyK-MG2MWS{_g8juKpxFD{vpB}r@qSNrBut0n2UWhdcl?Bk@@46KW_XxUn~ zBS`<&d}qK;84g(7Ayf7bi8%@UcOhS{gKY!0`v6DTuSbuy%T~q~jcNgfEM@*xUQrS` z)ooAbxK1a#kHhXbBAP(l`^j3k3<96a8Qr^lvH`-wcHE^kabx*Yvs7O%Zq@MG0M>sD zAV!vVas~B!D0K{1NKqzuSS#Uyl7Nj8rJX<-x%lhAoRxeS+6@Oz$&cSoJA&clnhL+? zhp_pYdCBWe4w#C3q9Vtf$Z-pv6wQ-@x+vjW{!4kojxpe<7Iv12Cvgg23R^TN6Evb$ zsCE9P<_W%-#p*Q^$<^{>Ll{}_9^K5@0!k)ec*UcLPWOvv1 zQYwd$%m2}G6@rT&l{KdsH}H5)9ZB_psmhhEq?RA@E^_vaFgZG!qU=h-bv99qYWYD! zoqL#Uez7TL_Ds~Z&P`E#9=ikI+ZlR#9Ux`hWD-^fXHTZTapr;G zy)0d&Cig29(Jm{HJpTLrBRdk0GzjnKNKC;|yoXX-)tXSQ16D2!$s-h$yic z;UZqUIvLVLY#zNM0-Lt)LrYa0U%vgBq>nS(@xYUmh6v$lxZFdLM6DS%N=Uq#Gsr58 zht~4n?4dZ{sZwj;+f*TN<&XNMC#TE~>+&W04>= zeHvjw(pT{Z9$L0vMGD{!ung>At0>G% z#!RH$5HgWZAH|Tng^5^NFXM5#VOx;Cdgm^Z$2#EV>Fkd+*6N>?5g?ySvmkTiX3oce zDE3e1n&N#-grIhw)P7NYc?ct=4<6^Yg;Rc{zJo_JW}2^R7=6_yBKJOBYDWl4`DcTs zh1Tj%l+#JYxr^CS?c3=rIu9+UDWntGO!!*?!i5)AG}5~Rl*|L2WIL?JHj5IuQUX|W zrTO%#4ZQ<)=6%X~3)Bx~W!u&|X@ft^niI!=rpM-4ZWrNfwCsD{b|p_Wds5;M428(c z*21>O6d`J390iH1Uy2|LtJ^Y`V<&TSF27vl~TlQ0^(YD*|h)ZSlxc9};ly&MG=sh{uy*2oT7 zc&mkCc`u>G=>i;&KI&lf5Sl59w#o6_CB;UCm74A`RaB|P1Nmcf7bI^r1rmO`f?;%P zBzIB;qkFOru8JMNlwCz?D@n?yRKV|E(V$GpZ#4ZX3$T6idz=v4lJZtGihPP}GK~aY zy5&zgqARg(XojDTCzqt=Q77~t@~6OmzW(lCz+q)_POYAt-9*%d*kM$*QB;vw=efxAJ zfQ9ZF-uZ1zLvn)hmI!{fn327^!r4ie;%v#ss@InjvYTW1ypNMyJ!w3Mn>^kRYo!=E zB{-SepZ960__WZHo_O>^o5vtVBYsMG>w}1yvk!^W2P;x#gKy2ihQwz3=J5jICm$bK zeDCrV-b?xVjut|)ll1D6TW5p}V26dclABwznR7fD+=j=)Ndlb82?SkoSQJKo7Sza& zo-#CS(x!<3>gEavFzvG&o&zeWV)7`a!hEYaUQU-)yjy@P-;#}QO>Y-!h_o#a!GHuv3!+9vVs@Qcyg)(cbKl_3=}vn zZ8ooU5SyG+Iqy+(F2021s><8*v3k*N4_(kYmeERqt-e7#&u3+N#$J$&#`+{r=MYtZ z!eOj>oj|bg@tDy0FuQ^!8g_3$5QplFx>7Zp zG-rYcFEwR}gIOyOrtxkx0oatD1Is$8HvJ^sngiZCW->LfnWzzeUWsGFe;_Nvm?6 z0c5n^4z`zwglT>lO;sSrc?iP!KLR`{6PY@W#B5MWhCn-NuBftI7v5+YoT+H)?kGt+ zc`LheF-^I$(ctcbANJ@Z&wHImxI4lgEoGn8Ev=j0!@JiRN*T<}5kyMr6RCZ&g6|PJ<$LMJa8@9}Hr#3+t{W%I5&}zB`GU@SQ+*3%GsP zN58GYD*Zuo!Dn6$KguV04?OaAhXG&)JJT*cT(M6Kh@*Fk0pFdczdObMGvN9EU$|a~ zM2g~c`L$3kuBffZoTUk{crtq(Z9YYqd+_rgxk0-jnff)STy-9^Uk)`8V3mp{Csn8gK< zKe=oCwBdIwx0W*?xBYMGX#Q)GvTm;_;|aSmC>n9SRPNXNf6_K{2T1q?{5fC&r*>=n z!~Go0?i7|mL|E9tnYWds>Tb`#SaaUxCCfSv#T8Ge&{~_xy@9jjk=Hb2R zWY=DS_nTJiyi>Kd+&a?gSHtG3`(-*tbm7a}z%PR`4)@lNldrfQM22e>_x+op?a$7m#?yrNMMA2YLksU7==bIRG1+;?29Hj}y zK(?VByrQ-Zpo$cByllE`f?GV%29*z4aCncu4{yH-F9y;O9FeROjxjRSzwOh+B}uNN zQsjqitk9{hWubD;kf+(?Sbx3@b7gJ;Cz=cTM(~mBs0j78`8KY&ZbFH`RXMx*sN{;ZhAIw8l*H+TX9qlP76 zR-XbvKdr~?km1g*1DYmLbHRVs_*b_!S===t8f)~8i@_m9v%Q_2C!f-Z@*w09w_+B_ zPNgKarYE!yD$(fJaqNb zyYf~~$tcekcJVLtTj{_hvt?G90p$CYQmp!$ z??(#rK+h!2G311}ktT*N0^=7bGuCB{$EQg#CiQZ-zVJ*@j1R8rdX+^3z@6 z)ViC-UAJdxR3dbYr<_WJR?A*PHw49P68tOy?f#20O0412ZFyQV2*)dKF%_cGbP9Uu z*gdl{Sc(jGs#fM>9$r)EGV)T-!7krgICFN_2a&tdDb`P#9uu7Qs%XyAlU_;2%;1G(0(Fo&ByHCwdfeNYEaFwd z5Ud9BF}NbV)V1Xf<0hs37(W9?xjJ%^Aq;1xRvFD0A$&jFpuLU>zi26=N^0Wd49E8( zE}Kim&jR&wFN_FXdKvP=UPi{N*uu(QWgT4vv3Y19ULZ%GY?W%li(azr-{P4yOSikd z^1<;O_9yApuD1|RFgmTgp7@YTY+eUi^@m)GZVF$Q)S2Y zo}{h^ytnL2()icRF8yg{KFQ$ta*ZOyrIi9-XAgsP>E7*Ev{gmbMeW3O8(s%l6hOaU zOlz(iyXSzBY4b-9A%#Z6?%7g<6qK2_%n&DHk+kB0N}gJB7J6A>A_j8pb1rtyCd*HK zKJ!C_a_8wAN4b0?Of8v9FyD$|rxtk+`K!kxz8T_&r~qXdX7rWlK0{Sfmdde8yEd=R zoD#gJB;S7R#yRY9kY+}|#B6kE<^W|UJtzqu+Ag-n*pl?XaCdWiS$I^`Q6#Ukhf;vN`PQmm|J3U;-ToEn^b&K zhsnZ`khkujWYI28-7Kf~5E-H&&ClWZ($KFUtDV9t9pN;1WR0b`c1|DLNuRWl3{xCr z^+5<>a`Qnr=5_?SEa=L!jGQHVGt#j5M9z=O^je1_am6w?+0KCX@Z+(VH4}5y9iA(L zl0C8O09B#k(6h|#VgiG77Fj-#UeSldlEH4)?i&Z6^kzEcC(t8uI2$|pQSne(#mX(= z9a=Lc*eB_y%@yfRyS!RZhXM&Rs-lG=m`|LbhPPcxRArj0t9%SU4+-mqkDS{`J8e1h zd4no2r)tbX*R!qHpj@I#y1jQdz1rbOoqY=vavKsz-?Do4-GhHg=JB)*N;L#n+^b0J z{Y6P^7i7Zbs+0VIUHj-jR)cP5LcSY7X3P@9sYaz!CT>$&gg?TJPBl7ioOYEn{LF}4 zXO@JddCQWDhm!XnACIGS;3ysAtOvdEz!q6?6m_eq2^^Ku9$2}JmMjLZ`;bLW_wGbR}$%QQlEayVvyN*#)IbBGqiFa#+u*^ zA>dk^1T_D*I2>Dg))&K40H~OFv<94(6>FRxBr&`XK1<)mDd|lD(Z6iv+_|`P%2-g` zm>1BYhS=ew?k1SV99)QAX=1WxR48- z25v>Hks~N!R$%Ex!bMljh1hS?vX#j0i74}Y)Y)912U_{bDHU>Sj8YXint=nq3TPr? znpA~{UO~P%fZ@^S>bwcWvF#$4K0-;o;r^w3J$uo+kFiY>>|?O?xlPG{=*FP2xGM3U z9)GhV11#7CXzTg5EZ=Y4mXrZiu&j$mg`&*QO!2=2`1%(rK_7En%_p_>HIn=SUjp-o zmNQ)E)1`iT>#eiOyJtIhW8>${EsMr80n}_mv(+oQqi(=#@%vK&+d2y+LDX`eP_*>a zO#oMvaaMu38)gF=Q$T4q2Q*R5%{XE(d#C#^BP+)o=(7YRK;>3gPgzW7xZ-^6Qq5)w zsI?>vXHp^hW;leqc17~0Tw!+&asO^`@M@9G&Y7Sg!ayeB`t@zxSu~?AxA!Cm^H0_Y z`00@h*SL?Gw_4aroT*61?wRc?+kjqp8KJnyIzb32@U_D1sEq+XjD;6YuqSdsWige* za}R&M^X-?ediVx_@ATa2lNnWUB8z#d;@Ki=v1R5%C1hT0@HXmk@yhc|x$VaDghHD~ zGRxe3Fv1D7^r+}H{PACz7@!Y7(=Wl5co?*AehtvSzjb=*AGY@I89>FpXPhB$#^6w5 z>hXNfozxAV!iM|&KD*zGd4HTjyw`PxbXHuPV6@)iS=YTg$rm5$4-n(Dk8WyuByRWF z1jA}26@>@p+zqb8pjIztkhn;298p#W^i>eg1h5@ZXPmd!>*N7OIVo`FqfJHF<#(y7 zDvtJ1^@^eW^ImNo!Bqt+bfZ|+HDE$ea=U)bb#uNrpQX*=BnE_?_xF8jNbTxOBeI|HFu(t;$xpBAdi zIOHDZIz4OafQg~GN_7yf35TO>Fbv|9D-To~@jZtH-^)W1{i3Yk*Z!f?PkV$TBLE4& z=XCQG!XIWPUeLDgaQbSFHT)DD(lqLut&y3dgG$-V;Tg1_WTY3qX9*qXqdDaBF(7b5 z)9pSD@}AG`@iC{4`;GlCBgnAQQXl)CZzC>xJ1JA`=jnGR6$N^hs8A=ed)n-yhs9}-A-#e4nQU_S1h;6g6nABz5n7p&E*3>%MDcvdE zt_{2|w3<(L5HV!XeCN3nR&dMzm}cJ3>j9@9m7ab5nA=^ZR=v%yJ;^?4RdpCmITU4B zi6r7E3nAM$K}@oil81Px)CI^&XOn2>sGWrg^c);LZFCvN ziYsC0jveF?%?{EQqilD_k(?qJp#z0O5i{M>9Br09Ox{Ni5Zyq$ zm5z$%LqN|XT2wC?y^d}0Cjx71l+JG<%CoRGMUOs(y z-+QlYOp+L7A7MoC{3d^`k=xcWpAc)ON`#$X%-DCuW_+xieQ3&|A@B#1BL%bi&{b)E z9X`ix`43fTzW({gR|udFcxTcKq3GUiM;Y2>=NfyXFz4=?{hg^$MX)+8W%;^>1?E+i zO-RI8^}xN=`hk@n>J0(QZa1$%OH75K2y}_1z>4Fu6h#ZZoRJ0m;#ReoJ{S7Uv`JZ7 za%GzwA^HYM{qpmHOnBaC&5>7q0?P!Pv?(y?=9H;(a-U zL;7?e5&d#iCVN+eRAo|@ad{JxlugY^22m*@%Ap1grqD>1-$m0zFpPLefq$(-baX5H za>RuhST)8PLVmWy+tRLrW!$eobL!2Np~_s0WRefwuW`B}ogZXJn1X))n=Geg%>hgx z_xoCg{J%W!*D|#L*del$wZjZnG&P3bdfV!TfEfCeoi}*@iw&Og>wkPs3I81!)5~A{Lo8^! zJZJ@1Co0aOo@aKkW@baAIIg=*Rj74ynWT+|V>mdgaUSl6sPZrk$_VXh#L+e!^Di)E z*ZHUmzCx!2G8|T75qY^)@B6i2-|wuZPEGQW>9R;ixME}8cvBECIXq(QsOwS2$lsUV z_x9>Kz>nTP;%(;B%Qii)cGKBG^kALzAHUWpP=>=4=#|Nkx=ndka7nN9Re^UkqRVf$ z^+t{f_yGkDSQ6#ss&8BG`8+eu{m=^dxg0|Jz31dRP4n#qe3^wvl@pa|QXJk;QTE%n4PlNWbWR+! zr72UGhIfr#L7-EcsQC!}e>-*P5%s@C>NP{xwGz4vf1gSq*0JOlFQQJ8OD#k#cmazB z(c0WSbwyxZ#Sk(%;8c`E?wNVI;i;qp-7tPZH$NrB3F1ZZ!5E>fW z$hQkB3je4O(f=HD2h0jYLm7=Kf%oVi)&BOGNOCEsoj;0F*k{SOTWyO|@~c8}J` zv#t_UFmGa_Zbf0IO|08w$BrY|`Fqq+7f>_9PN_2-AXA^?vo5Kg8r3EXE2{JrItKku z@Pt}`dfD!MYvJz0809jovcag0yP`27Ga?TAoN5PLWBXYL#N;=O9{tFP@~6 z)aB$T9&%-TN0inn+OQ5)31T}GG^`dx+e8p{sB<1g)xip>E4m|Ol7pxZev`4D2_N3e zeG=B*zpkT+=SLa}pe(Zpe$z)PJ1e^x3#ntb8+8b_jM6p4S!Ux%fXmxPd3h3; z9AO=i!!C|#<|V+v@1v&pc^)}!Zx=iY;9oO)+YeBo=Naxn>2)bK%tNyQBS6e2VdRIb zXnG~aX$buuOEupJ7j(FW;4bp2{#hk42O61`WLGxyyg6%>R1nSY6rLdH?O1vWhPG_` zIx9^9@qg?gif>(bwE@Omq2lYJ9-mKG=_yoqs>{XZHyR&z`b)AaMw9T{bNu<pCremG`L3iuR;PWNpq3Iom#vao6UC->M z_SC`uagUVtb|GDX_!}tqt-HXTMgf)YoHlURkBQk%v<%bEkzp-}5_O6YkuP)bb%{RP zWm=8Dy z`7x}~fQ;;F9M|NXP?=*Qp}6a)l6oIWh*`IUV6)UDe&SNXMBx2Pk^yTsilry_Q3y_3c>e}Tak&%D@K{3{|f<=926?%RLD_jaqJt}fV_UcKnkhX+x0 zZf8O4fD&arwQypNvnry^50NqvyM^0i zVSlQQ6-iYAZ(~Fsh8FroTSp$Wx+H!`c5EBT`GF<6&7_#}nI`dE10FK$EN1jV|0e;r&-Gew_ZBw=+5~Uj4Y^ZY(YMG*c z1l??yImF3|f{oY>oS?cHU7wc>AwEymqkc^eA&SqCYiu#cFWs-{66H%N{=_Zoh1zZM zUJb~|n=%uPh*Dl|6DA{*Fy|8@6~F5{RL4QbGAkjXAe`-IulnH3!j|SVA!e}~j}~2+ z?Kb3XAIc=S-{{jl`vLzKmzZ0mlVStU;nq{47>63BLkN6W@;|OFO|t~MoaC~6hERjl z#b^bPBF_oc`8kW>Sh^d>s>4)+GCy@PWKMIgcI%RLg{h_ki}Ap3Q>VQCAoJbFN3l-b zNNkfcYV##(FJf-1(c}Z59KGLgHl`9>7Lb{yNxtp>3pVJ=mYGcuRWsth+M2`hv$xS^ z9V(>%-~+1V^&1bpQ(}IO(}R4mrmucY_hlauANoyms4{L5*Sxj7-GAFof6upjr%#bV ziqYj+di)I;;S)oj3jl~v%fn<;;S}i2P{{`PvFr)%OtP4i6;wv`N7rPaf;=px#3hN05h!V;^=3{ZT_a}?3t-UTsU02ob_vK$H zBIWlK@!vcFG*SE{bS(v)f<$4Oqmsm`{5tAJ^75?Q06o=5y*X=15(oVUADVSkL$x7= zK48Tq{W$Rr6)|2b@I5Q{n%W&?D^3OL&3t;r^ziRnC1YmkY?8TNzh7)jlJHXsyLSre9T^+B`Dp0RJAW`A( zF_;Vzzb2EjgWm7q*L2^f4v7*g+E?K@N}C*z=^mI7Kg%9k`A(KwdiwK5*uQ52h?)0N zMC~vW^Cj)|JH&|Z(99hSv)2XKHLBud6dot_VmZKwHqU#2_w-{v6=}u_wO+tSoL&p( zlD(~k;mU0>xP1cr$EWPG_=Bj&sGW4D%ST#TdVk;}MzE{BdeqyM>a_2&%w5Au3F(?` znbc*-@{VZ#@91(#Q&*%RdItUNcFxTga4=C>r3@JZ{z>gmPV#S*l<5asvvIk0o)-A8XaI_e4;T)0ZBKWeNw4Z3@-XO&R&8s3lSh*_(nAb}!a!TF6m z#vcCVLLDPbxgK}wW|*V+(NsspkaJhka#Bj>^ow-3`GZmfE!H-3Bvzj)!QE=+OJYtH zkxuvCL&GAh`F5w-zh)BoOk`kF9a_vs7Ub5ug2AW8ZTwyc#h}2mr76z|5Eil1!l}Nz z$@$+g2mlyvFg90<{#Qb$);6?6eRIK)^U2hR#c0oA>zEf6qV`=OY37L%5~!%?cUkm| zalsRY?zFu!9`D% z^vjhGhMz2fxFz15K3(@e`^&$v|I?!-N66q!I<;b)a71x6JPGzNdLeYjH=mHu^;QWm z0`qXcst+h1oas;2b8`AOdG|N_kp*z~nZ2|?#k_Th&HguW_BUStFPz=qC-@hJ@E->G zGuzl_=w8z|l}S=kQyxr2*2f#R+0=fkI%cXEBlq9* zue~STP0GV^z#+!V19vd{X+AkW{&mms>#wVW-ewtm*nh^sd?}haaQYN{6-`6(DaJ9$#>AS`lX8!g$W}`cf7rV!CjE8sbd-N9i z3%4iUt+R()V*xsDW!(A%89o*6xS3-C4*5&H>96E&hSx=NFY~=)hZ9QQko6Spn;!CQ zcj?)5+MKkuHiWv;q;ETatkyaKdeil>#-(o1;Cw)>{7MBLXuFYMdsF_R8tut`?Q91% zKhHbbyzlp|DJ)|_&XvNOPoNh))gp?@z`vB0?o(AhaiXw`;oNa(-7Q6Ft*msn_MVXo zLg-T#eJYYdne{3J??0>+j6$TY0<*e}1~{SxBUB zZ1YLq;!!i_jd1YGt<(8mBdNeluI5yvw1^Z(OW?*z2%Fm%N4xF#iHL_?NA7S}{q*&k7z<2Ba@ggP*rp z+rPl$$9QI|@FY0>bDa6JXN@9%(3_nfMT=T!=@v>}pJ|C1Cp9E>WQf3m643yD6O};2 z$mSOP$pJFgPBPW56Q5Gjh0WLh!6~IBi~k|S<8$rb!y4?fywi#ptzx>>Ec|W zv}dxSmrxXSg!c zFB!9nYvsCZf_A&9RUx_Mo|mmKi)V-iKRps=w0sEKo>25SVy!>p2A=>r?B z%=hW@hRk(^$oX9?=FCM?xV*l(F+$L%e8^xC*7@CZ&&;Bxq5L>#Td7l+Vc|E z%&M+F+}igvzj?Y1>uP9gzOTS%k0v=YikZ!1`AI5EpG@C)Fkum*v3>y4YA=4oI7WlK z0p0$)*}XdPK>RcdQe2t5FjrqDKGEn(Je-hF`eC@6wR&X=S!p9toSX9=?%lqd-n6AY zf-1LZmN4oE+%GQmPC<;jc=$1_{wbqw>=w`nQfN^KJHIgTU-y4hl|+TH<7cUvy|W4g z>C|^=oG13Gh%)xx#HZmLMS`6vp)4~76T%=S<+orBR$G=GWb}iEWin9~$B-Ep0fWFVj^o{1i zU8rs0orSUQ!8OgUMhEX&<@sGr4V2eAaaR&ri8rn3-qv}W@)l0^vMz^eMD_-?v4M>I z5o1p7acBVa3<9Ax_syWlOdq6>TbI|59sD@oX$8|ez(hwAjMT%!V$QZn+P3^{g zz4r`ja@*F1u^`fQu@C_Pu>evP1f&b7h*YUkg9;)b1W{T7iAuA8pp-}l>0PN(k_ad* zG^Ip{5K(%7Kp=#Kl<#4k&Dv}2y}v)-kMq6PIWK?Y!el=4nRCoB?lI;V_b}!pqZ;>s z(o*zQNiQeuh@q4`m?~G`PlKImmbf2HPVHV#{w@L#cO3vgqj0rEmK_l3)yHuksEbSr5z@jPsmW+ zZrJubWc%{x+9rttqcA&x%N!4eTF?*6F*_A2UGJj>u}KZlJjqch)PTB5NbJK=9e`o2%P1?FA2} zWvInok@vXJRk|#HYeUBiD-q?A@TW`V;MWbZZNxoKWp|&Dp$a^~|0D1Uwv5fp+&r6) z60d)o@3f0e=-)V-6|Y|>+l%c^+0#u);TJxW*>$$(uZ{eCzV^ADAW{6IVX< zM!ZEx9_OI$EN}g01Kgr!}%F^R~03%9tZ%#JB!(>bUP*1awrzz=3B3$6#>&-Z5ggd@TN0Vg1UIK>F1| zEHYFqtuG-g+!yw97TV+)ZrbA?r1ub8xbZz(X=_pn(W1ibZI!(fF7n>XSWdztT62g6RzpOzSr$y9BZAF>X;IT0Be zKEZhEjSij=6+E=xTX0hEYI6`C;t3NR#(Q&taDM2~3DMIDuy(ZDpU4eSya_*5j{IS7*4!&(NPAQ`wg9iDqAr?VNJje<|YSx#l|Bp zR#{xw{}@D>-$n?{f0Ph+$!QN8@7t^6g#*&C|8hXocTO`WaGDWh+_K@K?MI2z;F8rW zWx7rj$_{m%kKbNjlR(~zMp3i)I+=m1L>_bVb#NO(Enn|P6dl;}g=&CB0QiUQiG@nb zZXAqon@0t=>4RScBjF(r^=UFs2DhigVE|Z}+r>%0st@mHyJ!J`m8MdB2PD4P$L6Lg zln8_zSNMl0(57biOH`Icj90i>3l}=Kxo2Bv+c5E_R;{z2{)9LQX~ddYy`W2c$@Jbv zc53LqH~a~ugYW1GLTPD4GlAn_LY0JKRs2D{S&Qu0Vg$CV z9dTq!@hc8@!9eY_xy`&5Gu|Be3mpu9T)7P-!_)Y7_0{*C{r7JNRG%56Fxv@#7a5JL z>DIPzFH6y1*=KdTd*tJ~7Br z0AG>H&v2f9@0MhEog#V>e1F#&n{mxlRX*t z@%r-D>gh;RZwDeTmMB7+yGp(+JrgF_li0pBXH4$Bt=t&sdlzLB8byCDro0v%UjVzY z7&g;qj7tk9U=N z<<8rG0Fn+C%3*S#leX=5Xsm|lFr5L#)Z(_4$hCC_Hjl51iVeO>&Z(sukOl^?S>fj4Ap9wh_Mi}i@Okxg0@1VQL_U!E&;=s-E*z4v5yh0hbL zdI{CME7rt-B6&1_liVQ?u#zmDKhuU(uHBRpKB~zHc<8&5et&f5B<%rC(%WHhssYMs zlK#GTuoV?n5$k%0i!MB$P}Tlng%hhY%ehFVHgTYSB)|d{HRx$RKdi9I1o8k!|MPvGCM|Igpxq4rsY>Yp`*KjLb&&rVwR zth7j%wq;7P@?*sWv#K0Q3$y)2NXUUcHLKPgy(+gWw$Hx;OP+HPa5*lQ;FY83O+j8R zL6@Qt992H!?xbn~*(CL%excW(E=mM>den`nUskZ$cAI&7MRDx(K7sWaA(B%sEmjg* z5ISa@dSR_{5b9H~lA+LTv~-yC7`Cg}QvdXf0l&@n4cAhx&ro}ZTIs?*JCi&vWMAKu z=Vx`O{zS8N_)qFa36R1Yf%M$4P!0YGDwKHDVAw0v&wx+a*2vzZfl+%lh)6(vxC=8m`5V}K1CaKpCgr$F#0}K0TAMk!i5E>xIj8LXb z3+3NKCD|70q=7H;WuJ2^ycMdKxM%%|7Q++HbNmtW^DgF?1Ovz?0RWf(V;!&m&H;Dmc8QpswV(<-eb_aM7-uo&%L3f=P9?9 z35#lHh_gC>$qk<0QHzXM0B*z4%-Gj!_^z(kNxLr^EWbGqdUyKvp@9{tlJsD!!U1P+ zYT^07u2}YEs}h#p;v-Y^TK;);eA1g0cA5_^zpXcW>M!F8H~gWJI{=mJ!Wfme9eUXv zOVCT{C{(qw3r$QK@q68Lu1sNlwu5_yJK}iuTZd95-80roHwb7&{3+fV{;t)u*-zTQ z0YvWZws7ztBlZI$Ixy^TiWTbOi5-@MEAJQ8Qp-ojEHia|k+CjeRvj;|!S$Xc@aHt_ zBKUubSmk0vu8KuZxk*s>Ma+5pu@cmLeg^!0$QDz(BrDWZs^NeR$)!B`3%5njbV}_P zw!mfOz_mTXA}!*Yh9HsE_ro#f2d0maVtijkzliT1CX8(2i{&*W1)N5UTy71@&-~JI zC-E}1RgQ?iioWjTzM;CP5)(Bm=X8>c$Vd=|==5y(@Ldh`n9nydOV5Bt(dgCb`H+rt z8t`Y1*r@c^Lwi0w`F8_Q2x~SGsoZQ2acFlH*0PA2jp}s&St}+x7(FY8KP`aBOyc}C zw_RlThtT510O1Q{av4`)euFVKW4e4f=56p2leU^L0&k0}Nk@ zNMS>X!-1D~6T30ySFOVXvL4^ooThtpx^EQYh6$<4?}B1t?XwY(Gp)XpI{W+4Au&n3 zgH@i_Jj%w+!|`myx+592A4q`tD|hiGth}z)=QGk}d%AyDas3q#);o$+>YN#>(vuvj zT26?5rj;LbE8WymKcs{1-z6qL(VH1<8(7ui?HM) z)W=8N{ zD3lqucVFOxcEb3MS(VHqd`&Utrp_e#5>}dAG^b-g`<_btr#Br{?TyA;d}qfWg@0U{8GP5NL#a=uIjXt9}{te zgbd`s{wxAweT)~o!r=D@8khpmKp`w$x?SmgWT;b@#5>mxSGa+cOQA#qf&roLXPAZ& zuRvix`1Sbl_IJBLC*!z>11d^#wN#&OOQPRPX-wSarg~+20`}$SuHPp7&&9Dtj~Q0N z^BSKCn8t_K1fSFazhSZo0!giB<_4T4()9L$pQ6N-t0Xch?}zz~vx^23z=Q$|AVLQU z4v^RjId2f+Lc8>-ny-X!0{#&wPX3g=JN>_c85JzvqD3yGYK(j243Zq!+PBaXfmLN@ zE_J?KT;W@9z4};AH>G5Bl2i%QUtNL07F%5K>)8$2^W75ui8f~2-=O zs-RqGNww1Lk@yMR{vr=^dAkf5#^Xh69ZQ{pjBRDM06%JLHxkj|0L0_iH~<6gG76rh zZ3<(vSF^PAZhtvkQpUNL{%GWD@sp4TH87q`2dN8(6G(3~nyZarTPpzws0B6weuwO; z_q5ETL)`;b1%TN?#Ubo;cEZ9UK1Db^-vAkmg2_H|K#XrTVZLU;@fo9uRU=Eu#xI7o zy>%uZP)5*+N|SQQxhB5>7e4e|mHiiIvI>8v=OR&&JfSV3q~C|f4Lny$Cr;SH-L_Jh z`{Z%w@7^ahZbCl55Tb0}cUVH0kACLPY)_^Cl)xQdSSF+F$|>%7{N8vy4kda6lsc-{2&Ey zJ?wS&Dm+$#ZEF`w{m=1xmbS?_Q{fvLcoOUxkE6UtMB4HY+lI+5l-pncw)MC_8F&jzoy1dxU@3Uwri8d@%7y?F`NzcEzxkuQw{T(bv&;K8OZ%63PZH)MivUEh- zt|Y9zusMo$WCt1OK7#e^D%7GsEO8fLiESBb{x;3EfDKG^9EG-%o1^I(R^eZaH9+h? zr_VA&t#sDknEJVhrVU8NB!HlZ;r^`~j^Ndp(T0CGOFk*p=Q!L0&W5Lqk3O3#=QJC5 zi%UQo_K0luM9Nuo@zkvds~<}&?3nIjW8|_VOv1M=UHY{rRWlfo#E+9STYnjfI2nQ) z&OpVl7nIICtyNrWfYMaOthBrlrXOY>@$jy2vmJNbmDu|uJN^qh-~)P+ov0!uB5Fe{ zwCXe=G^qQM;2r5${=KWk`vRC`S#ADn=~XX)xD?l@?D1@^+lcY#^n6qHInEarJ^9AD zK0nBw3R+Teb**Xurwukc>6t*l2NgI#pvbw%s$f|&-5MdoX z-)*wyNbotAC|2P!p8^8~`}=k=8Sr17R`$ZZc{ZlHQYw{tCjwR1Thb4w#Csu(0Li=Sy|{e8Nn;Hb zO+Y#PWuqZ1n1q%B#n9ak*{#c4Pc@zi%(^$Fb2(U2Pscw!A^Te)k0crs{Ci|rP39!z zW!Gx$E569E4N5lXS z%W4_2Q+O}nLuApN$=+RhC+g0qA38O?@A{vmXMg*8 zM*?0xS!jRz21oG^DA`y8K1-_&y$2B?)hJ{~Omb284HG=1-5+jEHXPxT7h(K)Af0%#4WJ6SyFOYu)X zbSv{-{CyX}|K%?2`l!6%FUqyRK&CWYe|s zspA3x%->jP^!2Kxgz=k~QNwH({boF=}20R7iN28r9ysP(cjZKSQ`BaXmaH@f4gkAGIo}Kbae1f z@={WPXkH;u^=Ww_T>kWrP9dO}y;q|04?5J6R7kg-C%|({0fJdO^gx$5|4I#X=rj<+ z_%Bv6;FpU@f8!RqZm`R?_W${d)>pe&B>S=%FmT6=Zbb4u6|$`iq}C@x*$Q@=a_v&N zQTCz~j_o;TFvtPn-<=-7LYXZdw!vMRf$PzG^z)z8b6V55y@wAO)|2>iw*JD#`Y5x4 zzE5VSh0HEntDC1Bi|zSNTd-X=*KcEg8wy}(3i>~1m}d{W&=sQf9<;qURODF70^#1a z@Es>90UcJv`pjbAi<$2RDFd(m_L8Yx(P09UC=G-D3A*9f)~3?TG8}i(X&fe_uB+Jw z)jJpO8;b4qByX(uYF=vu-3eF3rpm21;xlAlz&0(aXDY;uw^2&=dgoLr;pXKgedu3^ zuheb{_ksEo;hxX`||Agkimtoth@E+;xuoIcAz#DwWGyv`-iZrSF87ryYzRG^c zaL+)!qR^A&RK^SS=^t8n3qKGl+biYF3@mlPnN_=_(PXlsS_N&IbWRF#Yl-L3b|M<(Y?aYE%La6QntcqDz9=_8G|7jdF;Scd&6udjHht}@3Y z5%sKQuE&~Pbv+2b(qtuBP}4#gX_+<0zsP_i6fF1#6n-pt-E8c=Js&8C*DTjv&x2oM zX!q6Mqc!zuS5oBU>C~GLG1n@}&9S?<%?*!s0uT2<;xk`i;*sS(WUj=N-4jsKWt!TEg>3EgH6?(a8$~- zpwHZ&WT(@8DJZ4B>U%Uu+^TkJaV{SS=G?KWe~RyJO(66WPbc zei9$t+8(YxLr^Dt#pgQWM~_c~E6AWS$};B60xtRF-`D;$CsoQVd`O}?+#eqOqksiA zn8Ki6|I+;A%Nf;n>4LMOC?8tRi5bro0;F!y;kxpfMoeSH@ab(5sk2F)EDPRnS75F_n zsEtR(o-mkO-As<5oFg)<2$L1FI`?nDrlYX=5{zwD7ZlA1ddFBq7G=NIneyEll9+CX zhu-joR+NX#;b*o0!qUD&)J^QeH+FuRCn=~rRP!JV9sFZEpptnE=}LS6mj|;3(3nl3 z4bQE10usXogJs4-{97 z&AVhc+h+j2{+CL$-bj#$)KQvdA|*)t^cotPvr;OAef_k-lxU3YRV{hZhg(R=Pu%T2 z%7QC?HV=P5ohgqb5d18E=O~5_DKEP+!9-$o4}&k1$}jsJ_QCK-<>;QZGLrAaNtPb^ zH3vGc!I-0mTD^vZBR1hwBkE?fk4I9P=NPkH_9?6A9?Ph%SG}++iXha}z+XCJb4ZLl zZ{_g8>s1DU9pg6&-Wd7)jbBsYj1%?1P`#0lMSO^srC<=bj|6T4*fwVQng1JguU<@crL+WH>>BZ$)!Sfxi;tH}u#Pxd};( zSQX@{QOjL@X(*c^26b6^7wHzyx7CUUwS3`E=+qKNT-RI;#X`%@Zdp(GFdyFw?d>fJ z+m=9DhW-Fb<2Tgq%C186ZIpx@@sB~JEW&_IHatd%q`r-NiyvAKY=DV{keN|!e&*W+ z*t`Z7YriBw%Qk7Q^pKxo$V9`8Q1fJO@R~tLr=G=H1Bk<;h6T}qOt3bZBuEjy6f7$t zcq%L_t14C6~`u8kz*K|O}`#45nMu^C;sdxV@z$cnDL7ZrheGs?1dk!$M@{<&gcP**8)0*lU7ek>#$?)`CZ~qoBqWBcH#%?#(2cdpR<08Q(EO{ z%1T2-PSq?{dY{eJ;oN@ikxtuhJTcy66kn{k659d&(T(5G;=Kg+0dI#jrQD0iZxSNR z&!=JEmbCK?s`;|-xbVxrW*1GtJb@36nxr@Asq7xf5ETx(8x_g zqePAebzsf;MOXUDrd-$)1GFd5N~RE6y3e%>iz6;?k5o_T4O3`vz1N%d+cRy@Va>pK zRV=M+KqK7b5UGm0{yLh7EHWTH@D`)hX^go=x3Ur;cqcR;752uQrv`sazWH=W1v>G^ zrM4@`IBzu4htNkXLC^hwQm0v^c*X|{MAR<{zr=!Io@6CgJsWchN`RGr`RD!@V~Xoc z-8wOdMDHdcp{hu!j1v|Q>Jfvanxc)w0n)&~t>AS76rW`pHW3=5{Q(|x&kLvMG#O&c zcZ_xEB{Q9dhA{?kGe)>7vkf{#-FE2ajTj8puzlS2(YnFA6^Bv%zKs@qzF{$61S~;o zH7(7a-CSvZDcpYXVugk5jgaYw%!UH8e8Yqur3AD@&k_B?bEnL8tUlyGxK zYC0R1$(5%$90Z7`Y{W+b<>y=@$a_*L^s!T48DhN+Z2@Xui3+QGIYfr6v^a?Cyj99qn_CYG&+HH9L_SmNfO zVr|j=OP&!}n$|h#-!{5AkrJ&s+`Zkr0`>e$qOwKd$ey7ez6coc8ucyJwQo)*8|BhU znoWZVkjT#tfFXf?=op#;5YDdMY0vowQU31=(}3R29jtuvZQ+HT^ph+;Z_P^(NqY_@ zohm-6=JX;Jc?jY>?rL+>f@AV{PAFkxmb~x1`o{PDR|R{}cOmJ5N^c~fE_RBR*+m93 z3*5geB^_wJPGMuYH9ssq^lh!fKyQ$+z-@pNcGk+8@5Ta#8K$c69<15o)3{=PwhEia z3Okg6%nBuI0tsFblrsM^gAZICvzIz+82rVX{Zz!bCck|bq zDGBg((*l3Ikos6gK~gL6OEG=|^JHD17wM!YIg5Gk!3q8B4}-X>O`tz(dIHLVolfm& zBu#J2Mg}vTQxv~u7c}U@T@TH+<2)uQ<}GgqtZ_LOy3^~>`RYQWmq3zS*Pl$%XCPhJ zDLZyOrKbwc3RMU6EjSFpGC_wTfwGR%4N&##L+Z`VXOBe(UfkX;sh%GboHQGwhZ-VV zq>h#j_vZ5&A$ZKMB(=P1#e2xT<3O=X7BF0vX2Mt8G&Z4<;077pa5RC>l zaYCofW?npt9G^o zXwho?=u#Jd{REEknb6`hqQ#`j!gU@;WBD-B(v!(XWZe+y^G_1c4i!$s^pwfOY;jT_ zxRg?6VgXxqVK-;#fxGY5#cO4cW*3Y;VDym!=A;AYAZ-3U1l-D?uK`w^k+Rid*=3JQ z8zSa5yhz}`1XQ5>SN{dmFNUx~pO88At9QmWrzG;1PYa?d*9~DO4y9$?Qz>^b6D8LK zBX0tIZL1Jblv1jV+c9!Gu_xuEtUsJYtY&zTv$C)}Shx$nlKoBdc#*+YZ~<2}%bxuw zeEU`J$YZN0R5XV@CVHRNOskgd>X77?gllqMlQav#-EuKxytH{(jn+=q+5`#jpUaVE z{cj-ofRY=YAYUg>VojQkm~PixOn=)WZhP0!LMavcWJOK8`D3%cJb!4w@wW# zSE5YlUb_Sc;q)Z@eI={dk#)NjmHN&HQrnEH0yKDU<{F~*RDEBbgaf5tv+*+c1>am# z3?zbU3O+paM09+?cMC1ig&APq;{fN6FAja;yZvc@kWMTpYEBw&z3r7^i+e!tGIz+e zI4~bbNFeei+GF0NFL_PnGAFg4W1Cs8T3X)wbYZI!DQ1I@wMV`NA>$>w>Lp6ClXLwk zw!|&U(X%+R@thNB{`TU7-!K)M5IQoPYbDu#+<*+dw=zo^^oa8zJ-+&tu;+NSXOY;IW_%)U# z6?zlIVJUV{$XVtAc0!(e6o0&CWsqP^RQkd?Aib~BermN<)V);`Xl0QT6FL?8H3!-m z_=f*U`N|%O1<(Fo;}@$mbiwR7!*;S|_=A0#$n3@?L}Lvq)xsClZfN4(DqwFMp{wyz zSSoMDMsm_AX8b^%MBawBMTu|7&ugmL5WV+(OkD`BvcW+qZ*xMv48K0g+UN_IF-)|c zTOVB>AWoDI;QVTqudJd1`u7Qiq962h^VZ;JDf|jxRHu?+mV6(DkoX(KiR^=4peU`@ z`?QXa!M3o(1$YGh5-YuhBZB6-!6`g}$zG;xL3h%q{a8w#eOHbLVm*OB zxSqIOoBt}!{tyiNfW_^<#i)W+v{$)KI>ozYJI3=Rq%qMB@I$W!ZaT-@7UK!*I#2i_ z&9MOq{4l&e|Ix4V96yIgU2O2z;|FWIzwGsH;zZnGK+-syny@#B*%YzqcOXck+cpwQ z3ulb!e<0U?;jV?G52GimTRC|dnr~7v#K(hF;$4fTkU}lQ#ws2Xmb?e7ctjD zDz(~6u0ite0gvZLNOi|QKqavocSJR}2p>K6qHdaV%};`qmP!)}BhrL41%a1)ibZZ| zURYc$j3^LN6hwaSYbCiB^Q4UATCzmt!C9AyeR&yM5AsFbHNi3+c=5SVVSsN8@A^JuLO&eks zD1O7k+W_4b5zCrXJ~+Du+lS1a?B->iilJz3`C6SCEn|*k7wKW|`Ot#KHjS0?@Fp3L zEH}a?7~&c`WV3(AONOXBZO}uT){*;Asp@YA_8Tg#LiU`^*;^pQ7np!;y`FvcENjtY zV>N_9RA|*+z(6swkO_ze!xSR6MLw^zaiG=GnDcw9anz-=WJN(s78aBC(QC8{Th<`i z5>H)0H27Y-g{vL*U2iZPm;;+EHnk144?nD9F}n}zfbVMb^%l*Y!&mTio!NliY~7w! zKX^syl)2xV93n%p6+O$E^t|h>LsNoo1H4<0+f5Icac5Dx&O zEaJ1oQ!-}5DG=(Z#lCe+si|cvI`K+h6$r|*0GV?u6csJ_a)Yfu z^m@4cJ^FD<5`T72S#wT75xf4m!}}x7dK!2ro0f(Ou0Thq&eVUo}DkLCovIas4^kd(lRSmjRCay)T-lHyTj&r~hssvoEiQ<$UCD#Mh zb;5OV(3?{W$b!M;YK5Y)+2!be37y4+KDQ}t;$wQOY5 za{s5NigSNg10-H*n$X6=7o%e(0cIaLAVPm)9?6~G{>f$k-`^lhc;dpCk7+UjdqdO- zXub8k^2Nipi|>Sp=>N+&!_bOL_ zZ!mjxL+_865^8@o-w?+YpwR5~UOQ_CgwlWSJ2Ccw-Hg?L0EkzpsRr7;3sU zb!sYjz0%%nGaY~6Y5zV8axC))kUX$wWxQNMm zA}Ba6$pJy&`$G;U0LXONu*)L(_SMV=^G(9pmeADm!KtoN6%-vgQTy|SNRS9Ml3uy( zAcegLaSAWm_LwMN-EQ1*mDqBikzqM^4^m44I7{LJSObCl%qxxGHIcz&`DwWa41uFc zmDiwp(mRoAwud`w7N@f`!KCYtvG`xOcX?1Squ9xmh?oe;c|DLEHYo~$?U@FAyHnc{ zp&NAz+n^61lt|XVkq*y_H&f$y`u#WYUub8kJc}5JN%b3UNzT>KVlee+U z_0e=m?$PcQw{I;${w%x3@N3xgg=Ck9=#~$7iMNxvRYmYy75A1E^HT&vZsnKTrlc=Z zP(=+Mcon&VIt-wGyq#9$~e+>i1&S z8-~>uW;hCR_|?HiU+dX~>>;r7y;tR9b$V3c=gdrEs(2c(Yi!}-k`gi^&~NLI2!3&! zDOdK*IcyKf$7v?5xgMg^vOQWjnNK7IY9=isJnfz3^Lk3%JD$f?6LYxk#3h}X zp4&*@fAUldLs$t&1uAf0^|Nw%KwH6pY+M8yEpt4Va_X|bDOXTbcYSopj5{HZh?q#3 z@zl=U=Oz&%? z5udy%>fivQ-263D*2*cu(C8N6Zv-dmOwZBMw{W#^U$737AK0h@;DYbD6u{xL|Fa?y zFSviQ%xX@{x79IpEp>3606j`Vml|;&`!0*Ip0#c&ryNnbumTFczZmAyR>cxt$onmz zsZiUn3mhKOaBMjKE%y?>R{q@U+`wgBxK=Yyp9KI(>z3CA2&=9jdBWy)-s!J%n7K6i z>bsC^@y1s{C71hg`C}-wGd%G!sa(7t&2n(?YpmT1Ol%Ff9DH8rDT(&!!sNtAva*DR z=f?dWB2bfFK1iUJ1lFg$Pq!Odn>n@_tB$}(PJ3Q>e?91n!n4jVzQZTQ4RFoh#|0{> z*@$nIZrja%h}QuDt;}XGfOeYk20iLkA^t$4qjk;@;c!<2oOp-I3}BcgRhJF7plykv z>)>QnY~7J989iVkJKaHL(sX|#k>_TABSozGaVKRN2~pSkhOi?-M@bO_-@-w&ld~Jk zeIc(;=uCSy*My%lfQ^%w9!I?52QZ=O=P?un0emZ|uIFt`15KK3IDjKqCrPau82gX* zv@&T~QDJC%XtuVRLLf>Ja+IXRifMSjO>M1qiJ2a6{(%o)QTYcqJHkFHF=&=aV zjC#{7Mg^_K47qQ+(`$PeVEPggiJpxTF;px_O7yM3fu?zw#tc{2cMb`Q1glT@o29mS_n62G0l8 zjKheGo4=5q+rGmDW8u)VDvec;ZDgnrmGBL%=n-i!#>F}uq%`SSX{3}my6sBBH_JWz zdGZaI(vH9!VmO8qeS%8x2EK6b)%tEAGw$>NgfIbdB1K*T6v~CrYK_n#eT5>t`=sU= zqm2;|M^#LgoHR~2W_|*=yU(!zVAthDP?x%TLbDVd#nlqO%-dG*o^`sB_13bAT)vI; zY=aIN5EPodGne=c!f!QVO6i`&qHy!|xPij{?Xrr1ZJ>b!xq(L8I{b|K@R9SzCtX2V z!*$v1;0hC@4|MEvx8;(C%y!m|QwY7%HWton`>x7T0L%T#yh>~YdR*EGU9{IXglQ~G z=fEeTwo&Rt(Se??Ndgj%sbRM4cZu$uQxO4{a4^lijiP8S7U=I-BOiVxUgj!n1vgNY zBuslB788tN1Wttx0X+|8iwRnQv(69tLkyBje3kLWTaJulu*3yQ*C_Uh2mgWWTzDe< zM#@h4$A6uar_Zae2zx-@4)LV^z!&IzDa(|r>z`G`BpatL-|=Z}shCrfYWGM@Q`e5q zg^nP#I@LLlok4e+JEJzsOe<8hE$p5ReCdE97e}dkXV)F2w@ykZD{qhYZ6Z|9LkZk) zc&IPhtPro!>DwcNz@O^G^X@!&2M7Z{|G&b{dNTR!JdJ;_5U#^1Oh^PaCPTNI!X_{L-JXkUH5RFp@kYfy5WDmlbv)HbuYx2h#I+I#kj?2JIcZ;JMf zDHd_c&#gESWnn!ske<=Py_OdYbbnZ4pJTvpDqWFwo6As_$*^^^oMPZNvz2Bdhn1~{ zZRwLMWnRTWzWKD7nlQ^eo(m72W`xeV<#a`Nh!Zj$!V4U%fo=*RMC-*L2y4gC3;Hs9Mh+NH$A)PI{1EeJfy4pg^>k)?_6{ zVvxL?#ATv-XNDqoR?~h{IZedsi^`uH@>(#(D<=zYVZc0g2K@arZ(?vsw588HBh>0k zt-VM;E?303FPC!Nw6HgsSKFkA!;A!dJ50P4oi1g_q>j#C|dUG)Fws@oR9l5iAH=6!=Z3fnhC zyys5if#Qkbemf!_s9~}l;OPSuiwNI9nn)>XN(%HhVU)!jPfYV4Rhsee#QY#VKu#5Q zAMlnqNR1f1+CC!fF&gJ}q)ge){_0f;X^%J#_fp5sSWK|-1?vdCCY2 z1DL%^L0DK!?EyGP1y+2$r<%#yzyy^LztrjKihkyOrk`2Bcvf6{-*eV;AJy0oX@R!@;H_3Bw*A@~}84MQueCch~r zj)3)kk&ssBiVF$K6T;Q5i;f0JVzQI_QrkvORvyre66e0}!xSrZm1gD#d z8~S24wPt(@uCsLH7ZavNTF(W23@bTWce&I@EY|d~ZN0JxU-7)gUuJn?nu7>H37{4; zh;~?5FEsp6tc^_R*y-C!3klKDpBDqmlH$A&oHe-odN0KJe%%v&_F?h@jtwD(QXHcz z$(-0?^1|e|%bnBTLyexyq!pC=A!4)E-)EG~q1_NR0Ao3C@xU?N3YWzz6|ZvpI9hI2 zb}zpy3W=uwK+4f`taYaC1pRG<8wZt6Hp$0|SG6@O+RM^y%)Jp`1V*pTyipT|k9B?x z`vT6dr@k|7DmPp-nmFZYSB{7_dD;DNLbM5}j7E`I$!pdyx!a0%9@j0@ap&&6IPday zRA;k(8tP~+2};HsAX6XkUG*g&gNu{Z&pli`oO!sGU!t3#hT@?lFyN}6*F zrAoq6&qJ$dzQgzzX$`a|@ddUO76@ue*+^}wcVo2(+2#A`kwgYHeMDC^mQ#`c-OBdl zbdb?ONj?7L^cDdtm01t1*5sKB#h-I}lSCxZ5C2rCsXfmVBwBz@!rRE}f8qhHKHv|`1oGE6zmfD())dgmHAkrJ*~sG+M*JCpmlPfVb`s$o9z3cvDVaPfVS33klB55VO*iG3=6{KN{{^qku`@ z`BH&v?{bRpXgL1W_mzW^({ndJ`dUF>v6}1qeG%i1;Q=bz@myPDFC!xCI^X`=8kORs z>6cEP>-$ZB#Q2c{^p?nKGVTc+6sb0pDrO62ewN+fdB=mb=N)b`8TNFBUy9}*LhLUN zJP$8#!*L4ZibdLJX|t)!dew%s!~55cG$(+*$nJ|Nf3ZWPNg9u{8nrLzdyZ4`}}353Ib`4x?KZvmP=uuh&2E9eM~P z^J5FjIZQ2J)NkK`3s-YwHUu&;<4t?APuT|V{tn|GPF5fKp`;SJNc&M8VfMB5;xYmi zn|m)h9Dnj@LUz&fI;$#-fn`Twzt-_U-a=;&Ek4C~v(z)H2C#)cZGdvJEsK{bogW%R z@|%wv;_JFJ@gfV{1qw2~g5|)brc@!mc#F*qOnC59^)1r_8J)^OX{^RPCC%AvnF~fB zsMx*qn(e)pK;;G~npk@wXiRa$7Bb%~zKAkjbmZ@f#OS5WhIY0jygug~Gn#sCI>wyO zG{YakQ@$EuS?#HJBmRy7lYa8+?@3x!OMT1>+df7AQLs%30J*H7w8TtmKDva93af(7Cg?GKW600D zcUf#yrbSo!<(NClc|;sBx%^AfE2G||bVIc-l71sP4Bpj5>+8gy-Y18>HgOMKR82Mz zth7DaBRvhpZb?6Bg4gH}qr=8kY3mBo@j$heeyAVpffN~D% zDIf{zBl4Dw&S-?P9xvF}nvmaxD!=ot05E|fuNlp6A(DRKtNMtKLzsb-8UFOh;x`Ao z`{_1uV8oq?M0ka)Xaun*;8@0Q>Hj7Tat5$f7^I7`*Cq!0037|I|AxP*qwTLD^n2In z1b9Un@!Eu;?MY8QoTPw2yVXb-FSh&RqL{Mo2X1)VUWT?;__CU$9!wH+ea5W?bzqD> zslGq>8`^FFe%((J1SDvUL_@M7EJ za@(HLk?la5;(Vd{!@`|9^Qpe({H7Jfk*8cKVucob=R<<8Q?R$A3rnd!!y6*&2W#B2 zqRA!eyiF#h{o``HbL=`U&qNE@2^M3~Auld>={?9w!j|GgA{9^8FDz0jZk!wupj?~S zB3oV0-TNNmVq1oJmR0YV9ACG{7<|x%3@okAnKj-YZ0jdW7@0d?p4=nZTC+qBB+>%d z!mCRpFN`Hs{sa7tc*QG=K1^>w_1w&zc{p~g*<`VQ{8V!CqDs#Z6ORcI!OGzvNVWuX zDJP6TTAYAiM5?=?Y+?s-#iy8j8~86rO47eek$;Uq%6hx&Ot-}WCgrfc>u4&ZWVEw! zRINm`tS7ut!~B4^c5`uOn&g0B|{_AEZXzO{p&2r|baIeM=liFTFXhn&$dorw{IRqb?<} zzk#1Trx|j<==6ee;P`oAC*Rb`eUWs7*u=*pPWh+;7ZIn;Yk_9bH)#yx&lfbHX^MGs z-CY5g4e=Axd7h>D5c+vr-69hcYorHc;9qw8Aa#B;NpyM=N+f@WEF9g|Wm$RIh+=OI;!|Fe|pUe+cLd|5pVmXQ0~z(8Yf`OgaoS>bme3)%oA~ zKO+|`{P;zRBf~CP`H(LCE-CRv#ayu&=*T7Xm;Jfi6`WUI>RMVBW0LCOm~E+UkGkKP z8Bo-hNjbH57)iNN$Z@3J4u{QTmqdSiGWTGhM! z=BC_qwG;;O;9{53+voQK>SfbwY=kp48y&9L+?ZVuxYB)DeY_`=m}RR^bq-F4w}0(&etI zELcQ=X<=i~^T09P3UijPBdpA+D)~XF{JN2I$j-->XO#SagB#l5(Bu9*edg@}Ty^-E zzXriau8C=0wvq&PKHsklAC8Ki7AoaH(*J!T(V+olz(jtg2RJN=6nFT0HLmo#>23RW z4SY*PTcPg%0$8w1^2Yn z5P)2yP-YhTRn+hd?(F=DPM`G0)3F+1JBims*8e>=g zeohwecM%74sQU;Wr9!L3S5O+n0;cw>^0ih(&KIGoZeqL+b@o%o*9Dgdt6SsjRFLao zTfrgl#-=d=ik+skXVIT~G3SeLRj=PK1R91oZZULy-}b8Hd4G^X57hhx5&+xAEG>0V zT!&;lToo&7L-~FD{Qs2q9zacPVYsM@f;3SP5fCscpdcV!T4)L)AibBMQlulj1uPUr z5u^$TQ97XnsZt}N5V~}!K}o1WAe0bDxtmiw>N)q#d-LAhduLu|9Gz{l*INHtfBXKj zBr)FpWU#|CE5D+hMw3GPUHMuQMbqRZwD&^sLj*O3bEOpBcCal`yDgytF+|v3Yd9OT zBv<0`ZDr@)Z5PA+KwSW@C(6hqoOFOGgHLNSZq68;7*UelK(GL=83SS1o8K>lzDAwm ze`xc`c%)r+cmc`+LV7qgOduQ14tUtBJ50#6DK%|Df9Ju_Tb#Y=$qmnqfOka3K@QG> zIY#VvdLCv{f;|!$cfMzI86Ly$o}>n(Vr_(*#pZg1_{Z`I0EGK1D>re4lRg~9KYcwO z2U9~W-T^EPts1Ool=wu=QT)uupA^VpqWe)iqZ&%|zJuA6mNa@cNsW& z=Fb_;SE)JTz%I^B4~cAvu!j#oAFm0m<@zx2JXu{ zO#F0|iMi*>rEP`Ou#~jWkpr^ha@-5R+fzG*#H}s#p?wSW!VF4%~pYvZxPza7psZzX|gvwVy{b zx^D!jdQ>s_gLlW|hbn{ZD6)mPa}ugeF0A%b%Zo1qN0`N{1l`6~Ys*8D()>EDIF5z# z0=noI^$)0}!n?1|8s_Whz8hPTwFa3+P01GxH(iPcmMPBv?h!EqAEVWq@M215p;fbU zh;n&e9+A#(JdCwfL-`}VH7sFnDrRA-m+ThrQuBPywe=<~;q-7NI}Wo;o42n0%CKiK z?EEBDsdn_*1;jhxeCiXg*4}OJ5W(Vy4-MEKl$q#c1R<}$d+SZy8-Xy_RB&6j*J$|H zVd4vp-{}}TbrmK0Ao{C~sFN5kn#~^|sd()CVD$RokCgkrs%b!5c>tPG^bF?Q;$dR$ zou9ccWP@K7ymJ817n^ChxmOp5)ela`i#XObkiW4bG)M2R z8QA#w6+8%PG8HL`N{b6?vT(Sb;=AvYX=-lB;=Uw>NCWjg`8uKOLJ*tc5Ap(U<>M+K zOcEYl?G@A$EHWv1QU1VDllWRw{M|PN4uern=hpzb2Y{x@t-^X%G(E-L24F)qu>Fa~ zH@p7@AKIKTPBO$c1@r1`%+q|z30`fyuVmXG(^Z*8I$c=&@_Uk+H4m8Tt|0; zlmMu;)Q5=Y%!fAwD(b;W3tk1C*pQ5h|E46V^@)7F;0Q~yLEBmos9%a$gT&$j>ima@LT`@aLt?81o3E_L~o7=p&zzS zorzW519&e9KzTs}q)2D`B4>rWhWpx#Y7MYjDH7IG@iB(uzB!4yi5B3!pHIbka6~Zv z__-@v3i>*;j1jrjIo|#1K9TUwB=^J}=7H~xy1-9vytBeufTcPv+~Bh+1tvUy@rUhN z9o^~3S(x{RA@Lz(w$9^~0l1ILOBlup>?sH)9-UeCL#r0OGg$Z4ReSozjmWUqj;`2Fn>HRsx zzHdE0bW+K;8?+ZD3$Q^3Ck%nP1}R?r6Y;mgF`a4u`&)ZBKZ|aWDIy8FYm*{_4q?3j z2mY0?yRxfd<3Exgl4XP!g6-R3{IR&gq-3-joF=j4E#rv+C;_~vc;rS@pH}CgS_>YW@AJr{LMq_F?aTJ1xAnY_=HvyaWhXS|hQ7=S$&&Gbfa!OX!+z#A zVq?zEtU-7|S*n3aqlsXYaL+2I$V+jV0@o~~MA*3`wuDDh2HwjRM~LLy5NT9uQc-UC zmI%j{_tKL|2GE=65hxL8=Rl76o7QjaBJ5Qu#J5NE)dZ?ETf(ahl8-z8iJ~h7rZAP% zsdc!F+%R5t6W7xf+C$=Qb`%$l=X%94Tw6e}E-CB26gQP#y>yXRvK=Q};=0*!#~Br? zK`d5qK}gI9yRj>s0zh+8j~^f=pHMO77(S|35mez*P)Hw@Ml|qk7EASg6og!p zN-Z;s*j@tKWnK3cAMEu=f#dI4L!yRbKJcjy{vp?@nM9OExQ=s=#6kRP1=VE6Ud7R~ z4)dvosx2!l;cHU`l1kuKmP&I#cgk&Vj`&UQSjXfS!diKQ>R> z9t*)0s|{9~g%@ZG@^imf5UkXG6<`WPNUHh+x;0bpT}wAInGhQ4cI&_Fl;GSo0vIVD z6KfyPv<9kWC4g=~2*n+_$2;+|c2WKYPCFCU2AQX<#oP2zEMi!{TTw=)(sVd~f^4^e z^zA6g%9W(CF=FxpS;HSYfD}Qx03FjIKh0$bqDpz6*4W>G$$6#Z7{#!*(+GqufS>}L z(Bl@U+Ob6F*zSx)ebH==qqwVu79|e3#@9JRqBz_ym3ze{FuHZ zJ$@IurbLurQoPn$wDejRL!x# zHv6HU`v|7y#%S4)fep{WeEg>Pjs@G82wI5E-Z00tYroSF$Z0Hy?dYHnH2$meW*|_y z>Rn}YbNJ0dxa5LcU*l_EH=BTT1>*?#{-^fyUDG)#OFNb6hg!A#L2zUwX?FNEomH)03fPZDxUP?JoLp5@(9QV z)ggl^vhOg}+LT=u_giV}SHRs#E!H^j@+dk>tyhf0nwP1WC7GZaN>mgNDNQqt$a*62 zus%~)>8!Bxcta<>(OrBs7HCUbY7r${!Ll}B{dkcuf}uO7-aw2zn5|JtHEX@O-q}6> z^f~eC_(nl~=iUz8%!@-_GRoZ4EFr=h7Kxkfbc)V=B#vf4{_OwwLva2z>w}HU6{Suo zWAxnOzUH5ca5}vb7}MgE)}kR7wz3URvNr-e%bc;LMmbAenf-c{?Ff+#-5!wQJ0Nib z1%RNqh!+2$lL*3ui95d(;%2FQu@MN8GPg>98E7T-viyf81t4oS{O09t4oca#Ss~-c zH?3fZ{5e*ePw|sHtI}O$d!*Qb=stQVpODlRDKFtq>A01aVdi$zB~4|Zk{sK+TdJxP>fOf0#F=T~Xml}EZ9l>O7( zEO+Q-1W!4Pvcq5KI>xt|Pr`~7*9l7VhqMy_g27qz5jGmoY*tf>tbpcOAbrh>h5ilyC} zRQxrE%!6~kHdq^2^9mwNf&z_`)hBX0a#Ka4diCwiJs|c;VEUt7FuaU}VTbczkYV+b zbm8f$YtC&2SfJZj&L@FfV#D-{c&L&8A4*Z_XGKI+4SiCu4`SnT;3b3Rxvd)&D;z_y zgB9IqU0?RB+g$h=b-EC?ZF0Gyh9E!Bi*-YH*HIbYe+#=k&v^~e4w+>!&#XIhDj8X( z7Rl0ixwsCtC~_6RH6EH=giS*d5Tce&%py^;)bhq!^qI?9bwV&8A(r|JSQ+|u{>_eM zPAOl&5sIvQPI-B(4FqB%>Zp?`0(ip-Z-6hB)(K8qn7+c8KItIv63oI;RxdRb=E zp%qy8EAo2azor`E(GKK{4DijIqQ z2e8$9KyGgCFF;X<&(pZr$3Pm)g@2Mm^2!0n))GKL0W?Xu@TYJc8;E!EEau!dp!>*; zPk)=d0NM|ryFi-xF(55PPw=Ph0bJx7&bjtw;7#U#;;G7FqkreGh$jIk#++cJj83P~Alq9mRnSRuK>r;kK4&UU zhA##c!ppRmgnDX+Emjo#4`B`KJH;m}R7G6Mi)Ehvn+EVU5Hcv)%EL`BN;uAY$6~(w znsJ4d-%Lik=dnE;D_;8UZo>p`&q~mF^@|Zv7$-YhEqNb>*grN2C&ea>v!W_*#Wa=38MN~s?eq9|76z?y0y(9@o_8C(`x3F=rT7j^L zON~zU-)b7j$>~_Xr4X6UYk@pb|8XFL@Qe~$E};cxp?w#YHq1EulO{M07BmIugodrO zi@GozpjZIP*&H@7&AU*e4`APgpWD2o|D_u zQD+6ahQbBI({i!jcQ7uhp(_8ebk$HwtIZo+0n(t9Lap(qBK55Z$Tn*D*2r|#NWBXJ zS@M<{Q8zMU};82M;*!HB>rn0vJ*KCD0)e!<|7L87OS z{@{U~>yj^yMu&%=c!J)(7}mO*N$7gD`F*E&BvY||Dg#Pc)%(!XwKBH5l}T{vO&x_b zvWY~2D^6sNSX1D&*8;<&uT=oJd5;E4H^&Bg9RFZtX0dSJd4P2k${(?T!e+L37+XHV zg_gLmj2RiS7c0&swfSCB4V^oO5imgPnQ4T3n@0QM0gmMvZH|sZ-n)f@#QWz>LK0+l z#@h1^e$!Dy;X=YmR~Lu&tX6PoCWEpU5HLC?Z*~kXBf@mTC4@l5+%o_C{+?@VW8=(; z9KB1LZ?Bv+?}bE|i2~A2Q8N$~mX~i<+V{*QSaov|@I-ugO?3~Qkp=SDUT_1<&gJOH z?9y{e{lVgQx+uHBDsKBt)e|6eLS+<*ztD6~Chooq65Q7gsL$B%w%1U)w!LpX7j}C2 zC&t}Sy4~8L>w}E?;UT(*iBATLbb)(3pjL`(_c1_TnucH5S&Tp&)cCo}W2Jl7qf3|j zWWOJT?<_`KP;CWmA`%8Aokl^HKCg2QIPrE z+p_ zDI@EOWFcZHetY!fO)vb91G4uNnAshPq5q&9@N|!`u6aS507uou7Uds^17!Fr>Ii@W z15ZJK8t69qk5dFZ-P`)NssFVs;3+_Uu(V<04&cd8whbBXzYe4|1lZ95u{q%B(QBwS zhyPj;@N{qKU$p=05CKpB7cR1d$xIqUy}B|{%OzP^?~n7AZn&>&;b`GOr4X#!T?23K zh|AvrEefbupQLj|<%9bymj0lx+mDkeL^FH?rR2Y&t76Dl*`F!_yOB2+xUdA(->C>b z4@>up3|VANzqR}aiCX_AQKh);Xk3^wLoL~B<+~%6!|-*gPo(5wJr6SD&SIgt``0NqEB?im#;=>W%(uxqLDI82GIIPeDELwBj$VV__Aagg@AqN>OPDN#Du_ZV&X6)m<8;f$FDE{q z$ZM+XdC1Z4bswI|mkWNYIcyBuR~JDc*Z&NT7+kH<>?%}#I2VdI_b2g%QOlS zzV?e+OWI3re9E;wzIot2XRboseUr+;0)G#fdcpO`!u846lrRkm0N-nXCk?vcHMdf;<$ueTcTIm5Tt9}W2A9Q-SdIq|RR zx47efR447*XYdecj|O~{Z|t>x13uU3|2MwGr!E%JN#@cLUmhEN%xa45Vb{W!yI7tb zz%Q8k+Yv`N+Ny^wws)bmp+RG{C46a{X?xl6OTbkTd4ZN9=&&=8Oc$@03hPNBgh|tK z%-HZ@9AUT4iZUx7xSBG;J#L~6Yq|spT{G56neyVDtJazVLv50$#*FXn_M4*H{UO4} z6SK#|L~2&RaT;iFoIG{=Mk!^@Ew$%eta*wQGAhVpjS*cCJKaJlMh&Hm>Do!grX4}1Yt2oOUPD4-a4r$+gJ=4~e<8-$6q>t5Eq>%+O| zI<%5fii}CdT*ix$9w;9n8`>1?`kef&q2VODm+cgUY_;+dipYFCd2;)DY1l6C#|pYD zB1=iR%TXu0(mc836-rE&csz@?NVa6jn!BS=7iN8OtEB0rjl)($NFY`J9lC~q9fm2T zEH$Z8zS^(L2cAjc)*?s^2@5Nt6C3~lad0R|ffm*MmLvbNCfiIdyQgG=<_jZ6(sBOo9$G3NS z37GLynmferIodMu?siJ7jXO_*FKR*Vo&t^#_VFoVCnC9X$_Plh#sBBtX{n2WtpPtZuI5ewu1p= zGA3*z@)BAq*3vgDCW>TgM?`i*NlUjcnQ0)~G-@yd=MrYuB*gDwZoex=D-4%zul0;{ z-6Os2{E7>f-I5Ppf-7vVyqF+%(&5L3pAk7)&^WA8F*z8hepU7dxLK#d1Vz2kW7-v} z+mTO&Gh`ahVUuUJQ>ewwN+*wUF%$LglJH%2u1MY+vsRnhq3S0km8k?T?8u@$IDUyA zRT`o!c5#kcD`!TXrt^>t`P#UBjqs2RckB;aRav!aQmDY!`$F%=DPYp4$ulw-5_`}t zO)V!??!D>eBjz^Tn*Rv)#R?b1ciIYij3_o6fJLdu6K?n%3exwo10Th0Q||_}ifa zFtpgXAg?@TyHVDr0>Acl{D8*BoTRVk6%yei??y|ChSyRo!c=%>PeuaV!YXwV?-JSM z42++~9#_+&{N|1m^f@1{i-jU?hr*`i{5wU>OL4fR(Kh=3D3{ zsiIo=x=gua^`gKWd_)1;Pm&ubGP^gziqBqMVIV%}ojO8z+LYcz1Swf`*w~a)xoUSx z!0uQtLaSQn5KdZ7@y*WCx--Nr3<^@Z($vZjDT;h|NlQX#7fzapw;E>I_wXs-R7#Pt z5K!^8?{mhURFFK0Z^7>PV+@SnS>`s!K_rY2b?DVEL!GNdLqV=o5IVDs=h7%ksjzLP z$BMk4RPawF5#p_<;+xO#~B=Hqyy^ka0b`uHH2Tc4lB0NyeyouabuIJ z0GTTq%d+1vmY7sRUA;Bph>9)sLTGJC1t5a7vpGOYB+oZ^!d+3@fX&8^q=Q>qO9FRl zY5~dy9WZ8}w?uFvK)~>IK;zK6X|!q-7V*BoF@UdXRXr^oTzu>a^{~#$Nzuar{+I#w z<$Hn*7=mc$c=Jdx*=Uy9fG01rRdl4Te(;Q?1d1ut$V*)GAg!5fST83D(fegc)X=d* zgVp#Sa;!i69$=UfgO11IWoMKUQyv*IuXGNv4yerc@RF2E7fpLo+lCR;irw1qHzau1 z`1*7a`2xw>tl5bf9l{3zN|!q};0`G2$fpiOm3ILLez^u@6f*E$NC@d2{G2pB?q!aY zTcvnmqfVBNTw)E~eeX}`?hXCiG^La@6V@P7L#aLiEfc?}6;{ueqT5vN)z{cn`HC7< zm_TQWbqnU&BLM(D+)ay#2nqgLE%U7=ecFEkGVAB9WKTfGuUD)g+~s*g*n`W2{gA2W zI=z$_HnB6a3}5|mriwkKgi@20-ELp=E88!us9W;2zQAR?u*jVnYDn8b@|UJoa@eKL z5R%Y-38f@mseoic;CX)vsk(uZN{?ZIhqek1)aib2Dl!gnY={;)T+lhhZel$;#f^th z9)+46n?f%RFqjM;Au1(48Gsz(4G0O8+u99Wlkt)bYUF`(kx5j*)iDj(cV{rDdn*-P zOG}=wJ;XEdVkxZ5v>%o@149cUO|Wg-d2s%{(_96d=6aNN$JLmkoVltTPGYtU^kP|oV(Va zJYqvv7LDnaveBO)xb4mFfC~LXsQ|=6vt^IeI z07vFJozRH#Udh)GQ1TT9c(vI<65FEnFYoLVJ-vyAw^&%U$@9`(|DX>km9u)f1b!8J z==|3LQB&>@qRSaSO!?AgtwY_agI7-8k)$=x{du{_(y$UW;s9R~#Q2NIYuN8e{12=`{S^KUl>@bu(qa$%(UG`iPpew$mgI`AI#g>plA zR^Q7eFh7SOUxm+1)w9k+j;oaR;0t5#Eu=VE3huu8$^RVNH!na5`pRQE^V#XS9fKDr zAtB?ro2EiNpD26Ky4T0I%#?<>cGOA}IIvx^P>wZsv%t3ygdP?EAuH@)?d>5RDkp<> za!=JmOY_gL@6uwji1pDp^mEtlA-FDZ%i9J|bWf#8Oe>8b=iEPlN|KwNZy>Db-d%yV z-Dt0h%jawElx||N%7L(kiBqHe=ve0V2;r}8-ipLC+NXta6=H3>8rX$u`r46{qQip2 zFQ!&yjlr>@qvH~1M!J?|yd&^;JT_`2cP8Z9*A;>G43ex;+?MXnYE6{-3IMAU*+?Hz zGAWW_yk@@A-EK2SNf#D?&98T}pw_;)wdq_R;Z)!uD*_E;Mm>=ccsKXnEvAl^C(C#r z&(|c-Xy5n83ZvE5O=uoCIB)VxT9GkqEe=hOdjnpZ8nGFM_XQ|loB45w{mK?!(Mk!V z&Xwf!U=csn%m^3|&#q~uw4oqg?NVIBzX{v~?V(C!(XIIb_8;Mgy8j4HMrDIdnQ zTApM+jh&m)%p5Pj=I0=epJ{5ctu1uZlb6D<*I=--W(_w>E(W@W=4~;G_Mt}x-%Wh) zPTd)Z1D}C-49%S$wr!tC9)h_TQCmOM`JqhCF zuGYj;akT5^NvvuabY?Mvgr{Q}H_1$)eN(0>RpY^(gq?mXvtz{>wvu5N<)r7kH~T7{ zDiL9_|6BwqKsZw3m5db7>&)I8?>Z!w?glQ{)Fjp^g}7HVs`VA`N&}0d%30P|*fMhmI$e)LLwEyjnk9q(1`9Vg2vf-vs*2)FfflJuU4~EZ~Eu z1lwDho9{Z^yD2iOfn9o#bt#=6Kjo>(6Z@=vnbOepiAO8t&EmaSjjkp&IZI=C41^<% ziEGU4sGRMGzL|^O>1n4ET=MiM=e^B=TW-4q$SwFy)H?+p;r5Y;B)$PoK?Iuy*2Z1A z6r+Lp`nVSP^;oS#sOa)Ze(O3AMu@p5R>$**n%7McjCz-gO`LAI+N`e>VpQ*n5S;h$ z#HrJaHgj!iEna-AJvmFz)9p;Cst5XkSLzJt*8A?9Mlp3XzByLg2T4p1YA7rWu6@|G zB=#u#D=Jx=mAELTvEx%qzEOfOPFRZzyRD_-ek7)IU|Q{w zW}vvkmdvI(`Schw2zhd9ckeIXpAYQ2IFAE_k-XFaMt^H@=B&uaXRaTT04IUM5cC|7 z6d#lJNS^_wBTL$F>AcqA68zOzw6sW#j#>eTau@A+C0JGpq zRWDiAgPoI)vkKeQDS** zZ7y4-^k}AtE$0MQ^~=Q6ccGVaN2ME0E5%}<_sd?)WwARx#Ub0I%T**wVqpnSY4oQ% zobQ}=F#-qYjDI5Ar&Cha~`}^W4wYHJg z2*qPPWxVZ}P6+~Ei3;=8zT*L0_+@gE8Wp8iwh`!&cY#Hqd#m|D^pH@qcSV?z%9Z}y z%{*}TJyQTIV&T|Od(w^<|L*k;Zl3Vm8(%fs+4*jq7le)>G;RHx5b5e=S1#4iFF$E%3Qz-AgR+%Phy1rS3iY@^r5wn)CJS)bS)H|6eq=?|vd#P^0<+l53 z+_)y3gkJ`bc&x&<*HLN2Zi|ZHOpraL`K_(*6GavqWnrHO>jR4~xQ?KDe^@UU&zeBdsv zCQ?M(F`q|WE1S#Nr@NOZX^~tq{++UQdNDhkRBa%Hap^v&wU({N)6|*-LRx7UOJb;q z%)7KwX2}X7v2E3nS_zHeq@<@f#nXfDPl6Gn)8LI3sr4FZd+P9q*HwNx$MFNv|FIv6 zm~lqM2=u4PZSByb?{Aw@OBKKk-Q`*^*g-AWvbc+PL&kz+_dCl6(QIg}zjY^84cER!>397>!PoUPexz?hXiQ4$C)7U39b-MH@*f{RJ)T4xk} zSY58jWELY?H@@+~o|Chi64ZDlU*mfowroo?DqD$yG9{1aOYRuix}$* z29R^sM?7pQlDv~ zi6SIF?>@VE@j{e$qc8(y2b7+f(&(q79xG+Hx3-i2v9>^v62lpL&Pdv<*_#A2BUp z*MV72d+KWFcP|o+;vw@MHmn7bCne$B>`TVC%gHwTba27VlJKGN#6}#W+^!v^v2V_x z5!pZPqrUABswa}RTh*)=n-R;?DLuJG{1oe9>cmJC41msTI@>HQj(YEMx-gF#>KW-V zLa){8pNbb6?HIYdedBHMXfoGhXCVRDRo@&0QQ$3SKh#ZU6q%5%pJ9a1e)|DgQ8w!a zjUaG=Gs*Wqz>O2O-ZA!}vqMpO*6Yj9<8ZD2Ui9Rb*IDEoF--P4P@;A5FtOz45FfMP z7;p>v^5eMJn<~Fb{=0hmrV!1{u!Yy(VW}@1pFUsaIg0O#`+j6pHe0GrqjmT6I4e+B z75nf6EM06fP;bj!RJK~?c^q2qZ!ZCe@%6pVDuv=YMk^d?@WQ;G{ybYXT`#r9Kx|VgR z?vh9n&EP0s+ZL*CA#1m36|V%{|OxD zVWMzcGU2l$5J6Nlos3BCxw;wX&|a(Px$)XAgJ8}3t1;vJPDQv$5ce)EiNT|Ng{SQ; zx7*cB>ZkT3+glmti3CUgD_b7kZ~2ItQsSNHt~iLoc4;<$QA2HSpsR&;6OV|a$MB73 zCXYqib)B;-NG0^dddw>Bw3PYR@Gf^>?(uemEgnutJj{q@FI@bKqWDVdyL3wy>ue$z za>4tei=KvK%dF4ZNQL+m`(!2*^s-$x*sm}A$qVDQ%BnO8B5(UH4thbW&!yG}pVK8z zuuOz3-#E(QD-33_zQZ5(wMGDwXzk{;qn1j2-v~L!D}5Yq`BPx9D3WW?{Z)H!TAmeG zb;LQm>t-(-`XsV3Vfo70r~04gKYHWq1NoII6dAVjQy#K7%gJF7ARzVrvNA`8|IJYL z9*qBZ@Vta5>T7Jv4(1i9=O>@ec!t@TJvzC1gh5*#J?Q;~9lQE`Xk6~N=?Lz6_CTAR z3Jv93eK|qnYjk>LWJqZ#`c~btVq(!!e)KPAnM;Nk^iJ}f$;+~e==@OWuM<=E4imZO zh>%FBy6vf3flapVPj3S02_C?uc(QdXiivSB!C&~yePZg)SaHd{j0YD^h*I!}2wOLS z$h$&>b-BNz6BZ2ws}B>q`+RY4!bwIy>G^0{-m9NC{RDMn+?# zG3q$p@Y6pYk`U)$%ox9SeqFH&AI2XHTp&{GYC|D*t~J*v}Z^_sU!kt+Bz zu^>DS&1AjJa|5Nz_LE_GS5L;>qcJxxA<7e0uiol-e$z;pd%8nxLhrsSCgATwut1w;t`h=+ddH8BfJUjQGE+xx=O! znvkoAk)a0&7QnI@`@3Lre`U>S8Y=DQ>s|Z=;L1M<)f`*^6m0!`&ZB9i z9xM!^%ZE1&zI_fB0fH;>LqIT4Om%Rz?E3*7g`6V;RlVYPvY&LpMq*^t6 zPupy@1n)3-{Od>DS;o@hU1l!iQz#$|Z)w@DdQ(g5Q07>GCH;qM!i z#SQ42=Rh*oiQjjEZTWs&tlw3XM*f*!^}A%jUApj)%5$n5>{<@19#_9XPsig_G{}X!WVyR9auhVwItw!8(OgMZNX@D+eFA`~-j1@8ZHn ziglq>jN*xOw-SP0N$+o%OSDWuHitjWe#wFN5{>u}-PndN<3a$SmfC~uV0Ko(P!}d@ z@m+$S1!0-8h85r<0Q-3Jg~?@)v&r87|m7yhR&nc3-)Zg&=Y-y$&W<2P( z&#T|v!SD^dcII|gbhaJ??s8tQsBr9**in{PU}~ZzRV_;J-~(B%6_v~ZA_)ikNv5eJ zz?tE1=2jQPl``nR42^S1GRw)oW)9E9(QW7W=5FTt=56NrI`;An1u^Z#-GPX`Axs)$ znEi!mspMC1X>GsIVZ%rf+yKY@M*ReM#eq7KZtwa4|AAG_r5j6o!F!#hn@fm>uEE#f zx8?u>I(R|4Q{DLl<%d}MMq?7f-yvNn$Mw3CQhDz)BXM8%>p!O8kUp@;aREq{uvgsF zjJYjQQltL$N2W?a(C2;!WhG*b4qSvV5UC+C@oKK|R%9dhK`e785KF~@(K;A%0!vUz#jq{S**?qjr=CMEL=a-G{%heHPk5QHQ zwB+2#9+L&`6I`qNmgTVT+mC4uJxZ1?9s8IZU zJI+gnG)+R!_IGcLj#jt~3*2A%_BZGn&0;oIw)p*o#P$!YnbDxxweT21SN;()m;jz5 z?*lt!I5jRjqgZUxzpnprWWL+lJ8Js+bqviPC@CwdkvVK2cL%POz3jTsYz7@mH6B@0 zmiPk|?V9~H3(3vE*W`w%2-ex_fk^-;;l(5rijSu^kh6sU10s|6kY?D%EOtA&V|*@% z!RxJuvPhcS+PHko9@_VNx|B3#>9~<;!uS)vwEOq?l+Hq86ESO_A5ZkKbVOvnI>Ei0 zrKo@&I47!Jkvs5~uMhY-asX&Bw7?smPTVhR(2(`DD=)!8ya$1lotNtdQTwOELzJXMWP-iprKq9Rj>=6(oc&Z+HnBN& z?w#)Ko!xzIc&L+^NHr7%i;t>p_|EL!`2(>{Mb^+LF1&0<82_HU$M%ZgIv{tXIWpymCcFE8dbn+xWdanyZ!N_P9TYNj0<>4A5Y1S0j5A_e#NqqBt&p zqC3PRp>1|f!Pb3z)G#rMbsW3!+19lcNj8ci;iFj3T{ABjt6?48!@74U93GUP&-oD( zz=Jc5%){(H5vwh4Erzxqe1ZK?puoeO(su5SSG|&`cVBfjRLu)<`2u?o9d=8v-Ie!+ zOX@2Uyyg@oxbi#;-BYi=31rWQMX}&uT1@M-S=sX1=Q1m?-6dDoK82p72LR}qlA=XC z7I@|Mb73abv5MvELuBj6is%`@Nt7ub-yp_x=&X}APAO7}9m*RG*)t-WIWsOS^#D$x zN40x>G-Op0t#qLL8u9L7E)ncfp-iEPptwj!_yPH9^gO+hF zk1Tle@+tyT`{a6KJJc*SW~CcNt(Uz#?i#soMEcg%I9@KOiJB!V^IGz{1lo2Dj=b(l z4?}DcjuncVQADTRFoSLK_Cr&B(~GVN0{kBpPmA^5x$RQcAWvEMg}VcHoMVYC z*HUE=E@eR9%)t)@g`fJ%3TaAO?yXylV|Qh1j}Bo6`t(^hs*Y55CN!_oWY^+v+(!jy zQeFt7V%`+=MCoLt%k}RL4AFSlf8Y$hO`&Y!)J#u?w?$}u_fPwb=+#y7++I!#6O~eJ z8b;JEUj(Sc@FN`d-8Knmz$*1%zF+yotL*pQ^~j^)phW@aL zG^w{wmDi+&IeLi~%$;IMx0NtwaM;+Sh%&w0ck-2?+ktxKJENWpzNg-4A7ycH%HGEw zaTeN8+=!fC+mIDLxL>3p_LHrNzx~A;a_tR%<;h_drq)PIf;3n|z7QaS1&xflcK4w- z7E8T@EvPuFzB&f*nT}32=J`C@O}XP(mP1|Ouh+J{zHSvnMq6;*aoDW}5Z8r6s}JR3 z7xh}+O=+y&8oaKb98{UyH52PBoXW`zR_LE;SvuIYx%k$wS@2C%o?}e zTgbMVrHr8umj>}1;`ILB(tNAd`c(Y*teCHfltPLe1JQ72ze<2Bk-~q&%R@O;4`mB{j#*xVr_|2+@D`ipzPt!-U3h9_<@-jE9 z$QKQx$dA4<#GMIWqSLtbnG)L7uwo&hetF45mAE|h%5`8`(cz&#k>-r$+-&IPI3KOH z^^s@c>^V$ev&Rn`X*ab&Ov^KJG`x|U(_ipWW-ipm)8{azlGJ_uz5n=CMSx$Z)oa>R z0&>_jshvzod3uQX0>Bzq${~fOnVJYTyf1x|NXhdp$gyse^S&Yjc??S=p=R;rWsHkn zKg+R)N7*O97g^T7x)%3jE!OsLJ$j1MI#)<+y$@ut2(hDsuUrw-&HIqV6;w)^X}=g~ zb(;R>Tv_MB+`}_&OXvC10!O@Oi_|C)uY0)Lu4>^QwV@rH!7}Gr1iJ2=jP^oQ>SUgL z>ZO)4r@>1bPB7fXMa1a>*}O47nRYmjxn=h_{+afqBL(ga|P*KzVsksFE zUeb^a|0wr!UFB%4Hdvsu&Q*2maE`_@)D=ualEv?tj^U-*E;cDIRbUJiLY&# z5Rh5#o&6uA1I4eLvO0|mz}=rXbr?|w@d+nQKYCz0+E|OUQh7dqZMghets(%zLJ-Aa z5;AAjXPczD(4XH+$~vgNroTl5nM*4@qD5w#H|_*o8Mh?M&tIK9iSL|4X;dujxG`hz z7uQ^Ch9H~YgeXP=nrAd6!Q7CYeK!i@2X5eO62hd2PG6Bb{R%HOVt9E|!m8;*C=uKV zbsik9)_LxGPE|Yma)A=^|`{dSsKUN z^^V>3ZcP{ZK_TC+zg|GME&Ns-oI-@E&+M4m-K2MhD0WpC88YXtSUj z&<<@2gS}mSL54xCxp+H*C%CVS&6^`*^mIHInWo(K!((z!a0V1IFMP1=V~Q+pkc0H9 z7D;+-CBFXxX$2o=b1&?TXh?(YA(cz77MK=4O_AVFF$?}vORnF$L-doow=M@nJbyWN z5^N&&GJ@Ih`X)_;=P-nK&SGfBr_P;`xOl&?kP(_xmFK&ri6}H*uWNI;G=UzBC>%|% zRWUSNTdsTfaU!->2kEaA+NJLe8Of4EP~<Rk$)?Ishl5JM!mo$XPZ=mv8*`U? zWzFnwZ~0DY2mtwc1+#yfujP-7In}^&x-yX=l}f+KOTd-=%i-QP^wZe}yzJ7Zcm-;| z%BYgWx!#l91N5y&+IM+HJF+^z8OSPUSbMp~p3V@d!v%8#P?$m7 zhd~~ILN3l5ufK=gi?R#0+V#CZ=^?iy;wcB$^&NYw=xH!pEx+Q={M+K4uVC0AN&c^x zeJ3o->kS`W(>aGHiQX(d$hg#%)u2@0@-{5}sc^e4{DnEiA(Hg1*`Hg=9NP`;xg$UJ zlHw+3QxvO<`kr%#D$Nccs)Oo|M0(;r_)q4*kS7WqWOh?FZQ%xQ$Ak%h<+}PvGi^Ub zW+tsQ&2N2Y6yKf7s)hdF230fVF3mi@BX4p`eac)?X`~I;QDk^7p59#)wcRCVe&vok zM1j9v-fc*;JC&5+MXKRVf^wzVD7D|l zzt#Acb8Fa%G3aFk+zcgjW7x6p5mG3mP%vXr%{YtVtfN*@wJ-d8Gob?n^Zsq;n4?JU zCxqP)YytjFg>z-19bdVNYJATFP-$SaD5|HObL9GCaHV;WcWiJl-M7V^-%bmqRR_SY znM>LDzZZWigy~$V@jtB^TGy~`;<*EWK)_ApzgQVS)sZ_+jG5KN%JB@tr>Dq}=ElZ> zqj*E?C?uTMfLrM$Y5XXVDe(KjM!%~Ap>3Y6et8xgoDag}x1m6(BaFC=P}42H#Suk0 zAZ`EG(@OdSD?LnX0${k;0hb%Tg$ji3s&W5|2g0)XF`qS6n>@lvvmLHV=W!bQsoTwo zRzhcirsA)Se_?EoiK(E@O#zlp)ihO##G$?L!KLTdt2H4J*MWYm-!f}H;98Z|-<+oC z*Ey_q21R2zyWjl>vr}3lK-6=o={_Dc5@<#Q&eNk!5E%XCE5&0W)>EUO2$=UTgLwzl ze-{#z*3sPNs^mlLj=HqtbBkY6ZfbJ;7g;Q1a)*AOnF0b zrmE>|Z_ftrR8tVw#9?Blj`~Q%nQ57DV1;SF@bGz?xE4l!-?zPN z#tK5`)2EW(4^F4DVD(Ct)NQQ+vGFt00Cipb^>bzcrR%uo?Uaj+)25oOF!}SikkgSx zX>&_0PvUR`&l<9;h|TF<^XzSydCRJ8C8n1> z&Z;))o+~GOxovM<*C5mBoFMnF@jVjBL9$c`dPwI_81#3E`4^n|+pIPIf+c^WTI^ql z=`XqfkeGiVpTCRmzk+zbku&6PaP=3T44Bu5Nb2pE^^TSQeHSoR{C;=e2dMd@gduNc)8}OUq79BJ`F9vprj4S;2X;Q{0GR%npJtEif0B$@|$yX>`ivuc;L{_5yHIf@~^^0rv&(1 z63^ug1w-p-c!hC&Q1GKx6UVa3_91VB{XdYw%9ZcL9*fxK&9eo%uJN0Lzm`9`87$vt z3%{tA@_O;o(A0(N3tdSf`a|CORegs-`s@04{z!kU8`u#Njd<>yxbxA$%maF0o@Y${ zhn~m_6340tfzv%6CH9yt^c%UkZ{9XkmbeSuL$gNr87pc5pkYOEK2SQv@=vy@qnw#j z3!ElJx>!!${!yZc=1`uAsJN6Hr>_l9E@|{snLB;m-guY pq#~ugblm?l%BB98i6BXjq7)$P6XY61I==Tr^V-d;rK+|M{|8sDb{GHv literal 0 HcmV?d00001 diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index a67208b..c6acab6 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -537,7 +537,7 @@ function addCustomRoleEventListeners(deckManager, select) { if (!deckManager.getCard(name)) { deckManager.addToDeck(name); let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(name), deckManager); - toast('"' + name + '" included.', 'success', true, true, 3); + toast('"' + name + '" made available below.', 'success', true, true, 4); if (deckManager.getCard(name).team === globals.ALIGNMENT.GOOD) { document.getElementById("deck-good").appendChild(cardEl); } else { @@ -555,6 +555,19 @@ function addCustomRoleEventListeners(deckManager, select) { deckManager.removeFromCustomRoleOptions(name); updateCustomRoleOptionsList(deckManager, select); } + }); + + role.querySelector('.deck-select-info').addEventListener('click', (e) => { + let alignmentEl = document.getElementById("custom-role-info-modal-alignment"); + alignmentEl.classList.remove(globals.ALIGNMENT.GOOD); + alignmentEl.classList.remove(globals.ALIGNMENT.EVIL); + e.preventDefault(); + let option = deckManager.getCustomRoleOption(name); + document.getElementById("custom-role-info-modal-name").innerText = name; + alignmentEl.classList.add(option.team); + document.getElementById("custom-role-info-modal-description").innerText = option.description; + alignmentEl.innerText = option.team; + ModalManager.displayModal("custom-role-info-modal", "modal-background", "close-custom-role-info-modal-button"); }) }); } diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 15c18b4..ad16d30 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -15,9 +15,7 @@ export class GameStateRenderer { renderLobbyPlayers() { document.querySelectorAll('.lobby-player').forEach((el) => el.remove()) let lobbyPlayersContainer = document.getElementById("lobby-players"); - if (this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.PLAYER - && this.stateBucket.currentGameState.moderator.userType === globals.USER_TYPES.MODERATOR - ) { + if (this.stateBucket.currentGameState.moderator.userType === globals.USER_TYPES.MODERATOR) { lobbyPlayersContainer.appendChild( renderLobbyPerson( this.stateBucket.currentGameState.moderator.name, @@ -409,10 +407,17 @@ function renderPlayerRole(gameState) { '../images/roles/Villager' + Math.ceil(Math.random() * 2) + '.png' ); } else { - document.getElementById("role-image").setAttribute( - 'src', - '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' - ); + if (gameState.client.customRole) { + document.getElementById("role-image").setAttribute( + 'src', + '../images/roles/custom-role.svg' + ); + } else { + document.getElementById("role-image").setAttribute( + 'src', + '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' + ); + } } } diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index 06e231d..f3bd3b1 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -227,11 +227,11 @@ export const templates = { CREATE_GAME_DECK: "
    " + "
    " + - "" + + "" + "
    " + "
    " + "
    " + - "" + + "" + "
    " + "
    " + "
    ", @@ -246,7 +246,7 @@ export const templates = { '
    Export
    ' + '
    Import
    ' + '
    ' + - '' + + '' + '
    ' + '' + '
    ', @@ -258,7 +258,7 @@ export const templates = { DECK_SELECT_ROLE: '
    ' + '
    ' + - 'include' + + 'include' + 'info' + 'edit' + 'remove' + diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index c9d2946..9f75c87 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -165,6 +165,7 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => { toast(player.name + " joined!", "success", false, true, 3); stateBucket.currentGameState.people.push(player); + stateBucket.currentGameState.isFull = gameIsFull; gameStateRenderer.renderLobbyPlayers(); if ( gameIsFull diff --git a/client/src/styles/create.css b/client/src/styles/create.css index ff28b03..40192c1 100644 --- a/client/src/styles/create.css +++ b/client/src/styles/create.css @@ -135,7 +135,9 @@ bottom: 0; right: 0; width: 290px; - height: 20px; + height: 50px; + font-size: 20px; + text-align: center; } #custom-role-hamburger .hamburger-inner, #custom-role-hamburger .hamburger-inner::before, #custom-role-hamburger .hamburger-inner::after { diff --git a/client/src/styles/modal.css b/client/src/styles/modal.css index 2d4c757..0bddbba 100644 --- a/client/src/styles/modal.css +++ b/client/src/styles/modal.css @@ -49,6 +49,29 @@ flex-direction: row; } +#custom-role-info-modal { + display: flex; + color: #d7d7d7; + text-align: left; + font-family: signika-negative, sans-serif; +} + +#custom-role-info-modal-description { + margin: 2em 0; + max-height: 10em; + overflow: auto; +} + +#custom-role-info-modal-name { + font-family: 'diavlo', sans-serif; + font-size: 23px; +} + +#custom-role-info-modal-alignment { + font-size: 20px; + font-weight: bold; +} + #change-name-modal, #transfer-mod-modal, #role-info-modal { position: fixed; } diff --git a/client/src/views/create.html b/client/src/views/create.html index db7afe6..7b11d76 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -56,6 +56,14 @@
    +

    Create A Game

    diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 9c838fc..fe9dd23 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -355,6 +355,7 @@ function initializePeopleForGame(uniqueCards, moderator) { let j = 0; if (moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { // temporary moderators should be dealt in. moderator.gameRole = cards[j].role; + moderator.customRole = cards[j].custom; moderator.gameRoleDescription = cards[j].description; moderator.alignment = cards[j].team; people.push(moderator); @@ -371,6 +372,7 @@ function initializePeopleForGame(uniqueCards, moderator) { cards[j].description, cards[j].team ); + person.customRole = cards[j].custom; person.hasEnteredName = false; people.push(person); j ++; diff --git a/server/modules/GameStateCurator.js b/server/modules/GameStateCurator.js index ec75652..b7f4fa6 100644 --- a/server/modules/GameStateCurator.js +++ b/server/modules/GameStateCurator.js @@ -38,6 +38,7 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { userType: person.userType, gameRole: person.gameRole, gameRoleDescription: person.gameRoleDescription, + customRole: person.customRole, alignment: person.alignment, out: person.out } From 3b14ae3978eed05ecae273f9c59b30a3986099a6 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Mon, 10 Jan 2022 21:02:29 -0500 Subject: [PATCH 57/60] clean up state management --- client/src/config/globals.js | 7 +- client/src/modules/GameStateRenderer.js | 32 ++-- client/src/modules/Navbar.js | 2 +- client/src/modules/StateBucket.js | 3 +- client/src/modules/Templates.js | 4 +- client/src/scripts/game.js | 229 ++++++++++++++++-------- client/src/scripts/howToUse.js | 9 + client/src/styles/GLOBAL.css | 10 ++ client/src/views/how-to-use.html | 47 +++++ client/webpack/webpack-dev.config.js | 3 +- package.json | 2 +- server/config/globals.js | 4 +- server/main.js | 4 + server/modules/GameManager.js | 73 ++++++-- server/modules/GameStateCurator.js | 45 +++-- server/routes/router.js | 4 + 16 files changed, 349 insertions(+), 129 deletions(-) create mode 100644 client/src/scripts/howToUse.js create mode 100644 client/src/views/how-to-use.html diff --git a/client/src/config/globals.js b/client/src/config/globals.js index 47b16e9..e50b2f7 100644 --- a/client/src/config/globals.js +++ b/client/src/config/globals.js @@ -18,7 +18,8 @@ export const globals = { REVEAL_PLAYER: 'revealPlayer', TRANSFER_MODERATOR: 'transferModerator', CHANGE_NAME: 'changeName', - END_GAME: 'endGame' + END_GAME: 'endGame', + FETCH_IN_PROGRESS_STATE: 'fetchInitialInProgressState' }, STATUS: { LOBBY: "lobby", @@ -38,7 +39,9 @@ export const globals = { START_TIMER: "startTimer", KILL_PLAYER: "killPlayer", REVEAL_PLAYER: 'revealPlayer', - CHANGE_NAME: 'changeName' + CHANGE_NAME: 'changeName', + START_GAME: 'startGame', + PLAYER_LEFT: 'playerLeft' }, USER_TYPES: { MODERATOR: "moderator", diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index ad16d30..3b95c6d 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -10,6 +10,12 @@ export class GameStateRenderer { this.killPlayerHandlers = {}; this.revealRoleHandlers = {}; this.transferModHandlers = {}; + this.startGameHandler = (e) => { + e.preventDefault(); + if (confirm("Start the game and deal roles?")) { + socket.emit(globals.COMMANDS.START_GAME, this.stateBucket.currentGameState.accessCode); + } + } } renderLobbyPlayers() { @@ -465,16 +471,18 @@ function removeExistingPlayerElements(killPlayerHandlers, revealRoleHandlers) { } function createEndGamePromptComponent(socket, stateBucket) { - let div = document.createElement("div"); - div.innerHTML = templates.END_GAME_PROMPT; - div.querySelector("#end-game-button").addEventListener('click', (e) => { - e.preventDefault(); - if (confirm("End the game?")) { - socket.emit( - globals.COMMANDS.END_GAME, - stateBucket.currentGameState.accessCode - ); - } - }); - document.getElementById("game-content").appendChild(div); + if (document.querySelector("#end-game-prompt") === null) { + let div = document.createElement("div"); + div.innerHTML = templates.END_GAME_PROMPT; + div.querySelector("#end-game-button").addEventListener('click', (e) => { + e.preventDefault(); + if (confirm("End the game?")) { + socket.emit( + globals.COMMANDS.END_GAME, + stateBucket.currentGameState.accessCode + ); + } + }); + document.getElementById("game-content").appendChild(div); + } } diff --git a/client/src/modules/Navbar.js b/client/src/modules/Navbar.js index 2d3472e..c22411a 100644 --- a/client/src/modules/Navbar.js +++ b/client/src/modules/Navbar.js @@ -42,7 +42,7 @@ function getNavbarLinks (page=null, device) { '' + 'Home' + 'Create' + - 'How to Use' + + 'How to Use' + 'Contact' + 'Support the App' } diff --git a/client/src/modules/StateBucket.js b/client/src/modules/StateBucket.js index 7571fc2..fa71c56 100644 --- a/client/src/modules/StateBucket.js +++ b/client/src/modules/StateBucket.js @@ -4,5 +4,6 @@ */ export const stateBucket = { currentGameState: null, - timerWorker: null + timerWorker: null, + gameStateRequestInFlight: false } diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index f3bd3b1..4fe03b8 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -52,7 +52,7 @@ export const templates = { "

    " + "
    " + "
    " + - "

    Click to reveal your role

    " + + "

    Click to show your role

    " + "

    (click again to hide)

    " + "
    " + "
    " + @@ -138,7 +138,7 @@ export const templates = { "

    " + "
    " + "
    " + - "

    Click to reveal your role

    " + + "

    Click to show your role

    " + "

    (click again to hide)

    " + "
    " + "
    " + diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index 9f75c87..299f199 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -13,16 +13,21 @@ const game = () => { injectNavbar(); let timerWorker; const socket = io('/in-game'); + stateBucket.gameStateRequestInFlight = false; socket.on('disconnect', () => { + stateBucket.gameStateRequestInFlight = false; if (timerWorker) { timerWorker.terminate(); } toast('Disconnected. Attempting reconnect...', 'error', true, false); }); socket.on('connect', () => { - socket.emit(globals.COMMANDS.GET_ENVIRONMENT, function(returnedEnvironment) { - timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); - prepareGamePage(returnedEnvironment, socket, timerWorker); + console.log('fired connect event'); + socket.emit(globals.COMMANDS.GET_ENVIRONMENT, function (returnedEnvironment) { + if (!stateBucket.gameStateRequestInFlight) { + timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); + prepareGamePage(returnedEnvironment, socket, timerWorker); + } }); }) }; @@ -32,46 +37,51 @@ function prepareGamePage(environment, socket, timerWorker) { const splitUrl = window.location.href.split('/game/'); const accessCode = splitUrl[1]; if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) { + stateBucket.gameStateRequestInFlight = true; socket.emit(globals.COMMANDS.FETCH_GAME_STATE, accessCode, userId, function (gameState) { + stateBucket.gameStateRequestInFlight = false; stateBucket.currentGameState = gameState; document.querySelector('.spinner-container')?.remove(); document.querySelector('.spinner-background')?.remove(); if (gameState === null) { window.location = '/not-found?reason=' + encodeURIComponent('game-not-found'); - } + } else { + document.getElementById("game-content").innerHTML = templates.INITIAL_GAME_DOM; + toast('You are connected.', 'success', true, true, 2); + userId = gameState.client.cookie; + UserUtility.setAnonymousUserId(userId, environment); + let gameStateRenderer = new GameStateRenderer(stateBucket, socket); + let gameTimerManager; + if (stateBucket.currentGameState.timerParams) { + gameTimerManager = new GameTimerManager(stateBucket, socket); + } + initializeGame(stateBucket, socket, timerWorker, userId, gameStateRenderer, gameTimerManager); - document.getElementById("game-content").innerHTML = templates.INITIAL_GAME_DOM; - toast('You are connected.', 'success', true, true, 2); - userId = gameState.client.cookie; - UserUtility.setAnonymousUserId(userId, environment); - let gameStateRenderer = new GameStateRenderer(stateBucket, socket); - let gameTimerManager; - if (stateBucket.currentGameState.timerParams) { - gameTimerManager = new GameTimerManager(stateBucket, socket); - } - initializeGame(stateBucket, socket, timerWorker, userId, gameStateRenderer, gameTimerManager); - - if (!gameState.client.hasEnteredName) { - document.getElementById("prompt").innerHTML = templates.NAME_CHANGE_MODAL; - document.getElementById("change-name-form").onsubmit = (e) => { - e.preventDefault(); - let name = document.getElementById("player-new-name").value; - if (validateName(name)) { - socket.emit(globals.COMMANDS.CHANGE_NAME, gameState.accessCode, { name: name, personId: gameState.client.id }, (result) => { - switch (result) { - case "taken": - toast('This name is already taken.', 'error', true, true, 8); - break; - case "changed": - ModalManager.dispelModal("change-name-modal", "change-name-modal-background") - toast('Name set.', 'success', true, true, 5); - propagateNameChange(stateBucket.currentGameState, name, stateBucket.currentGameState.client.id); - processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker); - } - }) - } else { - toast("Name must be between 1 and 30 characters.", 'error', true, true, 8); + if (!gameState.client.hasEnteredName) { + document.getElementById("prompt").innerHTML = templates.NAME_CHANGE_MODAL; + document.getElementById("change-name-form").onsubmit = (e) => { + e.preventDefault(); + let name = document.getElementById("player-new-name").value; + if (validateName(name)) { + socket.emit(globals.COMMANDS.CHANGE_NAME, gameState.accessCode, { + name: name, + personId: gameState.client.id + }, (result) => { + switch (result) { + case "taken": + toast('This name is already taken.', 'error', true, true, 8); + break; + case "changed": + ModalManager.dispelModal("change-name-modal", "change-name-modal-background") + toast('Name set.', 'success', true, true, 5); + propagateNameChange(stateBucket.currentGameState, name, stateBucket.currentGameState.client.id); + processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker); + } + }) + } else { + toast("Name must be between 1 and 30 characters.", 'error', true, true, 8); + } } } } @@ -86,10 +96,12 @@ function initializeGame(stateBucket, socket, timerWorker, userId, gameStateRende processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker); } -function processGameState (currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker) { +function processGameState (currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker, refreshPrompt=true) { displayClientInfo(currentGameState.client.name, currentGameState.client.userType); - document.querySelector("#start-game-prompt")?.remove(); - document.querySelector("#end-game-prompt")?.remove(); + if (refreshPrompt) { + removeStartGameFunctionalityIfPresent(gameStateRenderer); + document.querySelector("#end-game-prompt")?.remove(); + } switch (currentGameState.status) { case globals.STATUS.LOBBY: document.getElementById("game-state-container").innerHTML = templates.LOBBY; @@ -101,8 +113,9 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer, currentGameState.client.userType === globals.USER_TYPES.MODERATOR || currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR ) + && refreshPrompt ) { - displayStartGamePromptForModerators(currentGameState, socket); + displayStartGamePromptForModerators(currentGameState, gameStateRenderer); } break; case globals.STATUS.IN_PROGRESS: @@ -174,21 +187,69 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo || stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR ) ) { - displayStartGamePromptForModerators(stateBucket.currentGameState, socket); + displayStartGamePromptForModerators(stateBucket.currentGameState, gameStateRenderer); } }); } + + if (!socket.hasListeners(globals.EVENTS.PLAYER_LEFT)) { + socket.on(globals.EVENTS.PLAYER_LEFT, (player) => { + removeStartGameFunctionalityIfPresent(gameStateRenderer); + toast(player.name + " has left!", "error", false, true, 3); + let index = stateBucket.currentGameState.people.findIndex(person => person.id === player.id); + if (index >= 0) { + stateBucket.currentGameState.people.splice( + index, + 1 + ); + gameStateRenderer.renderLobbyPlayers(); + } + }); + } + + if (!socket.hasListeners(globals.EVENTS.START_GAME)) { + socket.on(globals.EVENTS.START_GAME, () => { + socket.emit( + globals.COMMANDS.FETCH_IN_PROGRESS_STATE, + stateBucket.currentGameState.accessCode, + stateBucket.currentGameState.client.cookie, + function (gameState) { + stateBucket.gameStateRequestInFlight = false; + stateBucket.currentGameState = gameState; + processGameState( + stateBucket.currentGameState, + gameState.client.cookie, + socket, + gameStateRenderer, + gameTimerManager, + timerWorker + ); + } + ); + }); + } if (!socket.hasListeners(globals.EVENTS.SYNC_GAME_STATE)) { socket.on(globals.EVENTS.SYNC_GAME_STATE, () => { - socket.emit( - globals.COMMANDS.FETCH_GAME_STATE, - stateBucket.currentGameState.accessCode, - stateBucket.currentGameState.client.cookie, - function (gameState) { - stateBucket.currentGameState = gameState; - processGameState(stateBucket.currentGameState, gameState.client.cookie, socket, gameStateRenderer, gameTimerManager, timerWorker); - } - ); + if (!stateBucket.gameStateRequestInFlight) { + stateBucket.gameStateRequestInFlight = true; + socket.emit( + globals.COMMANDS.FETCH_IN_PROGRESS_STATE, + stateBucket.currentGameState.accessCode, + stateBucket.currentGameState.client.cookie, + function (gameState) { + stateBucket.gameStateRequestInFlight = false; + stateBucket.currentGameState = gameState; + processGameState( + stateBucket.currentGameState, + gameState.client.cookie, + socket, + gameStateRenderer, + gameTimerManager, + timerWorker + ); + } + ); + } }); } @@ -255,7 +316,15 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo socket.on(globals.EVENTS.CHANGE_NAME, (personId, name) => { propagateNameChange(stateBucket.currentGameState, name, personId); updateDOMWithNameChange(stateBucket.currentGameState, gameStateRenderer); - processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer, gameTimerManager, timerWorker); + processGameState( + stateBucket.currentGameState, + stateBucket.currentGameState.client.cookie, + socket, + gameStateRenderer, + gameTimerManager, + timerWorker, + false + ); }); } @@ -263,21 +332,22 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo socket.on(globals.COMMANDS.END_GAME, (people) => { stateBucket.currentGameState.people = people; stateBucket.currentGameState.status = globals.STATUS.ENDED; - processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer, gameTimerManager, timerWorker); + processGameState( + stateBucket.currentGameState, + stateBucket.currentGameState.client.cookie, + socket, + gameStateRenderer, + gameTimerManager, + timerWorker + ); }); } } -function displayStartGamePromptForModerators(gameState, socket) { +function displayStartGamePromptForModerators(gameState, gameStateRenderer) { let div = document.createElement("div"); div.innerHTML = templates.START_GAME_PROMPT; - div.querySelector('#start-game-button').addEventListener('click', (e) => { - e.preventDefault(); - if (confirm("Start the game and deal roles?")) { - socket.emit(globals.COMMANDS.START_GAME, gameState.accessCode); - } - - }); + div.querySelector('#start-game-button').addEventListener('click', gameStateRenderer.startGameHandler); document.body.appendChild(div); } @@ -296,8 +366,15 @@ function validateName(name) { return typeof name === 'string' && name.length > 0 && name.length <= 30; } +function removeStartGameFunctionalityIfPresent(gameStateRenderer) { + document.querySelector("#start-game-prompt")?.removeEventListener('click', gameStateRenderer.startGameHandler); + document.querySelector("#start-game-prompt")?.remove(); +} + function propagateNameChange(gameState, name, personId) { - gameState.client.name = name; + if (gameState.client.id === personId) { + gameState.client.name = name; + } let matchingPerson = gameState.people.find((person) => person.id === personId); if (matchingPerson) { matchingPerson.name = name; @@ -314,20 +391,24 @@ function propagateNameChange(gameState, name, personId) { } function updateDOMWithNameChange(gameState, gameStateRenderer) { - switch (gameState.client.userType) { - case globals.USER_TYPES.PLAYER: - case globals.USER_TYPES.KILLED_PLAYER: - case globals.USER_TYPES.SPECTATOR: - gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); - break; - case globals.USER_TYPES.MODERATOR: - gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(gameState.status === globals.STATUS.ENDED); - break; - case globals.USER_TYPES.TEMPORARY_MODERATOR: - gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); - break; - default: - break; + if (gameState.status === globals.STATUS.IN_PROGRESS) { + switch (gameState.client.userType) { + case globals.USER_TYPES.PLAYER: + case globals.USER_TYPES.KILLED_PLAYER: + case globals.USER_TYPES.SPECTATOR: + gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); + break; + case globals.USER_TYPES.MODERATOR: + gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(gameState.status === globals.STATUS.ENDED); + break; + case globals.USER_TYPES.TEMPORARY_MODERATOR: + gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); + break; + default: + break; + } + } else { + gameStateRenderer.renderLobbyPlayers(); } } diff --git a/client/src/scripts/howToUse.js b/client/src/scripts/howToUse.js new file mode 100644 index 0000000..ee25480 --- /dev/null +++ b/client/src/scripts/howToUse.js @@ -0,0 +1,9 @@ +import { injectNavbar } from "../modules/Navbar.js"; + +const howToUse = () => { injectNavbar(); }; + +if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = howToUse; +} else { + howToUse(); +} diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 17957de..04dbae5 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -221,6 +221,16 @@ input { z-index: 53000; } +#how-to-use-container { + color: #d7d7d7; + display: flex; + flex-direction: column; + margin: 0 auto; + width: 95%; + max-width: 70em; + line-height: 1.5; +} + #desktop-links > a:nth-child(1), #mobile-links a:nth-child(1) { margin: 0 0.5em; width: 50px; diff --git a/client/src/views/how-to-use.html b/client/src/views/how-to-use.html new file mode 100644 index 0000000..5d8d770 --- /dev/null +++ b/client/src/views/how-to-use.html @@ -0,0 +1,47 @@ + + + + + + Create A Game + + + + + + + + + + + + + + + +
    + +
    +

    Purpose of the Application

    +
    This app serves as a means of running games in a social setting where a traditional + running of the game is hindered. This might be when people are meeting virtually, and thus roles can't be handed + out in-person, or when people are in-person but don't have Werewolf cards with them. You can use a deck of regular + playing cards, but it can be difficult for players to remember which card signifies which role, especially if + you want to build a crazy game with many different roles. Even when people are together and have cards, there's + information that would be great to centralize for everyone - a timer, role descriptions, and the in/out status of + players. This app attempts to provide the utilities necessary to run Werewolf with all the different roles you want, + wherever you can access the internet. +
    +

    Creating a Game

    +
    + Creating a game through the app is a 4-step process: +
    +

    Step One: Choosing method of moderation

    +
    + You have two options for moderation during the game. If the moderator isn't playing, you can choose the "dedicated + moderator" option. +
    +
    + + + diff --git a/client/webpack/webpack-dev.config.js b/client/webpack/webpack-dev.config.js index 07ad9c0..a68125c 100644 --- a/client/webpack/webpack-dev.config.js +++ b/client/webpack/webpack-dev.config.js @@ -5,7 +5,8 @@ module.exports = { game: './client/src/scripts/game.js', home: './client/src/scripts/home.js', create: './client/src/scripts/create.js', - notFound: './client/src/scripts/notFound.js' + notFound: './client/src/scripts/notFound.js', + howToUse: './client/src/scripts/howToUse.js' }, output: { path: path.resolve(__dirname, '../dist'), diff --git a/package.json b/package.json index 42b1d8d..463e854 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "start:dev:no-hot-reload": "NODE_ENV=development && node server/main.js", "start:dev:windows": "SET NODE_ENV=development && nodemon server/main.js", "start:dev:windows:no-hot-reload": "SET NODE_ENV=development && node server/main.js", - "start": "NODE_ENV=production node server/main.js -- loglevel=debug port=8080", + "start": "NODE_ENV=production node server/main.js -- loglevel=trace port=8080", "start:windows": "SET NODE_ENV=production && node server/main.js -- loglevel=warn port=8080", "test": "jasmine" }, diff --git a/server/config/globals.js b/server/config/globals.js index 131659e..4ee35bc 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -14,7 +14,8 @@ const globals = { REVEAL_PLAYER: 'revealPlayer', TRANSFER_MODERATOR: 'transferModerator', CHANGE_NAME: 'changeName', - END_GAME: 'endGame' + END_GAME: 'endGame', + FETCH_IN_PROGRESS_STATE: 'fetchInitialInProgressState' }, MESSAGES: { ENTER_NAME: "Client must enter name." @@ -38,6 +39,7 @@ const globals = { }, EVENTS: { PLAYER_JOINED: "playerJoined", + PLAYER_LEFT: "playerLeft", SYNC_GAME_STATE: "syncGameState" }, ENVIRONMENT: { diff --git a/server/main.js b/server/main.js index 94f97e5..cb742a9 100644 --- a/server/main.js +++ b/server/main.js @@ -23,6 +23,10 @@ app.set('port', args.port); const inGameSocketServer = ServerBootstrapper.createSocketServer(main, app, args.port); inGameSocketServer.on('connection', function (socket) { + socket.on("disconnecting", (reason) => { + logger.trace('client socket disconnecting because: ' + reason) + gameManager.removeClientFromLobbyIfApplicable(socket); + }); gameManager.addGameSocketHandlers(inGameSocketServer, socket); }); diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index fe9dd23..6438a62 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -11,7 +11,6 @@ class GameManager { this.environment = environment; this.activeGameRunner = new ActiveGameRunner(logger).getInstance(); this.namespace = null; - //this.gameSocketUtility = GameSocketUtility; } addGameSocketHandlers = (namespace, socket) => { @@ -29,19 +28,36 @@ class GameManager { ); }); + /* this event handler will call handleRequestForGameState() with the 'handleNoMatch' arg as false - only + connections that match a participant in the game at that time will have the game state sent to them. + */ + socket.on(globals.CLIENT_COMMANDS.FETCH_IN_PROGRESS_STATE, (accessCode, personId, ackFn) => { + this.logger.trace('request for game state for accessCode ' + accessCode + ', person ' + personId); + this.handleRequestForGameState( + this.namespace, + this.logger, + this.activeGameRunner, + accessCode, + personId, + ackFn, + socket, + false + ); + }); + socket.on(globals.CLIENT_COMMANDS.GET_ENVIRONMENT, (ackFn) => { ackFn(this.environment); }); socket.on(globals.CLIENT_COMMANDS.START_GAME, (accessCode) => { let game = this.activeGameRunner.activeGames[accessCode]; - if (game) { + if (game && game.isFull) { game.status = globals.STATUS.IN_PROGRESS; - namespace.in(accessCode).emit(globals.EVENTS.SYNC_GAME_STATE); if (game.hasTimer) { game.timerParams.paused = true; this.activeGameRunner.runGame(game, namespace); } + namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.START_GAME); } }); @@ -142,7 +158,7 @@ class GameManager { ackFn("changed"); person.name = data.name.trim(); person.hasEnteredName = true; - socket.to(accessCode).emit(globals.EVENTS.SYNC_GAME_STATE); + namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.CHANGE_NAME, person.id, person.name); } else { ackFn("taken"); } @@ -261,14 +277,14 @@ class GameManager { } - handleRequestForGameState = (namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket) => { + handleRequestForGameState = (namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket, handleNoMatch=true) => { const game = gameRunner.activeGames[accessCode]; if (game) { let matchingPerson = game.people.find((person) => person.cookie === personCookie); if (!matchingPerson) { matchingPerson = game.spectators.find((spectator) => spectator.cookie === personCookie); } - if (game.moderator.cookie === personCookie) { + if (!matchingPerson && game.moderator.cookie === personCookie) { matchingPerson = game.moderator; } if (matchingPerson) { @@ -281,7 +297,7 @@ class GameManager { matchingPerson.socketId = socket.id; ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } - } else { + } else if (handleNoMatch) { this.handleRequestFromNonMatchingPerson(game, socket, gameRunner, ackFn, logger); } } else { @@ -291,7 +307,7 @@ class GameManager { } handleRequestFromNonMatchingPerson = (game, socket, gameRunner, ackFn, logger) => { - let personWithMatchingSocketId = findPersonWithMatchingSocketId(game.people, socket.id); + let personWithMatchingSocketId = findPersonWithMatchingSocketId(game, socket.id); if (personWithMatchingSocketId) { logger.trace("matching person found whose cookie got cleared after establishing a connection to the room: " + personWithMatchingSocketId.name); ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, personWithMatchingSocketId, gameRunner, socket, logger)); @@ -308,7 +324,7 @@ class GameManager { game.isFull = isFull; socket.to(game.accessCode).emit( globals.EVENTS.PLAYER_JOINED, - {name: unassignedPerson.name, userType: unassignedPerson.userType}, + GameStateCurator.mapPerson(unassignedPerson), isFull ); } else { // if the game is full, make them a spectator. @@ -326,6 +342,30 @@ class GameManager { } } + removeClientFromLobbyIfApplicable(socket) { + socket.rooms.forEach((room) => { + if (this.activeGameRunner.activeGames[room]) { + this.logger.trace('disconnected socket is in a game'); + let game = this.activeGameRunner.activeGames[room]; + if (game.status === globals.STATUS.LOBBY) { + let matchingPlayer = findPlayerBySocketId(game.people, socket.id); + if (matchingPlayer) { + this.logger.trace("un-assigning disconnected player: " + matchingPlayer.name); + matchingPlayer.assigned = false; + matchingPlayer.socketId = null; + matchingPlayer.cookie = createRandomId(); + matchingPlayer.hasEnteredName = false; + socket.to(game.accessCode).emit( + globals.EVENTS.PLAYER_LEFT, + GameStateCurator.mapPerson(matchingPlayer) + ); + game.isFull = isGameFull(game); + matchingPlayer.name = UsernameGenerator.generate(); + } + } + } + }) + } } function getRandomInt (max) { @@ -403,8 +443,19 @@ function rejectClientRequestForGameState(acknowledgementFunction) { return acknowledgementFunction(null); } -function findPersonWithMatchingSocketId(people, socketId) { - return people.find((person) => person.socketId === socketId); +function findPersonWithMatchingSocketId(game, socketId) { + let person = game.people.find((person) => person.socketId === socketId); + if (!person) { + person = game.spectators.find((spectator) => spectator.socketId === socketId); + } + if (!person && game.moderator.socketId === socketId) { + person = game.moderator; + } + return person; +} + +function findPlayerBySocketId(people, socketId) { + return people.find((person) => person.socketId === socketId && person.userType === globals.USER_TYPES.PLAYER); } function isGameFull(game) { diff --git a/server/modules/GameStateCurator.js b/server/modules/GameStateCurator.js index b7f4fa6..74859f7 100644 --- a/server/modules/GameStateCurator.js +++ b/server/modules/GameStateCurator.js @@ -24,6 +24,21 @@ const GameStateCurator = { out: person.out, revealed: person.revealed })); + }, + mapPerson: (person) => { + if (person.revealed) { + return { + name: person.name, + id: person.id, + userType: person.userType, + out: person.out, + revealed: person.revealed, + gameRole: person.gameRole, + alignment: person.alignment + }; + } else { + return { name: person.name, id: person.id, userType: person.userType, out: person.out, revealed: person.revealed }; + } } } @@ -48,7 +63,7 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { let state = { accessCode: game.accessCode, status: game.status, - moderator: mapPerson(game.moderator), + moderator: GameStateCurator.mapPerson(game.moderator), client: client, deck: game.deck, people: game.people @@ -56,7 +71,7 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { return person.assigned === true }) .map((filteredPerson) => - mapPerson(filteredPerson) + GameStateCurator.mapPerson(filteredPerson) ), timerParams: game.timerParams, isFull: game.isFull, @@ -69,7 +84,7 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { return { accessCode: game.accessCode, status: game.status, - moderator: mapPerson(game.moderator), + moderator: GameStateCurator.mapPerson(game.moderator), client: client, deck: game.deck, people: GameStateCurator.mapPeopleForModerator(game.people, client), @@ -81,14 +96,14 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { return { accessCode: game.accessCode, status: game.status, - moderator: mapPerson(game.moderator), + moderator: GameStateCurator.mapPerson(game.moderator), client: client, deck: game.deck, people: game.people .filter((person) => { return person.assigned === true }) - .map((filteredPerson) => mapPerson(filteredPerson)), + .map((filteredPerson) => GameStateCurator.mapPerson(filteredPerson)), timerParams: game.timerParams, isFull: game.isFull } @@ -96,14 +111,14 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { return { accessCode: game.accessCode, status: game.status, - moderator: mapPerson(game.moderator), + moderator: GameStateCurator.mapPerson(game.moderator), client: client, deck: game.deck, people: game.people .filter((person) => { return person.assigned === true }) - .map((filteredPerson) => mapPerson(filteredPerson)), + .map((filteredPerson) => GameStateCurator.mapPerson(filteredPerson)), timerParams: game.timerParams, isFull: game.isFull, } @@ -112,20 +127,4 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { } } -function mapPerson(person) { - if (person.revealed) { - return { - name: person.name, - id: person.id, - userType: person.userType, - out: person.out, - revealed: person.revealed, - gameRole: person.gameRole, - alignment: person.alignment - }; - } else { - return { name: person.name, id: person.id, userType: person.userType, out: person.out, revealed: person.revealed }; - } -} - module.exports = GameStateCurator; diff --git a/server/routes/router.js b/server/routes/router.js index 473368a..d60c298 100644 --- a/server/routes/router.js +++ b/server/routes/router.js @@ -10,6 +10,10 @@ router.get('/create', function (request, response) { response.sendFile(path.join(__dirname, '../../client/src/views/create.html')); }); +router.get('/how-to-use', function (request, response) { + response.sendFile(path.join(__dirname, '../../client/src/views/how-to-use.html')); +}); + router.get('/game/:code', function (request, response) { response.sendFile(path.join(__dirname, '../../client/src/views/game.html')); }); From c0c4ccdd742c8136afd664f5e96f5633c3e83cee Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Tue, 11 Jan 2022 19:57:13 -0500 Subject: [PATCH 58/60] editing custom roles --- README.md | 1 - client/src/images/screenshots/player.PNG | Bin 63302 -> 69921 bytes client/src/images/tutorial/custom-roles.PNG | Bin 0 -> 23992 bytes client/src/images/tutorial/default-roles.PNG | Bin 0 -> 23818 bytes .../src/images/tutorial/moderation-option.png | Bin 0 -> 53186 bytes client/src/modules/DeckStateManager.js | 9 ++ client/src/modules/GameCreationStepManager.js | 82 ++++++++++++++---- client/src/modules/GameStateRenderer.js | 1 + client/src/modules/StateBucket.js | 3 +- client/src/scripts/game.js | 47 ++++------ client/src/styles/GLOBAL.css | 34 +++++++- client/src/styles/game.css | 1 - client/src/views/create.html | 8 +- client/src/views/how-to-use.html | 44 +++++++++- 14 files changed, 172 insertions(+), 58 deletions(-) create mode 100644 client/src/images/tutorial/custom-roles.PNG create mode 100644 client/src/images/tutorial/default-roles.PNG create mode 100644 client/src/images/tutorial/moderation-option.png diff --git a/README.md b/README.md index 66bf448..eb6dcff 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ smoothly when you don't have a deck, or when you and your friends are together v After a long hiatus from maintaining the application, I have come back and undertaken a large-scale redesign, rewriting most of the code and producing a result that I believe is more stable and has much more sensible client-server interaction. -It's a shame that my first attempt is what ended up in Github's Artic Code Vault :) ![player](./client/src/images/screenshots/player.PNG) diff --git a/client/src/images/screenshots/player.PNG b/client/src/images/screenshots/player.PNG index 79d25c722d99b1030accb2d11460f9fd971a9241..45054c10c14e56bc9240b4ec9b1bcd6b211e8e60 100644 GIT binary patch literal 69921 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

    )v(m{g=g@^PFct&))m9%c)!q3-MV28^2N=_ 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 J>8}oV{sYoGLTdm3 literal 63302 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 diff --git a/client/src/images/tutorial/custom-roles.PNG b/client/src/images/tutorial/custom-roles.PNG new file mode 100644 index 0000000000000000000000000000000000000000..e5413d5e78b766c07bbca08c1ecb9748a9a87ebf GIT binary patch literal 23992 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 P(hX-^Jx*4f2)O$%BuEn8 literal 0 HcmV?d00001 diff --git a/client/src/images/tutorial/default-roles.PNG b/client/src/images/tutorial/default-roles.PNG new file mode 100644 index 0000000000000000000000000000000000000000..57f9c7c7c75e4067a2f02180dce1d32de6fa0cb8 GIT binary patch literal 23818 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 card.custom === true)))); } + updateCustomRoleOption(option, name, description, team) { + option.role = name; + option.description = description; + option.team = team; + localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(this.customRoleOptions.concat(this.deck.filter(card => card.custom === true)))); + } + removeFromCustomRoleOptions(name) { let option = this.customRoleOptions.find((option) => option.role === name); if (option) { diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index c6acab6..f06a0cb 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -470,32 +470,41 @@ function constructCompactDeckBuilderElement(card, deckManager) { } function initializeRemainingEventListeners(deckManager) { - document.getElementById("add-role-form").onsubmit = (e) => { + document.getElementById("role-form").onsubmit = (e) => { e.preventDefault(); let name = document.getElementById("role-name").value.trim(); let description = document.getElementById("role-description").value.trim(); let team = document.getElementById("role-alignment").value.toLowerCase().trim(); - if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { // confirm there is no existing custom role with the same name - if (name.length > 40) { - toast('Your name is too long (max 40 characters).', "error", true); - return; + if (deckManager.createMode) { + if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { // confirm there is no existing custom role with the same name + processNewCustomRoleSubmission(name, description, team, deckManager,false); + } else { + toast("There is already a role with this name", "error", true, true, 3); } - if (description.length > 500) { - toast('Your description is too long (max 500 characters).', "error", true); - return; - } - deckManager.addToCustomRoleOptions({role: name, description: description, team: team, custom: true}); - updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")) - ModalManager.dispelModal("add-role-modal", "modal-background"); - toast("Role Created", "success", true); } else { - toast("There is already a role with this name", "error", true, true, 3); + let option = deckManager.getCustomRoleOption(deckManager.currentlyEditingRoleName); + if (name === option.role) { // did they edit the name? + processNewCustomRoleSubmission(name, description, team, deckManager,true, option); + } else { + if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { + processNewCustomRoleSubmission(name, description, team, deckManager, true, option); + } else { + toast("There is already a role with this name", "error", true, true, 3); + } + } } } document.getElementById("custom-role-btn").addEventListener( "click", () => { + let createBtn = document.getElementById("create-role-button"); + createBtn.setAttribute("value", "Create"); + deckManager.createMode = true; + deckManager.currentlyEditingRoleName = null; + document.getElementById("role-name").value = ""; + document.getElementById("role-alignment").value = globals.ALIGNMENT.GOOD; + document.getElementById("role-description").value = ""; ModalManager.displayModal( - "add-role-modal", + "role-modal", "modal-background", "close-modal-button" ) @@ -503,6 +512,28 @@ function initializeRemainingEventListeners(deckManager) { ) } +function processNewCustomRoleSubmission(name, description, team, deckManager, isUpdate, option=null) { + if (name.length > 40) { + toast('Your name is too long (max 40 characters).', "error", true); + return; + } + if (description.length > 500) { + toast('Your description is too long (max 500 characters).', "error", true); + return; + } + if (isUpdate) { + deckManager.updateCustomRoleOption(option, name, description, team); + ModalManager.dispelModal("role-modal", "modal-background"); + toast("Role Updated", "success", true); + } else { + deckManager.addToCustomRoleOptions({role: name, description: description, team: team, custom: true}); + ModalManager.dispelModal("role-modal", "modal-background"); + toast("Role Created", "success", true); + } + + updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")); +} + function updateCustomRoleOptionsList(deckManager, selectEl) { document.querySelectorAll('#deck-select .deck-select-role').forEach(e => e.remove()); addOptionsToList(deckManager, selectEl); @@ -568,17 +599,34 @@ function addCustomRoleEventListeners(deckManager, select) { document.getElementById("custom-role-info-modal-description").innerText = option.description; alignmentEl.innerText = option.team; ModalManager.displayModal("custom-role-info-modal", "modal-background", "close-custom-role-info-modal-button"); - }) + }); + + role.querySelector('.deck-select-edit').addEventListener('click', (e) => { + e.preventDefault(); + let option = deckManager.getCustomRoleOption(name); + document.getElementById("role-name").value = option.role; + document.getElementById("role-alignment").value = option.team; + document.getElementById("role-description").value = option.description; + deckManager.createMode = false; + deckManager.currentlyEditingRoleName = option.role; + let createBtn = document.getElementById("create-role-button"); + createBtn.setAttribute("value", "Update"); + ModalManager.displayModal("role-modal", "modal-background", "close-modal-button"); + }); }); } +function displayCustomRoleModalInAddOrEditMode() { + let ad +} + function updateDeckStatus(deckManager) { document.querySelectorAll('.deck-role').forEach((el) => el.remove()); document.getElementById("deck-count").innerText = deckManager.getDeckSize() + " Players"; if (deckManager.getDeckSize() === 0) { let placeholder = document.createElement("div"); placeholder.setAttribute("id", "deck-list-placeholder"); - placeholder.innerText = "Add a card from the included roles below."; + placeholder.innerText = "Add a card from the available roles below."; document.getElementById("deck-list").appendChild(placeholder); } else { if (document.getElementById("deck-list-placeholder")) { diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 3b95c6d..1b91c39 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -215,6 +215,7 @@ export class GameStateRenderer { } displayAvailableModerators() { + document.getElementById("transfer-mod-modal-content").innerText = ""; document.querySelectorAll('.potential-moderator').forEach((el) => { let pointer = el.dataset.pointer; if (pointer && this.transferModHandlers[pointer]) { diff --git a/client/src/modules/StateBucket.js b/client/src/modules/StateBucket.js index fa71c56..7571fc2 100644 --- a/client/src/modules/StateBucket.js +++ b/client/src/modules/StateBucket.js @@ -4,6 +4,5 @@ */ export const stateBucket = { currentGameState: null, - timerWorker: null, - gameStateRequestInFlight: false + timerWorker: null } diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index 299f199..b27a201 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -13,9 +13,7 @@ const game = () => { injectNavbar(); let timerWorker; const socket = io('/in-game'); - stateBucket.gameStateRequestInFlight = false; socket.on('disconnect', () => { - stateBucket.gameStateRequestInFlight = false; if (timerWorker) { timerWorker.terminate(); } @@ -24,10 +22,8 @@ const game = () => { socket.on('connect', () => { console.log('fired connect event'); socket.emit(globals.COMMANDS.GET_ENVIRONMENT, function (returnedEnvironment) { - if (!stateBucket.gameStateRequestInFlight) { - timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); - prepareGamePage(returnedEnvironment, socket, timerWorker); - } + timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); + prepareGamePage(returnedEnvironment, socket, timerWorker); }); }) }; @@ -37,9 +33,7 @@ function prepareGamePage(environment, socket, timerWorker) { const splitUrl = window.location.href.split('/game/'); const accessCode = splitUrl[1]; if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) { - stateBucket.gameStateRequestInFlight = true; socket.emit(globals.COMMANDS.FETCH_GAME_STATE, accessCode, userId, function (gameState) { - stateBucket.gameStateRequestInFlight = false; stateBucket.currentGameState = gameState; document.querySelector('.spinner-container')?.remove(); document.querySelector('.spinner-background')?.remove(); @@ -214,7 +208,6 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo stateBucket.currentGameState.accessCode, stateBucket.currentGameState.client.cookie, function (gameState) { - stateBucket.gameStateRequestInFlight = false; stateBucket.currentGameState = gameState; processGameState( stateBucket.currentGameState, @@ -230,26 +223,22 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo } if (!socket.hasListeners(globals.EVENTS.SYNC_GAME_STATE)) { socket.on(globals.EVENTS.SYNC_GAME_STATE, () => { - if (!stateBucket.gameStateRequestInFlight) { - stateBucket.gameStateRequestInFlight = true; - socket.emit( - globals.COMMANDS.FETCH_IN_PROGRESS_STATE, - stateBucket.currentGameState.accessCode, - stateBucket.currentGameState.client.cookie, - function (gameState) { - stateBucket.gameStateRequestInFlight = false; - stateBucket.currentGameState = gameState; - processGameState( - stateBucket.currentGameState, - gameState.client.cookie, - socket, - gameStateRenderer, - gameTimerManager, - timerWorker - ); - } - ); - } + socket.emit( + globals.COMMANDS.FETCH_IN_PROGRESS_STATE, + stateBucket.currentGameState.accessCode, + stateBucket.currentGameState.client.cookie, + function (gameState) { + stateBucket.currentGameState = gameState; + processGameState( + stateBucket.currentGameState, + gameState.client.cookie, + socket, + gameStateRenderer, + gameTimerManager, + timerWorker + ); + } + ); }); } diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 04dbae5..4f6fcb5 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -180,6 +180,26 @@ button { border: 2px solid #1c8a36; } +.emphasized { + font-weight: bold; + color: #00a718; +} + +#how-to-use-container img { + max-width: 98%; + border: 1px solid #57566a; + border-radius: 3px; + width: 45em; + margin: 0 auto; + /* justify-self: center; */ + /* align-self: center; */ + display: flex; +} + +#how-to-use-container h1 { + font-family: signika-negative, sans-serif; +} + input { padding: 10px; } @@ -227,10 +247,22 @@ input { flex-direction: column; margin: 0 auto; width: 95%; - max-width: 70em; + max-width: 64em; line-height: 1.5; } +#how-to-use-container h3 { + font-size: 25px; +} + +#how-to-use-container h1 { + font-size: 40px !important; +} + +.tutorial-image-small { + width: 30em !important; +} + #desktop-links > a:nth-child(1), #mobile-links a:nth-child(1) { margin: 0 0.5em; width: 50px; diff --git a/client/src/styles/game.css b/client/src/styles/game.css index f53a7ae..aee49c9 100644 --- a/client/src/styles/game.css +++ b/client/src/styles/game.css @@ -694,7 +694,6 @@ label[for='moderator'] { button { font-size: 16px; - padding: 5px; } #play-pause img { diff --git a/client/src/views/create.html b/client/src/views/create.html index 7b11d76..cc0bbc2 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -23,8 +23,8 @@

    - ' + "
    " + - "

    +

    " + - "
    "; + '

    +

    ' + + '
    '; cardContainer.querySelector('.card-role').innerText = card.role; cardContainer.title = card.role; @@ -455,7 +452,7 @@ function constructCompactDeckBuilderElement(card, deckManager) { updateDeckStatus(deckManager); cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; if (deckManager.getCard(card.role).quantity > 0) { - document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.add('selected-card') + document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.add('selected-card'); } }); cardContainer.querySelector('.compact-card-left').addEventListener('click', () => { @@ -463,95 +460,95 @@ function constructCompactDeckBuilderElement(card, deckManager) { updateDeckStatus(deckManager); cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; if (deckManager.getCard(card.role).quantity === 0) { - document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.remove('selected-card') + document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.remove('selected-card'); } }); return cardContainer; } -function initializeRemainingEventListeners(deckManager) { - document.getElementById("role-form").onsubmit = (e) => { +function initializeRemainingEventListeners (deckManager) { + document.getElementById('role-form').onsubmit = (e) => { e.preventDefault(); - let name = document.getElementById("role-name").value.trim(); - let description = document.getElementById("role-description").value.trim(); - let team = document.getElementById("role-alignment").value.toLowerCase().trim(); + const name = document.getElementById('role-name').value.trim(); + const description = document.getElementById('role-description').value.trim(); + const team = document.getElementById('role-alignment').value.toLowerCase().trim(); if (deckManager.createMode) { if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { // confirm there is no existing custom role with the same name - processNewCustomRoleSubmission(name, description, team, deckManager,false); + processNewCustomRoleSubmission(name, description, team, deckManager, false); } else { - toast("There is already a role with this name", "error", true, true, 3); + toast('There is already a role with this name', 'error', true, true, 3); } } else { - let option = deckManager.getCustomRoleOption(deckManager.currentlyEditingRoleName); + const option = deckManager.getCustomRoleOption(deckManager.currentlyEditingRoleName); if (name === option.role) { // did they edit the name? - processNewCustomRoleSubmission(name, description, team, deckManager,true, option); + processNewCustomRoleSubmission(name, description, team, deckManager, true, option); } else { if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { processNewCustomRoleSubmission(name, description, team, deckManager, true, option); } else { - toast("There is already a role with this name", "error", true, true, 3); + toast('There is already a role with this name', 'error', true, true, 3); } } } - } - document.getElementById("custom-role-btn").addEventListener( - "click", () => { - let createBtn = document.getElementById("create-role-button"); - createBtn.setAttribute("value", "Create"); + }; + document.getElementById('custom-role-btn').addEventListener( + 'click', () => { + const createBtn = document.getElementById('create-role-button'); + createBtn.setAttribute('value', 'Create'); deckManager.createMode = true; deckManager.currentlyEditingRoleName = null; - document.getElementById("role-name").value = ""; - document.getElementById("role-alignment").value = globals.ALIGNMENT.GOOD; - document.getElementById("role-description").value = ""; + document.getElementById('role-name').value = ''; + document.getElementById('role-alignment').value = globals.ALIGNMENT.GOOD; + document.getElementById('role-description').value = ''; ModalManager.displayModal( - "role-modal", - "modal-background", - "close-modal-button" - ) + 'role-modal', + 'modal-background', + 'close-modal-button' + ); } - ) + ); } -function processNewCustomRoleSubmission(name, description, team, deckManager, isUpdate, option=null) { +function processNewCustomRoleSubmission (name, description, team, deckManager, isUpdate, option = null) { if (name.length > 40) { - toast('Your name is too long (max 40 characters).', "error", true); + toast('Your name is too long (max 40 characters).', 'error', true); return; } if (description.length > 500) { - toast('Your description is too long (max 500 characters).', "error", true); + toast('Your description is too long (max 500 characters).', 'error', true); return; } if (isUpdate) { deckManager.updateCustomRoleOption(option, name, description, team); - ModalManager.dispelModal("role-modal", "modal-background"); - toast("Role Updated", "success", true); + ModalManager.dispelModal('role-modal', 'modal-background'); + toast('Role Updated', 'success', true); } else { - deckManager.addToCustomRoleOptions({role: name, description: description, team: team, custom: true}); - ModalManager.dispelModal("role-modal", "modal-background"); - toast("Role Created", "success", true); + deckManager.addToCustomRoleOptions({ role: name, description: description, team: team, custom: true }); + ModalManager.dispelModal('role-modal', 'modal-background'); + toast('Role Created', 'success', true); } - updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")); + updateCustomRoleOptionsList(deckManager, document.getElementById('deck-select')); } -function updateCustomRoleOptionsList(deckManager, selectEl) { +function updateCustomRoleOptionsList (deckManager, selectEl) { document.querySelectorAll('#deck-select .deck-select-role').forEach(e => e.remove()); addOptionsToList(deckManager, selectEl); } -function addOptionsToList(deckManager, selectEl) { - let options = deckManager.getCurrentCustomRoleOptions(); +function addOptionsToList (deckManager, selectEl) { + const options = deckManager.getCurrentCustomRoleOptions(); options.sort((a, b) => { if (a.team !== b.team) { return a.team === globals.ALIGNMENT.GOOD ? 1 : -1; } return a.role.localeCompare(b.role); }); - for (let i = 0; i < options.length; i ++) { - let optionEl = document.createElement("div"); + for (let i = 0; i < options.length; i++) { + const optionEl = document.createElement('div'); optionEl.innerHTML = templates.DECK_SELECT_ROLE; optionEl.classList.add('deck-select-role'); - let alignmentClass = options[i].team === globals.ALIGNMENT.GOOD ? globals.ALIGNMENT.GOOD : globals.ALIGNMENT.EVIL + const alignmentClass = options[i].team === globals.ALIGNMENT.GOOD ? globals.ALIGNMENT.GOOD : globals.ALIGNMENT.EVIL; optionEl.classList.add(alignmentClass); optionEl.querySelector('.deck-select-role-name').innerText = options[i].role; selectEl.appendChild(optionEl); @@ -560,19 +557,19 @@ function addOptionsToList(deckManager, selectEl) { addCustomRoleEventListeners(deckManager, selectEl); } -function addCustomRoleEventListeners(deckManager, select) { +function addCustomRoleEventListeners (deckManager, select) { document.querySelectorAll('.deck-select-role').forEach((role) => { - let name = role.querySelector('.deck-select-role-name').innerText; + const name = role.querySelector('.deck-select-role-name').innerText; role.querySelector('.deck-select-include').addEventListener('click', (e) => { e.preventDefault(); if (!deckManager.getCard(name)) { deckManager.addToDeck(name); - let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(name), deckManager); + const cardEl = constructCompactDeckBuilderElement(deckManager.getCard(name), deckManager); toast('"' + name + '" made available below.', 'success', true, true, 4); if (deckManager.getCard(name).team === globals.ALIGNMENT.GOOD) { - document.getElementById("deck-good").appendChild(cardEl); + document.getElementById('deck-good').appendChild(cardEl); } else { - document.getElementById("deck-evil").appendChild(cardEl); + document.getElementById('deck-evil').appendChild(cardEl); } updateCustomRoleOptionsList(deckManager, select); } else { @@ -589,52 +586,48 @@ function addCustomRoleEventListeners(deckManager, select) { }); role.querySelector('.deck-select-info').addEventListener('click', (e) => { - let alignmentEl = document.getElementById("custom-role-info-modal-alignment"); + const alignmentEl = document.getElementById('custom-role-info-modal-alignment'); alignmentEl.classList.remove(globals.ALIGNMENT.GOOD); alignmentEl.classList.remove(globals.ALIGNMENT.EVIL); e.preventDefault(); - let option = deckManager.getCustomRoleOption(name); - document.getElementById("custom-role-info-modal-name").innerText = name; + const option = deckManager.getCustomRoleOption(name); + document.getElementById('custom-role-info-modal-name').innerText = name; alignmentEl.classList.add(option.team); - document.getElementById("custom-role-info-modal-description").innerText = option.description; + document.getElementById('custom-role-info-modal-description').innerText = option.description; alignmentEl.innerText = option.team; - ModalManager.displayModal("custom-role-info-modal", "modal-background", "close-custom-role-info-modal-button"); + ModalManager.displayModal('custom-role-info-modal', 'modal-background', 'close-custom-role-info-modal-button'); }); role.querySelector('.deck-select-edit').addEventListener('click', (e) => { e.preventDefault(); - let option = deckManager.getCustomRoleOption(name); - document.getElementById("role-name").value = option.role; - document.getElementById("role-alignment").value = option.team; - document.getElementById("role-description").value = option.description; + const option = deckManager.getCustomRoleOption(name); + document.getElementById('role-name').value = option.role; + document.getElementById('role-alignment').value = option.team; + document.getElementById('role-description').value = option.description; deckManager.createMode = false; deckManager.currentlyEditingRoleName = option.role; - let createBtn = document.getElementById("create-role-button"); - createBtn.setAttribute("value", "Update"); - ModalManager.displayModal("role-modal", "modal-background", "close-modal-button"); + const createBtn = document.getElementById('create-role-button'); + createBtn.setAttribute('value', 'Update'); + ModalManager.displayModal('role-modal', 'modal-background', 'close-modal-button'); }); }); } -function displayCustomRoleModalInAddOrEditMode() { - let ad -} - -function updateDeckStatus(deckManager) { +function updateDeckStatus (deckManager) { document.querySelectorAll('.deck-role').forEach((el) => el.remove()); - document.getElementById("deck-count").innerText = deckManager.getDeckSize() + " Players"; + document.getElementById('deck-count').innerText = deckManager.getDeckSize() + ' Players'; if (deckManager.getDeckSize() === 0) { - let placeholder = document.createElement("div"); - placeholder.setAttribute("id", "deck-list-placeholder"); - placeholder.innerText = "Add a card from the available roles below."; - document.getElementById("deck-list").appendChild(placeholder); + const placeholder = document.createElement('div'); + placeholder.setAttribute('id', 'deck-list-placeholder'); + placeholder.innerText = 'Add a card from the available roles below.'; + document.getElementById('deck-list').appendChild(placeholder); } else { - if (document.getElementById("deck-list-placeholder")) { - document.getElementById("deck-list-placeholder").remove(); + if (document.getElementById('deck-list-placeholder')) { + document.getElementById('deck-list-placeholder').remove(); } - for (let card of deckManager.getCurrentDeck()) { + for (const card of deckManager.getCurrentDeck()) { if (card.quantity > 0) { - let roleEl = document.createElement("div"); + const roleEl = document.createElement('div'); roleEl.classList.add('deck-role'); if (card.team === globals.ALIGNMENT.GOOD) { roleEl.classList.add(globals.ALIGNMENT.GOOD); @@ -642,12 +635,12 @@ function updateDeckStatus(deckManager) { roleEl.classList.add(globals.ALIGNMENT.EVIL); } roleEl.innerText = card.quantity + 'x ' + card.role; - document.getElementById("deck-list").appendChild(roleEl); + document.getElementById('deck-list').appendChild(roleEl); } } } } -function hasTimer(hours, minutes) { +function hasTimer (hours, minutes) { return (!isNaN(hours) || !isNaN(minutes)); } diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 1b91c39..29ac700 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -1,10 +1,10 @@ -import { globals } from "../config/globals.js"; -import { toast } from "./Toast.js"; -import {templates} from "./Templates.js"; -import {ModalManager} from "./ModalManager.js"; +import { globals } from '../config/globals.js'; +import { toast } from './Toast.js'; +import { templates } from './Templates.js'; +import { ModalManager } from './ModalManager.js'; export class GameStateRenderer { - constructor(stateBucket, socket) { + constructor (stateBucket, socket) { this.stateBucket = stateBucket; this.socket = socket; this.killPlayerHandlers = {}; @@ -12,38 +12,38 @@ export class GameStateRenderer { this.transferModHandlers = {}; this.startGameHandler = (e) => { e.preventDefault(); - if (confirm("Start the game and deal roles?")) { + if (confirm('Start the game and deal roles?')) { socket.emit(globals.COMMANDS.START_GAME, this.stateBucket.currentGameState.accessCode); } - } + }; } - renderLobbyPlayers() { - document.querySelectorAll('.lobby-player').forEach((el) => el.remove()) - let lobbyPlayersContainer = document.getElementById("lobby-players"); + renderLobbyPlayers () { + document.querySelectorAll('.lobby-player').forEach((el) => el.remove()); + const lobbyPlayersContainer = document.getElementById('lobby-players'); if (this.stateBucket.currentGameState.moderator.userType === globals.USER_TYPES.MODERATOR) { lobbyPlayersContainer.appendChild( renderLobbyPerson( this.stateBucket.currentGameState.moderator.name, this.stateBucket.currentGameState.moderator.userType ) - ) + ); } - for (let person of this.stateBucket.currentGameState.people) { - lobbyPlayersContainer.appendChild(renderLobbyPerson(person.name,person.userType)) + for (const person of this.stateBucket.currentGameState.people) { + lobbyPlayersContainer.appendChild(renderLobbyPerson(person.name, person.userType)); } - let playerCount = this.stateBucket.currentGameState.people.length; + const playerCount = this.stateBucket.currentGameState.people.length; document.querySelector("label[for='lobby-players']").innerText = - "Participants (" + playerCount + "/" + getGameSize(this.stateBucket.currentGameState.deck) + " Players)"; + 'Participants (' + playerCount + '/' + getGameSize(this.stateBucket.currentGameState.deck) + ' Players)'; } - renderLobbyHeader() { + renderLobbyHeader () { removeExistingTitle(); - let title = document.createElement("h1"); - title.innerText = "Lobby"; - document.getElementById("game-title").appendChild(title); - let gameLinkContainer = document.getElementById("game-link"); - let linkDiv = document.createElement("div"); + const title = document.createElement('h1'); + title.innerText = 'Lobby'; + document.getElementById('game-title').appendChild(title); + const gameLinkContainer = document.getElementById('game-link'); + const linkDiv = document.createElement('div'); linkDiv.innerText = window.location; gameLinkContainer.prepend(linkDiv); gameLinkContainer.addEventListener('click', () => { @@ -51,27 +51,27 @@ export class GameStateRenderer { toast('Link copied!', 'success', true); }); }); - let copyImg = document.createElement("img"); - copyImg.setAttribute("src", "../images/copy.svg"); + const copyImg = document.createElement('img'); + copyImg.setAttribute('src', '../images/copy.svg'); gameLinkContainer.appendChild(copyImg); - let time = document.getElementById("game-time"); - let playerCount = document.getElementById("game-player-count"); - playerCount.innerText = getGameSize(this.stateBucket.currentGameState.deck) + ' Players' + const time = document.getElementById('game-time'); + const playerCount = document.getElementById('game-player-count'); + playerCount.innerText = getGameSize(this.stateBucket.currentGameState.deck) + ' Players'; if (this.stateBucket.currentGameState.timerParams) { - let timeString = ""; - let hours = this.stateBucket.currentGameState.timerParams.hours; - let minutes = this.stateBucket.currentGameState.timerParams.minutes + let timeString = ''; + const hours = this.stateBucket.currentGameState.timerParams.hours; + const minutes = this.stateBucket.currentGameState.timerParams.minutes; if (hours) { timeString += hours > 1 ? hours + ' hours ' - : hours + ' hour ' + : hours + ' hour '; } if (minutes) { timeString += minutes > 1 ? minutes + ' minutes ' - : minutes + ' minute ' + : minutes + ' minute '; } time.innerText = timeString; } else { @@ -79,76 +79,75 @@ export class GameStateRenderer { } } - renderLobbyFooter() { - let gameDeckContainer = document.getElementById("game-deck"); - for (let card of this.stateBucket.currentGameState.deck) { - let cardEl = document.createElement("div"); + renderLobbyFooter () { + for (const card of this.stateBucket.currentGameState.deck) { + const cardEl = document.createElement('div'); cardEl.innerText = card.quantity + 'x ' + card.role; - cardEl.classList.add('lobby-card') + cardEl.classList.add('lobby-card'); } } - renderGameHeader() { + renderGameHeader () { removeExistingTitle(); // let title = document.createElement("h1"); // title.innerText = "Game"; // document.getElementById("game-title").appendChild(title); } - renderModeratorView() { + renderModeratorView () { createEndGamePromptComponent(this.socket, this.stateBucket); - let modTransferButton = document.getElementById("mod-transfer-button"); + const modTransferButton = document.getElementById('mod-transfer-button'); modTransferButton.addEventListener( - "click", () => { + 'click', () => { this.displayAvailableModerators(); ModalManager.displayModal( - "transfer-mod-modal", - "transfer-mod-modal-background", - "close-mod-transfer-modal-button" - ) + 'transfer-mod-modal', + 'transfer-mod-modal-background', + 'close-mod-transfer-modal-button' + ); } - ) + ); this.renderPlayersWithRoleAndAlignmentInfo(); } - renderTempModView() { - createEndGamePromptComponent(this.socket, this.stateBucket) + renderTempModView () { + createEndGamePromptComponent(this.socket, this.stateBucket); renderPlayerRole(this.stateBucket.currentGameState); this.renderPlayersWithNoRoleInformationUnlessRevealed(true); } - renderPlayerView(isKilled=false) { + renderPlayerView (isKilled = false) { if (isKilled) { - let clientUserType = document.getElementById("client-user-type"); + const clientUserType = document.getElementById('client-user-type'); if (clientUserType) { - clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80' + clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80'; } } renderPlayerRole(this.stateBucket.currentGameState); this.renderPlayersWithNoRoleInformationUnlessRevealed(false); } - renderSpectatorView() { + renderSpectatorView () { this.renderPlayersWithNoRoleInformationUnlessRevealed(); } - refreshPlayerList(isModerator) { + refreshPlayerList (isModerator) { if (isModerator) { - this.renderPlayersWithRoleAndAlignmentInfo() + this.renderPlayersWithRoleAndAlignmentInfo(); } else { this.renderPlayersWithNoRoleInformationUnlessRevealed(); } } - renderPlayersWithRoleAndAlignmentInfo() { + renderPlayersWithRoleAndAlignmentInfo () { removeExistingPlayerElements(this.killPlayerHandlers, this.revealRoleHandlers); this.stateBucket.currentGameState.people.sort((a, b) => { return a.name >= b.name ? 1 : -1; }); - let teamGood = this.stateBucket.currentGameState.people.filter((person) => person.alignment === globals.ALIGNMENT.GOOD); - let teamEvil = this.stateBucket.currentGameState.people.filter((person) => person.alignment === globals.ALIGNMENT.EVIL); + const teamGood = this.stateBucket.currentGameState.people.filter((person) => person.alignment === globals.ALIGNMENT.GOOD); + const teamEvil = this.stateBucket.currentGameState.people.filter((person) => person.alignment === globals.ALIGNMENT.EVIL); renderGroupOfPlayers( teamEvil, this.killPlayerHandlers, @@ -167,16 +166,15 @@ export class GameStateRenderer { this.stateBucket.currentGameState.moderator.userType, this.socket ); - document.getElementById("players-alive-label").innerText = - 'Players: ' + this.stateBucket.currentGameState.people.filter((person) => !person.out).length + ' / ' - + this.stateBucket.currentGameState.people.length + ' Alive'; - + document.getElementById('players-alive-label').innerText = + 'Players: ' + this.stateBucket.currentGameState.people.filter((person) => !person.out).length + ' / ' + + this.stateBucket.currentGameState.people.length + ' Alive'; } - renderPlayersWithNoRoleInformationUnlessRevealed(tempMod = false) { + renderPlayersWithNoRoleInformationUnlessRevealed (tempMod = false) { if (tempMod) { document.querySelectorAll('.game-player').forEach((el) => { - let pointer = el.dataset.pointer; + const pointer = el.dataset.pointer; if (pointer && this.killPlayerHandlers[pointer]) { el.removeEventListener('click', this.killPlayerHandlers[pointer]); delete this.killPlayerHandlers[pointer]; @@ -190,7 +188,7 @@ export class GameStateRenderer { } document.querySelectorAll('.game-player').forEach((el) => el.remove()); sortPeopleByStatus(this.stateBucket.currentGameState.people); - let modType = tempMod ? this.stateBucket.currentGameState.moderator.userType : null; + const modType = tempMod ? this.stateBucket.currentGameState.moderator.userType : null; renderGroupOfPlayers( this.stateBucket.currentGameState.people, this.killPlayerHandlers, @@ -200,24 +198,23 @@ export class GameStateRenderer { modType, this.socket ); - document.getElementById("players-alive-label").innerText = - 'Players: ' + this.stateBucket.currentGameState.people.filter((person) => !person.out).length + ' / ' - + this.stateBucket.currentGameState.people.length + ' Alive'; - + document.getElementById('players-alive-label').innerText = + 'Players: ' + this.stateBucket.currentGameState.people.filter((person) => !person.out).length + ' / ' + + this.stateBucket.currentGameState.people.length + ' Alive'; } - updatePlayerCardToKilledState() { - document.querySelector('#role-image').classList.add("killed-card"); - document.getElementById("role-image").setAttribute( + updatePlayerCardToKilledState () { + document.querySelector('#role-image').classList.add('killed-card'); + document.getElementById('role-image').setAttribute( 'src', '../images/tombstone.png' ); } - displayAvailableModerators() { - document.getElementById("transfer-mod-modal-content").innerText = ""; + displayAvailableModerators () { + document.getElementById('transfer-mod-modal-content').innerText = ''; document.querySelectorAll('.potential-moderator').forEach((el) => { - let pointer = el.dataset.pointer; + const pointer = el.dataset.pointer; if (pointer && this.transferModHandlers[pointer]) { el.removeEventListener('click', this.transferModHandlers[pointer]); delete this.transferModHandlers[pointer]; @@ -238,28 +235,28 @@ export class GameStateRenderer { ); if (document.querySelectorAll('.potential-moderator').length === 0) { - document.getElementById("transfer-mod-modal-content").innerText = "There is nobody available to transfer to." + document.getElementById('transfer-mod-modal-content').innerText = 'There is nobody available to transfer to.'; } } - renderEndOfGame() { + renderEndOfGame () { this.renderPlayersWithNoRoleInformationUnlessRevealed(); } } -function renderPotentialMods(gameState, group, transferModHandlers, socket) { - let modalContent = document.getElementById("transfer-mod-modal-content"); - for (let member of group) { +function renderPotentialMods (gameState, group, transferModHandlers, socket) { + const modalContent = document.getElementById('transfer-mod-modal-content'); + for (const member of group) { if ((member.out || member.userType === globals.USER_TYPES.SPECTATOR) && !(member.id === gameState.client.id)) { - let container = document.createElement("div"); + const container = document.createElement('div'); container.classList.add('potential-moderator'); container.dataset.pointer = member.id; container.innerText = member.name; transferModHandlers[member.id] = () => { - if (confirm("Transfer moderator powers to " + member.name + "?")) { + if (confirm('Transfer moderator powers to ' + member.name + '?')) { socket.emit(globals.COMMANDS.TRANSFER_MODERATOR, gameState.accessCode, member.id); } - } + }; container.addEventListener('click', transferModHandlers[member.id]); modalContent.appendChild(container); @@ -267,10 +264,10 @@ function renderPotentialMods(gameState, group, transferModHandlers, socket) { } } -function renderLobbyPerson(name, userType) { - let el = document.createElement("div"); - let personNameEl = document.createElement("div"); - let personTypeEl = document.createElement("div"); +function renderLobbyPerson (name, userType) { + const el = document.createElement('div'); + const personNameEl = document.createElement('div'); + const personTypeEl = document.createElement('div'); personNameEl.innerText = name; personTypeEl.innerText = userType + globals.USER_TYPE_ICONS[userType]; el.classList.add('lobby-player'); @@ -281,47 +278,47 @@ function renderLobbyPerson(name, userType) { return el; } -function sortPeopleByStatus(people) { +function sortPeopleByStatus (people) { people.sort((a, b) => { if (a.out !== b.out) { return a.out ? 1 : -1; } else { if (a.revealed !== b.revealed) { - return a.revealed? -1 : 1; + return a.revealed ? -1 : 1; } return a.name >= b.name ? 1 : -1; } }); } -function getGameSize(cards) { +function getGameSize (cards) { let quantity = 0; - for (let card of cards) { + for (const card of cards) { quantity += card.quantity; } return quantity; } -function removeExistingTitle() { - let existingTitle = document.querySelector('#game-title h1'); +function removeExistingTitle () { + const existingTitle = document.querySelector('#game-title h1'); if (existingTitle) { existingTitle.remove(); } } // TODO: refactor to reduce the cyclomatic complexity of this function -function renderGroupOfPlayers( +function renderGroupOfPlayers ( people, killPlayerHandlers, revealRoleHandlers, - accessCode=null, - alignment=null, + accessCode = null, + alignment = null, moderatorType, - socket=null + socket = null ) { - for (let player of people) { - let container = document.createElement("div"); + for (const player of people) { + const container = document.createElement('div'); container.classList.add('game-player'); if (moderatorType) { container.dataset.pointer = player.id; @@ -330,44 +327,44 @@ function renderGroupOfPlayers( container.innerHTML = templates.GAME_PLAYER; } container.querySelector('.game-player-name').innerText = player.name; - let roleElement = container.querySelector('.game-player-role') + const roleElement = container.querySelector('.game-player-role'); if (moderatorType) { roleElement.classList.add(alignment); if (moderatorType === globals.USER_TYPES.MODERATOR) { roleElement.innerText = player.gameRole; - document.getElementById("player-list-moderator-team-" + alignment).appendChild(container); + document.getElementById('player-list-moderator-team-' + alignment).appendChild(container); } else { if (player.revealed) { roleElement.innerText = player.gameRole; roleElement.classList.add(player.alignment); } else { - roleElement.innerText = "Unknown"; + roleElement.innerText = 'Unknown'; } - document.getElementById("game-player-list").appendChild(container); + document.getElementById('game-player-list').appendChild(container); } } else if (player.revealed) { roleElement.classList.add(player.alignment); roleElement.innerText = player.gameRole; - document.getElementById("game-player-list").appendChild(container); + document.getElementById('game-player-list').appendChild(container); } else { - roleElement.innerText = "Unknown"; - document.getElementById("game-player-list").appendChild(container); + roleElement.innerText = 'Unknown'; + document.getElementById('game-player-list').appendChild(container); } if (player.out) { container.classList.add('killed'); if (moderatorType) { container.querySelector('.kill-player-button')?.remove(); - insertPlaceholderButton(container, false, "killed"); + insertPlaceholderButton(container, false, 'killed'); } } else { if (moderatorType) { killPlayerHandlers[player.id] = () => { - if (confirm("KILL " + player.name + "?")) { + if (confirm('KILL ' + player.name + '?')) { socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id); } - } + }; container.querySelector('.kill-player-button').addEventListener('click', killPlayerHandlers[player.id]); } } @@ -375,52 +372,52 @@ function renderGroupOfPlayers( if (player.revealed) { if (moderatorType) { container.querySelector('.reveal-role-button')?.remove(); - insertPlaceholderButton(container, true, "revealed"); + insertPlaceholderButton(container, true, 'revealed'); } } else { if (moderatorType) { revealRoleHandlers[player.id] = () => { - if (confirm("REVEAL " + player.name + "?")) { + if (confirm('REVEAL ' + player.name + '?')) { socket.emit(globals.COMMANDS.REVEAL_PLAYER, accessCode, player.id); } - } + }; container.querySelector('.reveal-role-button').addEventListener('click', revealRoleHandlers[player.id]); } } } } -function renderPlayerRole(gameState) { - let name = document.querySelector('#role-name'); +function renderPlayerRole (gameState) { + const name = document.querySelector('#role-name'); name.innerText = gameState.client.gameRole; if (gameState.client.alignment === globals.ALIGNMENT.GOOD) { - document.getElementById("game-role").classList.add('game-role-good'); + document.getElementById('game-role').classList.add('game-role-good'); name.classList.add('good'); } else { - document.getElementById("game-role").classList.add('game-role-evil'); + document.getElementById('game-role').classList.add('game-role-evil'); name.classList.add('evil'); } - name.setAttribute("title", gameState.client.gameRole); + name.setAttribute('title', gameState.client.gameRole); if (gameState.client.out) { - document.querySelector('#role-image').classList.add("killed-card"); - document.getElementById("role-image").setAttribute( + document.querySelector('#role-image').classList.add('killed-card'); + document.getElementById('role-image').setAttribute( 'src', '../images/tombstone.png' ); } else { if (gameState.client.gameRole.toLowerCase() === 'villager') { - document.getElementById("role-image").setAttribute( + document.getElementById('role-image').setAttribute( 'src', '../images/roles/Villager' + Math.ceil(Math.random() * 2) + '.png' ); } else { if (gameState.client.customRole) { - document.getElementById("role-image").setAttribute( + document.getElementById('role-image').setAttribute( 'src', '../images/roles/custom-role.svg' ); } else { - document.getElementById("role-image").setAttribute( + document.getElementById('role-image').setAttribute( 'src', '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' ); @@ -430,24 +427,24 @@ function renderPlayerRole(gameState) { document.querySelector('#role-description').innerText = gameState.client.gameRoleDescription; - document.getElementById("game-role-back").addEventListener('click', () => { - document.getElementById("game-role").style.display = 'flex'; - document.getElementById("game-role-back").style.display = 'none'; + document.getElementById('game-role-back').addEventListener('click', () => { + document.getElementById('game-role').style.display = 'flex'; + document.getElementById('game-role-back').style.display = 'none'; }); - document.getElementById("game-role").addEventListener('click', () => { - document.getElementById("game-role-back").style.display = 'flex'; - document.getElementById("game-role").style.display = 'none'; + document.getElementById('game-role').addEventListener('click', () => { + document.getElementById('game-role-back').style.display = 'flex'; + document.getElementById('game-role').style.display = 'none'; }); } -function insertPlaceholderButton(container, append, type) { - let button = document.createElement("div"); +function insertPlaceholderButton (container, append, type) { + const button = document.createElement('div'); button.classList.add('placeholder-button'); - if (type === "killed") { + if (type === 'killed') { button.innerText = 'Killed'; } else { - button.innerText = "Revealed"; + button.innerText = 'Revealed'; } if (append) { container.querySelector('.player-action-buttons').appendChild(button); @@ -456,9 +453,9 @@ function insertPlaceholderButton(container, append, type) { } } -function removeExistingPlayerElements(killPlayerHandlers, revealRoleHandlers) { +function removeExistingPlayerElements (killPlayerHandlers, revealRoleHandlers) { document.querySelectorAll('.game-player').forEach((el) => { - let pointer = el.dataset.pointer; + const pointer = el.dataset.pointer; if (pointer && killPlayerHandlers[pointer]) { el.removeEventListener('click', killPlayerHandlers[pointer]); delete killPlayerHandlers[pointer]; @@ -471,19 +468,19 @@ function removeExistingPlayerElements(killPlayerHandlers, revealRoleHandlers) { }); } -function createEndGamePromptComponent(socket, stateBucket) { - if (document.querySelector("#end-game-prompt") === null) { - let div = document.createElement("div"); +function createEndGamePromptComponent (socket, stateBucket) { + if (document.querySelector('#end-game-prompt') === null) { + const div = document.createElement('div'); div.innerHTML = templates.END_GAME_PROMPT; - div.querySelector("#end-game-button").addEventListener('click', (e) => { + div.querySelector('#end-game-button').addEventListener('click', (e) => { e.preventDefault(); - if (confirm("End the game?")) { + if (confirm('End the game?')) { socket.emit( globals.COMMANDS.END_GAME, stateBucket.currentGameState.accessCode ); } }); - document.getElementById("game-content").appendChild(div); + document.getElementById('game-content').appendChild(div); } } diff --git a/client/src/modules/GameTimerManager.js b/client/src/modules/GameTimerManager.js index 4fe03f9..2e977a6 100644 --- a/client/src/modules/GameTimerManager.js +++ b/client/src/modules/GameTimerManager.js @@ -1,17 +1,17 @@ -import {globals} from "../config/globals.js"; +import { globals } from '../config/globals.js'; export class GameTimerManager { - constructor(stateBucket, socket) { + constructor (stateBucket, socket) { this.stateBucket = stateBucket; this.playListener = () => { socket.emit(globals.COMMANDS.RESUME_TIMER, this.stateBucket.currentGameState.accessCode); - } + }; this.pauseListener = () => { socket.emit(globals.COMMANDS.PAUSE_TIMER, this.stateBucket.currentGameState.accessCode); - } + }; } - resumeGameTimer(totalTime, tickRate, soundManager, timerWorker) { + resumeGameTimer (totalTime, tickRate, soundManager, timerWorker) { if (window.Worker) { if ( this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR @@ -19,8 +19,8 @@ export class GameTimerManager { ) { this.swapToPauseButton(); } - let instance = this; - let timer = document.getElementById('game-timer'); + const instance = this; + const timer = document.getElementById('game-timer'); timer.classList.remove('paused'); timer.classList.remove('paused-low'); timer.classList.remove('low'); @@ -46,7 +46,7 @@ export class GameTimerManager { } } - pauseGameTimer(timerWorker, timeRemaining) { + pauseGameTimer (timerWorker, timeRemaining) { if (window.Worker) { if ( this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR @@ -56,7 +56,7 @@ export class GameTimerManager { } timerWorker.postMessage('stop'); - let timer = document.getElementById('game-timer'); + const timer = document.getElementById('game-timer'); if (timeRemaining < 60000) { timer.innerText = returnHumanReadableTime(timeRemaining, true); timer.classList.add('paused-low'); @@ -68,7 +68,7 @@ export class GameTimerManager { } } - displayPausedTime(time) { + displayPausedTime (time) { if ( this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR || this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR @@ -76,7 +76,7 @@ export class GameTimerManager { this.swapToPlayButton(); } - let timer = document.getElementById('game-timer'); + const timer = document.getElementById('game-timer'); if (time < 60000) { timer.innerText = returnHumanReadableTime(time, true); timer.classList.add('paused-low'); @@ -87,71 +87,71 @@ export class GameTimerManager { } } - displayExpiredTime() { - let currentBtn = document.querySelector('#play-pause img'); + displayExpiredTime () { + const currentBtn = document.querySelector('#play-pause img'); if (currentBtn) { currentBtn.removeEventListener('click', this.pauseListener); currentBtn.removeEventListener('click', this.playListener); currentBtn.remove(); } - let timer = document.getElementById('game-timer'); + const timer = document.getElementById('game-timer'); timer.innerText = returnHumanReadableTime(0, true); } - attachTimerSocketListeners(socket, timerWorker, gameStateRenderer) { - if(!socket.hasListeners(globals.COMMANDS.PAUSE_TIMER)) { + attachTimerSocketListeners (socket, timerWorker, gameStateRenderer) { + if (!socket.hasListeners(globals.COMMANDS.PAUSE_TIMER)) { socket.on(globals.COMMANDS.PAUSE_TIMER, (timeRemaining) => { - this.pauseGameTimer(timerWorker, timeRemaining) + this.pauseGameTimer(timerWorker, timeRemaining); }); } - if(!socket.hasListeners(globals.COMMANDS.RESUME_TIMER)) { + if (!socket.hasListeners(globals.COMMANDS.RESUME_TIMER)) { socket.on(globals.COMMANDS.RESUME_TIMER, (timeRemaining) => { this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker); }); } - if(!socket.hasListeners(globals.COMMANDS.GET_TIME_REMAINING)) { + if (!socket.hasListeners(globals.COMMANDS.GET_TIME_REMAINING)) { socket.on(globals.COMMANDS.GET_TIME_REMAINING, (timeRemaining, paused) => { if (paused) { this.displayPausedTime(timeRemaining); } else if (timeRemaining === 0) { this.displayExpiredTime(); } else { - this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker); + this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker); } }); } } - swapToPlayButton() { - let currentBtn = document.querySelector('#play-pause img'); + swapToPlayButton () { + const currentBtn = document.querySelector('#play-pause img'); if (currentBtn) { currentBtn.removeEventListener('click', this.pauseListener); currentBtn.remove(); } - let playBtn = document.createElement('img'); + const playBtn = document.createElement('img'); playBtn.setAttribute('src', '../images/play-button.svg'); playBtn.addEventListener('click', this.playListener); document.getElementById('play-pause').appendChild(playBtn); } - swapToPauseButton() { - let currentBtn = document.querySelector('#play-pause img'); + swapToPauseButton () { + const currentBtn = document.querySelector('#play-pause img'); if (currentBtn) { currentBtn.removeEventListener('click', this.playListener); currentBtn.remove(); } - let pauseBtn = document.createElement('img'); + const pauseBtn = document.createElement('img'); pauseBtn.setAttribute('src', '../images/pause-button.svg'); pauseBtn.addEventListener('click', this.pauseListener); document.getElementById('play-pause').appendChild(pauseBtn); } - processTimeRemaining(timeRemaining, paused, timerWorker) { + processTimeRemaining (timeRemaining, paused, timerWorker) { if (paused) { this.displayPausedTime(timeRemaining); } else if (timeRemaining === 0) { @@ -162,18 +162,17 @@ export class GameTimerManager { } } -function returnHumanReadableTime(milliseconds, tenthsOfSeconds=false) { - - let tenths = Math.floor((milliseconds / 100) % 10); +function returnHumanReadableTime (milliseconds, tenthsOfSeconds = false) { + const tenths = Math.floor((milliseconds / 100) % 10); let seconds = Math.floor((milliseconds / 1000) % 60); let minutes = Math.floor((milliseconds / (1000 * 60)) % 60); let hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); - hours = hours < 10 ? "0" + hours : hours; - minutes = minutes < 10 ? "0" + minutes : minutes; - seconds = seconds < 10 ? "0" + seconds : seconds; + hours = hours < 10 ? '0' + hours : hours; + minutes = minutes < 10 ? '0' + minutes : minutes; + seconds = seconds < 10 ? '0' + seconds : seconds; return tenthsOfSeconds - ? hours + ":" + minutes + ":" + seconds + '.' + tenths - : hours + ":" + minutes + ":" + seconds; + ? hours + ':' + minutes + ':' + seconds + '.' + tenths + : hours + ':' + minutes + ':' + seconds; } diff --git a/client/src/modules/ModalManager.js b/client/src/modules/ModalManager.js index e5e4d19..75a4457 100644 --- a/client/src/modules/ModalManager.js +++ b/client/src/modules/ModalManager.js @@ -1,9 +1,9 @@ export const ModalManager = { displayModal: displayModal, dispelModal: dispelModal -} +}; -function displayModal(modalId, backgroundId, closeButtonId) { +function displayModal (modalId, backgroundId, closeButtonId) { const modal = document.getElementById(modalId); const modalOverlay = document.getElementById(backgroundId); const closeBtn = document.getElementById(closeButtonId); @@ -11,19 +11,19 @@ function displayModal(modalId, backgroundId, closeButtonId) { if (modal && modalOverlay && closeBtn) { modal.style.display = 'flex'; modalOverlay.style.display = 'flex'; - modalOverlay.removeEventListener("click", closeModalHandler); - modalOverlay.addEventListener("click", closeModalHandler = function(e) { + modalOverlay.removeEventListener('click', closeModalHandler); + modalOverlay.addEventListener('click', closeModalHandler = function (e) { e.preventDefault(); dispelModal(modalId, backgroundId); }); - closeBtn.removeEventListener("click", closeModalHandler); - closeBtn.addEventListener("click", closeModalHandler); + closeBtn.removeEventListener('click', closeModalHandler); + closeBtn.addEventListener('click', closeModalHandler); } else { - throw new Error("One or more of the ids supplied to ModalManager.displayModal is invalid."); + throw new Error('One or more of the ids supplied to ModalManager.displayModal is invalid.'); } } -function dispelModal(modalId, backgroundId) { +function dispelModal (modalId, backgroundId) { const modal = document.getElementById(modalId); const modalOverlay = document.getElementById(backgroundId); if (modal && modalOverlay) { diff --git a/client/src/modules/Navbar.js b/client/src/modules/Navbar.js index c22411a..642b61a 100644 --- a/client/src/modules/Navbar.js +++ b/client/src/modules/Navbar.js @@ -1,23 +1,23 @@ -export const injectNavbar = (page=null) => { +export const injectNavbar = (page = null) => { if (document.getElementById('navbar') !== null) { document.getElementById('navbar').innerHTML = - "' - + '' - + '
    ' - + '
    ' - + '' - + '
    ' - + ''; + "' + + '' + + '' + + '
    ' + + '' + + '
    ' + + ''; } attachHamburgerListener(); }; @@ -35,7 +35,7 @@ function flipHamburger () { } } -function getNavbarLinks (page=null, device) { +function getNavbarLinks (page = null, device) { const linkClass = device === 'mobile' ? 'mobile-link' : 'desktop-link'; return 'Create' + 'How to Use' + 'Contact' + - 'Support the App' + 'Support the App'; } function attachHamburgerListener () { diff --git a/client/src/modules/StateBucket.js b/client/src/modules/StateBucket.js index 7571fc2..af46c84 100644 --- a/client/src/modules/StateBucket.js +++ b/client/src/modules/StateBucket.js @@ -5,4 +5,4 @@ export const stateBucket = { currentGameState: null, timerWorker: null -} +}; diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index 4fe03b8..6d23494 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -1,165 +1,165 @@ export const templates = { LOBBY: "
    " + - "
    " + + '
    ' + "" + "" + - "
    " + + '
    ' + "
    " + - "
    " + + '
    ' + "clock" + "
    " + - "
    " + - "
    " + + '
    ' + + '
    ' + "person" + "
    " + - "
    " + - "
    " + - "
    " + + '
    ' + + '
    ' + + '
    ' + "
    " + - "
    " + - "
    " + + '
    ' + + '
    ' + + '
    ' + "
    " + "" + "
    " + - "
    " + + '
    ' + "" + - "
    ", + '' + + '', START_GAME_PROMPT: "
    " + "" + - "
    ", + '', END_GAME_PROMPT: "
    " + "" + - "
    ", + '', PLAYER_GAME_VIEW: "
    " + - "
    " + + '
    ' + "" + "
    " + - "
    " + - "
    " + + '
    ' + + '
    ' + "
    " + - "
    " + + '
    ' + + '' + "" + + '' + "
    " + - "

    Click to show your role

    " + - "

    (click again to hide)

    " + - "
    " + + '

    Click to show your role

    ' + + '

    (click again to hide)

    ' + + '' + "
    " + "" + "
    " + - "
    ", + '', SPECTATOR_GAME_VIEW: "
    " + - "
    " + + '
    ' + "" + "
    " + - "
    " + - "
    " + + '
    ' + + '
    ' + "
    " + - "
    " + + '
    ' + + '' + "
    " + "" + "
    " + - "
    ", + '', MODERATOR_GAME_VIEW: "" + "" + + '' + + '' + "
    " + "
    " + - "
    " + + '
    ' + "" + "
    " + - "
    " + - "
    " + "
    " + - "
    " + + '
    ' + + "
    " + '
    ' + + '
    ' + "" + - "
    " + + '
    ' + "
    " + - "
    " + - "
    " + + '
    ' + + '' + + '
    ' + "" + "
    " + "
    " + "" + "
    " + - "
    " + + '
    ' + "
    " + "" + "
    " + - "
    " + - "
    " + - "", + '' + + '' + + '', TEMP_MOD_GAME_VIEW: "" + "' + "" + - "" + - "" + + '' + + '' + + '' + "
    " + "
    " + - "
    " + + '
    ' + "" + "
    " + - "
    " + - "
    " + "
    " + - "
    " + - "
    " + + '
    ' + + "
    " + '
    ' + + '
    ' + + '
    ' + "
    " + - "
    " + + '' + + '' + "" + + '' + "
    " + - "

    Click to show your role

    " + - "

    (click again to hide)

    " + - "
    " + + '

    Click to show your role

    ' + + '

    (click again to hide)

    ' + + '' + "
    " + "" + "
    " + - "
    " + - "", + '' + + '', MODERATOR_PLAYER: - "
    " + + '
    ' + "
    " + "
    " + - "
    " + + '
    ' + "
    " + "" + "" + - "
    ", + '', GAME_PLAYER: - "
    " + + '
    ' + "
    " + "
    " + - "
    ", + '
    ', INITIAL_GAME_DOM: "
    " + "
    " + @@ -167,25 +167,25 @@ export const templates = { "
    " + "
    " + "
    " + - "
    " + - "
    " + + '' + + '' + "
    ", // via https://loading.io/css/ SPINNER: - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    " + - "
    ", + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ', NAME_CHANGE_MODAL: "" + "' + "" + - "" + - "", + '' + + '' + + '', ROLE_INFO_MODAL: "" + "", + '' + + '', END_OF_GAME_VIEW: - "

    The moderator has ended the game. Roles are revealed.

    " + + '

    The moderator has ended the game. Roles are revealed.

    ' + "
    " + - "
    " + + '
    ' + "
    " + - "
    " + + '
    ' + + '" + - "
    " + + '' + + '
    ' + + '' + "
    " + "" + "
    " + - "
    ", + '', CREATE_GAME_DECK: "
    " + - "
    " + + '
    ' + "" + "
    " + - "
    " + - "
    " + + '
    ' + + '
    ' + "" + "
    " + - "
    " + - "
    ", + '
    ' + + '', CREATE_GAME_CUSTOM_ROLES: - '
    ' + + '
    ' + '
    ' + - '' + - '
    ' + - '' + + '' + + '
    ' + + '' + '
    ', CREATE_GAME_DECK_STATUS: '
    ' + @@ -263,4 +263,4 @@ export const templates = { 'edit' + 'remove' + '
    ' -} +}; diff --git a/client/src/modules/Timer.js b/client/src/modules/Timer.js index abc81cf..ff0c180 100644 --- a/client/src/modules/Timer.js +++ b/client/src/modules/Timer.js @@ -38,7 +38,7 @@ function stepFn (expected, interval, start, totalTime) { } const delta = now - expected; expected += interval; - let displayTime = (totalTime - (now - start)) < 60000 + const displayTime = (totalTime - (now - start)) < 60000 ? returnHumanReadableTime(totalTime - (now - start), true) : returnHumanReadableTime(totalTime - (now - start)); postMessage({ @@ -46,8 +46,8 @@ function stepFn (expected, interval, start, totalTime) { displayTime: displayTime }); Singleton.setNewTimeoutReference(setTimeout(() => { - stepFn(expected, interval, start, totalTime); - }, Math.max(0, interval - delta) + stepFn(expected, interval, start, totalTime); + }, Math.max(0, interval - delta) )); // take into account drift - also retain a reference to this clock tick so it can be cleared later } @@ -73,7 +73,7 @@ class Timer { } } - stopTimer() { + stopTimer () { if (this.timeoutId) { clearTimeout(this.timeoutId); } @@ -105,18 +105,17 @@ class Singleton { } } -function returnHumanReadableTime(milliseconds, tenthsOfSeconds=false) { - - let tenths = Math.floor((milliseconds / 100) % 10); +function returnHumanReadableTime (milliseconds, tenthsOfSeconds = false) { + const tenths = Math.floor((milliseconds / 100) % 10); let seconds = Math.floor((milliseconds / 1000) % 60); let minutes = Math.floor((milliseconds / (1000 * 60)) % 60); let hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); - hours = hours < 10 ? "0" + hours : hours; - minutes = minutes < 10 ? "0" + minutes : minutes; - seconds = seconds < 10 ? "0" + seconds : seconds; + hours = hours < 10 ? '0' + hours : hours; + minutes = minutes < 10 ? '0' + minutes : minutes; + seconds = seconds < 10 ? '0' + seconds : seconds; return tenthsOfSeconds - ? hours + ":" + minutes + ":" + seconds + '.' + tenths - : hours + ":" + minutes + ":" + seconds; + ? hours + ':' + minutes + ':' + seconds + '.' + tenths + : hours + ':' + minutes + ':' + seconds; } diff --git a/client/src/modules/Toast.js b/client/src/modules/Toast.js index 8c83b17..0302da8 100644 --- a/client/src/modules/Toast.js +++ b/client/src/modules/Toast.js @@ -1,6 +1,6 @@ -import {globals} from "../config/globals.js"; +import { globals } from '../config/globals.js'; -export const toast = (message, type, positionAtTop = true, dispelAutomatically=true, duration=null) => { +export const toast = (message, type, positionAtTop = true, dispelAutomatically = true, duration = null) => { if (message && type) { buildAndInsertMessageElement(message, type, positionAtTop, dispelAutomatically, duration); } @@ -21,21 +21,21 @@ function buildAndInsertMessageElement (message, type, positionAtTop, dispelAutom break; case 'success': backgroundColor = '#bef5cb'; - border = '3px solid #8ac78a;' + border = '3px solid #8ac78a;'; break; } - let durationInSeconds = duration ? duration + 's' : globals.TOAST_DURATION_DEFAULT + 's'; + const durationInSeconds = duration ? duration + 's' : globals.TOAST_DURATION_DEFAULT + 's'; let animation = ''; if (dispelAutomatically) { animation = 'animation:fade-in-slide-down-then-exit ' + durationInSeconds + ' ease normal forwards'; } else { animation = 'animation:fade-in-slide-down ' + durationInSeconds + ' ease normal forwards'; } - const messageEl = document.createElement("div"); - messageEl.setAttribute("id", "current-info-message"); - messageEl.setAttribute("style", 'background-color:' + backgroundColor + ';' + 'border:' + border + ';' + position + animation); - messageEl.setAttribute("class", 'info-message'); + const messageEl = document.createElement('div'); + messageEl.setAttribute('id', 'current-info-message'); + messageEl.setAttribute('style', 'background-color:' + backgroundColor + ';' + 'border:' + border + ';' + position + animation); + messageEl.setAttribute('class', 'info-message'); messageEl.innerText = message; document.body.prepend(messageEl); } diff --git a/client/src/modules/XHRUtility.js b/client/src/modules/XHRUtility.js index 758d5e5..0607047 100644 --- a/client/src/modules/XHRUtility.js +++ b/client/src/modules/XHRUtility.js @@ -34,7 +34,6 @@ export const XHRUtility = }; body ? req.send(body) : req.send(); }); - }, - + } }; diff --git a/client/src/scripts/create.js b/client/src/scripts/create.js index 5ddf3db..241bb49 100644 --- a/client/src/scripts/create.js +++ b/client/src/scripts/create.js @@ -1,13 +1,13 @@ -import { DeckStateManager } from "../modules/DeckStateManager.js"; -import { GameCreationStepManager } from "../modules/GameCreationStepManager.js"; -import { injectNavbar } from "../modules/Navbar.js"; +import { DeckStateManager } from '../modules/DeckStateManager.js'; +import { GameCreationStepManager } from '../modules/GameCreationStepManager.js'; +import { injectNavbar } from '../modules/Navbar.js'; const create = () => { injectNavbar(); - let deckManager = new DeckStateManager(); - let gameCreationStepManager = new GameCreationStepManager(deckManager); - gameCreationStepManager.renderStep("creation-step-container", 1); -} + const deckManager = new DeckStateManager(); + const gameCreationStepManager = new GameCreationStepManager(deckManager); + gameCreationStepManager.renderStep('creation-step-container', 1); +}; if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = create; diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index b27a201..0b3edaf 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -1,13 +1,13 @@ -import { UserUtility } from "../modules/UserUtility.js"; -import { globals } from "../config/globals.js"; -import {templates} from "../modules/Templates.js"; -import {GameStateRenderer} from "../modules/GameStateRenderer.js"; -import {cancelCurrentToast, toast} from "../modules/Toast.js"; -import {GameTimerManager} from "../modules/GameTimerManager.js"; -import {ModalManager} from "../modules/ModalManager.js"; -import {stateBucket} from "../modules/StateBucket.js"; +import { UserUtility } from '../modules/UserUtility.js'; +import { globals } from '../config/globals.js'; +import { templates } from '../modules/Templates.js'; +import { GameStateRenderer } from '../modules/GameStateRenderer.js'; +import { toast } from '../modules/Toast.js'; +import { GameTimerManager } from '../modules/GameTimerManager.js'; +import { ModalManager } from '../modules/ModalManager.js'; +import { stateBucket } from '../modules/StateBucket.js'; import { io } from 'socket.io-client'; -import { injectNavbar } from "../modules/Navbar.js"; +import { injectNavbar } from '../modules/Navbar.js'; const game = () => { injectNavbar(); @@ -25,10 +25,10 @@ const game = () => { timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); prepareGamePage(returnedEnvironment, socket, timerWorker); }); - }) + }); }; -function prepareGamePage(environment, socket, timerWorker) { +function prepareGamePage (environment, socket, timerWorker) { let userId = UserUtility.validateAnonUserSignature(environment); const splitUrl = window.location.href.split('/game/'); const accessCode = splitUrl[1]; @@ -41,11 +41,11 @@ function prepareGamePage(environment, socket, timerWorker) { if (gameState === null) { window.location = '/not-found?reason=' + encodeURIComponent('game-not-found'); } else { - document.getElementById("game-content").innerHTML = templates.INITIAL_GAME_DOM; + document.getElementById('game-content').innerHTML = templates.INITIAL_GAME_DOM; toast('You are connected.', 'success', true, true, 2); userId = gameState.client.cookie; UserUtility.setAnonymousUserId(userId, environment); - let gameStateRenderer = new GameStateRenderer(stateBucket, socket); + const gameStateRenderer = new GameStateRenderer(stateBucket, socket); let gameTimerManager; if (stateBucket.currentGameState.timerParams) { gameTimerManager = new GameTimerManager(stateBucket, socket); @@ -53,30 +53,30 @@ function prepareGamePage(environment, socket, timerWorker) { initializeGame(stateBucket, socket, timerWorker, userId, gameStateRenderer, gameTimerManager); if (!gameState.client.hasEnteredName) { - document.getElementById("prompt").innerHTML = templates.NAME_CHANGE_MODAL; - document.getElementById("change-name-form").onsubmit = (e) => { + document.getElementById('prompt').innerHTML = templates.NAME_CHANGE_MODAL; + document.getElementById('change-name-form').onsubmit = (e) => { e.preventDefault(); - let name = document.getElementById("player-new-name").value; + const name = document.getElementById('player-new-name').value; if (validateName(name)) { socket.emit(globals.COMMANDS.CHANGE_NAME, gameState.accessCode, { name: name, personId: gameState.client.id }, (result) => { switch (result) { - case "taken": + case 'taken': toast('This name is already taken.', 'error', true, true, 8); break; - case "changed": - ModalManager.dispelModal("change-name-modal", "change-name-modal-background") + case 'changed': + ModalManager.dispelModal('change-name-modal', 'change-name-modal-background'); toast('Name set.', 'success', true, true, 5); propagateNameChange(stateBucket.currentGameState, name, stateBucket.currentGameState.client.id); processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker); } - }) + }); } else { - toast("Name must be between 1 and 30 characters.", 'error', true, true, 8); + toast('Name must be between 1 and 30 characters.', 'error', true, true, 8); } - } + }; } } }); @@ -85,20 +85,20 @@ function prepareGamePage(environment, socket, timerWorker) { } } -function initializeGame(stateBucket, socket, timerWorker, userId, gameStateRenderer, gameTimerManager) { +function initializeGame (stateBucket, socket, timerWorker, userId, gameStateRenderer, gameTimerManager) { setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager); processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker); } -function processGameState (currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker, refreshPrompt=true) { +function processGameState (currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker, refreshPrompt = true) { displayClientInfo(currentGameState.client.name, currentGameState.client.userType); if (refreshPrompt) { removeStartGameFunctionalityIfPresent(gameStateRenderer); - document.querySelector("#end-game-prompt")?.remove(); + document.querySelector('#end-game-prompt')?.remove(); } switch (currentGameState.status) { case globals.STATUS.LOBBY: - document.getElementById("game-state-container").innerHTML = templates.LOBBY; + document.getElementById('game-state-container').innerHTML = templates.LOBBY; gameStateRenderer.renderLobbyHeader(); gameStateRenderer.renderLobbyPlayers(); if ( @@ -109,31 +109,31 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer, ) && refreshPrompt ) { - displayStartGamePromptForModerators(currentGameState, gameStateRenderer); + displayStartGamePromptForModerators(currentGameState, gameStateRenderer); } break; case globals.STATUS.IN_PROGRESS: gameStateRenderer.renderGameHeader(); switch (currentGameState.client.userType) { case globals.USER_TYPES.PLAYER: - document.getElementById("game-state-container").innerHTML = templates.PLAYER_GAME_VIEW; + document.getElementById('game-state-container').innerHTML = templates.PLAYER_GAME_VIEW; gameStateRenderer.renderPlayerView(); break; case globals.USER_TYPES.KILLED_PLAYER: - document.getElementById("game-state-container").innerHTML = templates.PLAYER_GAME_VIEW; + document.getElementById('game-state-container').innerHTML = templates.PLAYER_GAME_VIEW; gameStateRenderer.renderPlayerView(true); break; case globals.USER_TYPES.MODERATOR: - document.getElementById("game-state-container").innerHTML = templates.MODERATOR_GAME_VIEW; + document.getElementById('game-state-container').innerHTML = templates.MODERATOR_GAME_VIEW; gameStateRenderer.renderModeratorView(); break; case globals.USER_TYPES.TEMPORARY_MODERATOR: - document.getElementById("game-state-container").innerHTML = templates.TEMP_MOD_GAME_VIEW; + document.getElementById('game-state-container').innerHTML = templates.TEMP_MOD_GAME_VIEW; gameStateRenderer.renderTempModView(); break; case globals.USER_TYPES.SPECTATOR: - document.getElementById("game-state-container").innerHTML = templates.SPECTATOR_GAME_VIEW; + document.getElementById('game-state-container').innerHTML = templates.SPECTATOR_GAME_VIEW; gameStateRenderer.renderSpectatorView(); break; default: @@ -148,12 +148,13 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer, document.querySelector('label[for="game-timer"]')?.remove(); } break; - case globals.STATUS.ENDED: - let container = document.getElementById("game-state-container") + case globals.STATUS.ENDED: { + const container = document.getElementById('game-state-container'); container.innerHTML = templates.END_OF_GAME_VIEW; container.classList.add('vertical-flex'); gameStateRenderer.renderEndOfGame(); break; + } default: break; } @@ -161,16 +162,16 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer, activateRoleInfoButton(stateBucket.currentGameState.deck); } -function displayClientInfo(name, userType) { - document.getElementById("client-name").innerText = name; - document.getElementById("client-user-type").innerText = userType; - document.getElementById("client-user-type").innerText += globals.USER_TYPE_ICONS[userType]; +function displayClientInfo (name, userType) { + document.getElementById('client-name').innerText = name; + document.getElementById('client-user-type').innerText = userType; + document.getElementById('client-user-type').innerText += globals.USER_TYPE_ICONS[userType]; } -function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager) { +function setClientSocketHandlers (stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager) { if (!socket.hasListeners(globals.EVENTS.PLAYER_JOINED)) { socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => { - toast(player.name + " joined!", "success", false, true, 3); + toast(player.name + ' joined!', 'success', false, true, 3); stateBucket.currentGameState.people.push(player); stateBucket.currentGameState.isFull = gameIsFull; gameStateRenderer.renderLobbyPlayers(); @@ -189,8 +190,8 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo if (!socket.hasListeners(globals.EVENTS.PLAYER_LEFT)) { socket.on(globals.EVENTS.PLAYER_LEFT, (player) => { removeStartGameFunctionalityIfPresent(gameStateRenderer); - toast(player.name + " has left!", "error", false, true, 3); - let index = stateBucket.currentGameState.people.findIndex(person => person.id === player.id); + toast(player.name + ' has left!', 'error', false, true, 3); + const index = stateBucket.currentGameState.people.findIndex(person => person.id === player.id); if (index >= 0) { stateBucket.currentGameState.people.splice( index, @@ -248,17 +249,17 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo if (!socket.hasListeners(globals.EVENTS.KILL_PLAYER)) { socket.on(globals.EVENTS.KILL_PLAYER, (id) => { - let killedPerson = stateBucket.currentGameState.people.find((person) => person.id === id); + const killedPerson = stateBucket.currentGameState.people.find((person) => person.id === id); if (killedPerson) { killedPerson.out = true; if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) { toast(killedPerson.name + ' killed.', 'success', true, true, 6); - gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED) + gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED); } else { if (killedPerson.id === stateBucket.currentGameState.client.id) { - let clientUserType = document.getElementById("client-user-type"); + const clientUserType = document.getElementById('client-user-type'); if (clientUserType) { - clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80' + clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80'; } gameStateRenderer.updatePlayerCardToKilledState(); toast('You have been killed!', 'warning', true, true, 6); @@ -277,14 +278,14 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo if (!socket.hasListeners(globals.EVENTS.REVEAL_PLAYER)) { socket.on(globals.EVENTS.REVEAL_PLAYER, (revealData) => { - let revealedPerson = stateBucket.currentGameState.people.find((person) => person.id === revealData.id); + const revealedPerson = stateBucket.currentGameState.people.find((person) => person.id === revealData.id); if (revealedPerson) { revealedPerson.revealed = true; revealedPerson.gameRole = revealData.gameRole; revealedPerson.alignment = revealData.alignment; if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) { toast(revealedPerson.name + ' revealed.', 'success', true, true, 6); - gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED) + gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED); } else { if (revealedPerson.id === stateBucket.currentGameState.client.id) { toast('Your role has been revealed!', 'warning', true, true, 6); @@ -333,38 +334,27 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo } } -function displayStartGamePromptForModerators(gameState, gameStateRenderer) { - let div = document.createElement("div"); +function displayStartGamePromptForModerators (gameState, gameStateRenderer) { + const div = document.createElement('div'); div.innerHTML = templates.START_GAME_PROMPT; div.querySelector('#start-game-button').addEventListener('click', gameStateRenderer.startGameHandler); document.body.appendChild(div); } -function runGameTimer (hours, minutes, tickRate, soundManager, timerWorker) { - if (window.Worker) { - timerWorker.onmessage = function (e) { - if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds > 0) { - document.getElementById('game-timer').innerText = e.data.displayTime; - } - }; - timerWorker.postMessage({ hours: hours, minutes: minutes, tickInterval: tickRate }); - } -} - -function validateName(name) { +function validateName (name) { return typeof name === 'string' && name.length > 0 && name.length <= 30; } -function removeStartGameFunctionalityIfPresent(gameStateRenderer) { - document.querySelector("#start-game-prompt")?.removeEventListener('click', gameStateRenderer.startGameHandler); - document.querySelector("#start-game-prompt")?.remove(); +function removeStartGameFunctionalityIfPresent (gameStateRenderer) { + document.querySelector('#start-game-prompt')?.removeEventListener('click', gameStateRenderer.startGameHandler); + document.querySelector('#start-game-prompt')?.remove(); } -function propagateNameChange(gameState, name, personId) { +function propagateNameChange (gameState, name, personId) { if (gameState.client.id === personId) { gameState.client.name = name; } - let matchingPerson = gameState.people.find((person) => person.id === personId); + const matchingPerson = gameState.people.find((person) => person.id === personId); if (matchingPerson) { matchingPerson.name = name; } @@ -373,13 +363,13 @@ function propagateNameChange(gameState, name, personId) { gameState.moderator.name = name; } - let matchingSpectator = gameState.spectators?.find((spectator) => spectator.id === personId); + const matchingSpectator = gameState.spectators?.find((spectator) => spectator.id === personId); if (matchingSpectator) { matchingSpectator.name = name; } } -function updateDOMWithNameChange(gameState, gameStateRenderer) { +function updateDOMWithNameChange (gameState, gameStateRenderer) { if (gameState.status === globals.STATUS.IN_PROGRESS) { switch (gameState.client.userType) { case globals.USER_TYPES.PLAYER: @@ -401,23 +391,23 @@ function updateDOMWithNameChange(gameState, gameStateRenderer) { } } -function activateRoleInfoButton(deck) { +function activateRoleInfoButton (deck) { deck.sort((a, b) => { return a.team === globals.ALIGNMENT.GOOD ? 1 : -1; - }) - document.getElementById("role-info-button").addEventListener("click", (e) => { + }); + document.getElementById('role-info-button').addEventListener('click', (e) => { e.preventDefault(); - document.getElementById("prompt").innerHTML = templates.ROLE_INFO_MODAL; - let modalContent = document.getElementById('game-role-info-container'); - for (let card of deck) { - let roleDiv = document.createElement("div"); - let roleNameDiv = document.createElement("div"); + document.getElementById('prompt').innerHTML = templates.ROLE_INFO_MODAL; + const modalContent = document.getElementById('game-role-info-container'); + for (const card of deck) { + const roleDiv = document.createElement('div'); + const roleNameDiv = document.createElement('div'); roleNameDiv.classList.add('role-info-name'); - let roleName = document.createElement("h5"); - let roleQuantity = document.createElement("h5"); - let roleDescription = document.createElement("p"); + const roleName = document.createElement('h5'); + const roleQuantity = document.createElement('h5'); + const roleDescription = document.createElement('p'); roleDescription.innerText = card.description; roleName.innerText = card.role; @@ -429,8 +419,8 @@ function activateRoleInfoButton(deck) { roleName.classList.add(globals.ALIGNMENT.EVIL); } - roleNameDiv .appendChild(roleQuantity); - roleNameDiv .appendChild(roleName); + roleNameDiv.appendChild(roleQuantity); + roleNameDiv.appendChild(roleName); roleDiv.appendChild(roleNameDiv); roleDiv.appendChild(roleDescription); diff --git a/client/src/scripts/home.js b/client/src/scripts/home.js index b7372a0..15cf119 100644 --- a/client/src/scripts/home.js +++ b/client/src/scripts/home.js @@ -1,44 +1,44 @@ -import { XHRUtility } from "../modules/XHRUtility.js"; -import { toast } from "../modules/Toast.js"; -import { injectNavbar } from "../modules/Navbar.js"; +import { XHRUtility } from '../modules/XHRUtility.js'; +import { toast } from '../modules/Toast.js'; +import { injectNavbar } from '../modules/Navbar.js'; const home = () => { injectNavbar(); - document.getElementById("join-form").onsubmit = (e) => { + document.getElementById('join-form').onsubmit = (e) => { e.preventDefault(); - let userCode = document.getElementById("room-code").value; + const userCode = document.getElementById('room-code').value; if (roomCodeIsValid(userCode)) { attemptToJoinGame(userCode); } else { toast('Invalid code. Codes are 6 numbers or letters.', 'error', true, true); } - } + }; }; -function roomCodeIsValid(code) { - return typeof code === "string" && /^[a-z0-9]{6}$/.test(code.toLowerCase()); +function roomCodeIsValid (code) { + return typeof code === 'string' && /^[a-z0-9]{6}$/.test(code.toLowerCase()); } -function attemptToJoinGame(code) { +function attemptToJoinGame (code) { XHRUtility.xhr( '/api/games/availability/' + code.toLowerCase().trim(), 'GET', null, null ) - .then((res) => { - if (res.status === 200) { - window.location = '/game/' + res.content; - } - }).catch((res) => { - if (res.status === 404) { - toast("Game not found", "error", true); - } else if (res.status === 400) { - toast(res.content, "error", true); - } else { - toast("An unknown error occurred. Please try again later.", "error", true); - } - }); + .then((res) => { + if (res.status === 200) { + window.location = '/game/' + res.content; + } + }).catch((res) => { + if (res.status === 404) { + toast('Game not found', 'error', true); + } else if (res.status === 400) { + toast(res.content, 'error', true); + } else { + toast('An unknown error occurred. Please try again later.', 'error', true); + } + }); } if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { @@ -46,4 +46,3 @@ if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { } else { home(); } - diff --git a/client/src/scripts/howToUse.js b/client/src/scripts/howToUse.js index ee25480..e7d2679 100644 --- a/client/src/scripts/howToUse.js +++ b/client/src/scripts/howToUse.js @@ -1,4 +1,4 @@ -import { injectNavbar } from "../modules/Navbar.js"; +import { injectNavbar } from '../modules/Navbar.js'; const howToUse = () => { injectNavbar(); }; diff --git a/client/src/scripts/notFound.js b/client/src/scripts/notFound.js index e5a5ee2..68e7047 100644 --- a/client/src/scripts/notFound.js +++ b/client/src/scripts/notFound.js @@ -1,4 +1,4 @@ -import { injectNavbar } from "../modules/Navbar.js"; +import { injectNavbar } from '../modules/Navbar.js'; const notFound = () => { injectNavbar(); }; diff --git a/package-lock.json b/package-lock.json index d41b01d..6c5fd3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,19 +18,20 @@ "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==" }, "@babel/core": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.5.tgz", - "integrity": "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", + "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", + "dev": true, "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.5", - "@babel/helper-compilation-targets": "^7.16.3", - "@babel/helper-module-transforms": "^7.16.5", - "@babel/helpers": "^7.16.5", - "@babel/parser": "^7.16.5", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.5", - "@babel/types": "^7.16.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.7", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -39,10 +40,192 @@ "source-map": "^0.5.0" }, "dependencies": { + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-transforms": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-simple-access": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true + }, + "@babel/highlight": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", + "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", + "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", + "dev": true + }, + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/traverse": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", + "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.16.8", + "@babel/types": "^7.16.8", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", + "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, "requires": { "ms": "2.1.2" } @@ -50,10 +233,22 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true } } }, + "@babel/eslint-parser": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.5.tgz", + "integrity": "sha512-mUqYa46lgWqHKQ33Q6LNCGp/wPR3eqOYTUixHFsfrSQqRxH0+WOzca75iEjFr5RDGH1dDz622LaHhLOzOuQRUA==", + "dev": true, + "requires": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + } + }, "@babel/generator": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", @@ -299,13 +494,160 @@ } }, "@babel/helpers": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.5.tgz", - "integrity": "sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", + "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "dev": true, "requires": { - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.5", - "@babel/types": "^7.16.0" + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", + "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", + "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", + "dev": true + }, + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/traverse": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", + "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.16.8", + "@babel/types": "^7.16.8", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", + "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "@babel/highlight": { @@ -1042,6 +1384,83 @@ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==" }, + "@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.2.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@socket.io/component-emitter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", @@ -1090,6 +1509,12 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/node": { "version": "17.0.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.3.tgz", @@ -1273,6 +1698,12 @@ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1289,6 +1720,18 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1297,11 +1740,61 @@ "color-convert": "^1.9.0" } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + } + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + } + } + }, "babel-loader": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", @@ -1430,6 +1923,12 @@ "get-intrinsic": "^1.0.2" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "caniuse-lite": { "version": "1.0.30001292", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", @@ -1522,6 +2021,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -1579,6 +2079,12 @@ "ms": "2.0.0" } }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1597,6 +2103,15 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1697,16 +2212,64 @@ "tapable": "^2.2.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "envinfo": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==" }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, "es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1722,6 +2285,374 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "eslint": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.6.0.tgz", + "integrity": "sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.3.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint-scope": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "eslint-config-standard": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", + "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==" + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz", + "integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", + "integrity": "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==", + "dev": true + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1731,6 +2662,63 @@ "estraverse": "^4.1.1" } }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", + "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + }, + "dependencies": { + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true + } + } + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -1846,11 +2834,26 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==" }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -1884,6 +2887,22 @@ "path-exists": "^4.0.0" } }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1904,10 +2923,17 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true }, "get-intrinsic": { "version": "1.1.1", @@ -1924,6 +2950,16 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -1937,6 +2973,15 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, "glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", @@ -1960,6 +3005,12 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", @@ -1975,6 +3026,15 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "http-errors": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", @@ -2007,6 +3067,30 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "import-local": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", @@ -2016,6 +3100,12 @@ "resolve-cwd": "^3.0.0" } }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2030,6 +3120,17 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -2040,6 +3141,31 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, "is-core-module": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", @@ -2048,11 +3174,50 @@ "has": "^1.0.3" } }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-docker": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -2061,11 +3226,54 @@ "isobject": "^3.0.1" } }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -2128,6 +3336,15 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -2143,10 +3360,17 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, "requires": { "minimist": "^1.2.5" } @@ -2156,6 +3380,16 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, "loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -2194,6 +3428,21 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -2263,6 +3512,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -2291,6 +3546,12 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -2307,6 +3568,17 @@ "object-keys": "^1.1.1" } }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2340,6 +3612,20 @@ "is-wsl": "^2.1.1" } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "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" + } + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -2361,6 +3647,15 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parseqs": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", @@ -2414,6 +3709,18 @@ "find-up": "^4.0.0" } }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2491,6 +3798,12 @@ "@babel/runtime": "^7.8.4" } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, "regexpu-core": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", @@ -2546,6 +3859,15 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2643,6 +3965,17 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", @@ -2769,11 +4102,52 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2833,6 +4207,12 @@ } } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -2843,6 +4223,44 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "tsconfig-paths": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", + "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2852,6 +4270,18 @@ "mime-types": "~2.1.24" } }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -2894,6 +4324,12 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3009,11 +4445,30 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3029,6 +4484,12 @@ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", diff --git a/package.json b/package.json index 463e854..1235b00 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,14 @@ "author": "", "license": "ISC", "dependencies": { - "@babel/core": "^7.16.5", "@babel/plugin-transform-object-assign": "^7.16.5", "@babel/preset-env": "^7.16.5", "acorn": "^8.6.0", + "babel-eslint": "^10.1.0", "babel-loader": "^8.2.3", "body-parser": "^1.19.1", "cors": "^2.8.5", + "eslint-config-standard": "^16.0.3", "express": "^4.17.1", "express-force-https": "^1.0.0", "express-rate-limit": "^6.0.1", @@ -39,7 +40,14 @@ "webpack-cli": "^4.9.1", "webpack-remove-debug": "^0.1.0" }, - "devDependencies": {}, + "devDependencies": { + "@babel/core": "^7.16.7", + "@babel/eslint-parser": "^7.16.5", + "eslint": "^8.6.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.0.0" + }, "nodemonConfig": { "ignore": [ "client/*", diff --git a/server/api/GamesAPI.js b/server/api/GamesAPI.js index 82aebc9..206f745 100644 --- a/server/api/GamesAPI.js +++ b/server/api/GamesAPI.js @@ -3,7 +3,7 @@ const router = express.Router(); const debugMode = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())).includes('debug'); const logger = require('../modules/Logger')(debugMode); const GameManager = require('../modules/GameManager.js'); -const rateLimit = require('express-rate-limit').default +const rateLimit = require('express-rate-limit').default; const globals = require('../config/globals'); const gameManager = new GameManager().getInstance(); @@ -12,8 +12,8 @@ const apiLimiter = rateLimit({ windowMs: 600000, max: 5, standardHeaders: true, - legacyHeaders: false, -}) + legacyHeaders: false +}); if (process.env.NODE_ENV.trim() === 'production') { // in prod, limit clients to creating 5 games per 10 minutes. router.use('/create', apiLimiter); @@ -42,7 +42,7 @@ router.get('/availability/:code', function (req, res) { res.status(404).send(); } else if (result instanceof Error) { res.status(400).send(result.message); - } else if (typeof result === "string") { + } else if (typeof result === 'string') { logger.debug(result); res.status(200).send(result); } else { diff --git a/server/config/globals.js b/server/config/globals.js index 4ee35bc..59c26d0 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -18,48 +18,48 @@ const globals = { FETCH_IN_PROGRESS_STATE: 'fetchInitialInProgressState' }, MESSAGES: { - ENTER_NAME: "Client must enter name." + ENTER_NAME: 'Client must enter name.' }, STATUS: { - LOBBY: "lobby", - IN_PROGRESS: "in progress", - ENDED: "ended" + LOBBY: 'lobby', + IN_PROGRESS: 'in progress', + ENDED: 'ended' }, USER_SIGNATURE_LENGTH: 25, USER_TYPES: { - MODERATOR: "moderator", - PLAYER: "player", - TEMPORARY_MODERATOR: "player / temp mod", - KILLED_PLAYER: "killed", - SPECTATOR: "spectator" + MODERATOR: 'moderator', + PLAYER: 'player', + TEMPORARY_MODERATOR: 'player / temp mod', + KILLED_PLAYER: 'killed', + SPECTATOR: 'spectator' }, ERROR_MESSAGE: { - GAME_IS_FULL: "This game is full", - BAD_CREATE_REQUEST: "Game has invalid options." + GAME_IS_FULL: 'This game is full', + BAD_CREATE_REQUEST: 'Game has invalid options.' }, EVENTS: { - PLAYER_JOINED: "playerJoined", - PLAYER_LEFT: "playerLeft", - SYNC_GAME_STATE: "syncGameState" + PLAYER_JOINED: 'playerJoined', + PLAYER_LEFT: 'playerLeft', + SYNC_GAME_STATE: 'syncGameState' }, ENVIRONMENT: { - LOCAL: "local", - PRODUCTION: "production" + LOCAL: 'local', + PRODUCTION: 'production' }, LOG_LEVEL: { - INFO: "info", - DEBUG: "debug", - ERROR: "error", - WARN: "warn", - TRACE: "trace" + INFO: 'info', + DEBUG: 'debug', + ERROR: 'error', + WARN: 'warn', + TRACE: 'trace' }, GAME_PROCESS_COMMANDS: { - END_TIMER: "endTimer", - START_GAME: "startGame", - START_TIMER: "startTimer", - PAUSE_TIMER: "pauseTimer", - RESUME_TIMER: "resumeTimer", - GET_TIME_REMAINING: "getTimeRemaining" + END_TIMER: 'endTimer', + START_GAME: 'startGame', + START_TIMER: 'startTimer', + PAUSE_TIMER: 'pauseTimer', + RESUME_TIMER: 'resumeTimer', + GET_TIME_REMAINING: 'getTimeRemaining' } }; diff --git a/server/main.js b/server/main.js index cb742a9..a2d3748 100644 --- a/server/main.js +++ b/server/main.js @@ -2,7 +2,7 @@ const express = require('express'); const path = require('path'); const app = express(); const bodyParser = require('body-parser'); -const GameManager = require('./modules/GameManager.js'); +const GameManager = require('./modules/GameManager.js'); const globals = require('./config/globals'); const ServerBootstrapper = require('./modules/ServerBootstrapper'); @@ -16,15 +16,15 @@ const args = ServerBootstrapper.processCLIArgs(); const logger = require('./modules/Logger')(args.logLevel); logger.info('LOG LEVEL IS: ' + args.logLevel); -const main = ServerBootstrapper.createServerWithCorrectHTTPProtocol(app, args.useHttps, args.port, logger) +const main = ServerBootstrapper.createServerWithCorrectHTTPProtocol(app, args.useHttps, args.port, logger); app.set('port', args.port); const inGameSocketServer = ServerBootstrapper.createSocketServer(main, app, args.port); inGameSocketServer.on('connection', function (socket) { - socket.on("disconnecting", (reason) => { - logger.trace('client socket disconnecting because: ' + reason) + socket.on('disconnecting', (reason) => { + logger.trace('client socket disconnecting because: ' + reason); gameManager.removeClientFromLobbyIfApplicable(socket); }); gameManager.addGameSocketHandlers(inGameSocketServer, socket); @@ -70,5 +70,5 @@ app.use(function (req, res) { }); main.listen(args.port, function () { - logger.info(`Starting server on port ${args.port}` ); + logger.info(`Starting server on port ${args.port}`); }); diff --git a/server/model/Game.js b/server/model/Game.js index 0e4c6f1..7a35f89 100644 --- a/server/model/Game.js +++ b/server/model/Game.js @@ -1,5 +1,5 @@ class Game { - constructor(accessCode, status, people, deck, hasTimer, moderator, timerParams=null) { + constructor (accessCode, status, people, deck, hasTimer, moderator, timerParams = null) { this.accessCode = accessCode; this.status = status; this.moderator = moderator; diff --git a/server/model/Person.js b/server/model/Person.js index 5e1eb6f..3c6afa0 100644 --- a/server/model/Person.js +++ b/server/model/Person.js @@ -1,8 +1,8 @@ // noinspection DuplicatedCode class Person { - constructor(id, cookie, name, userType, gameRole=null, gameRoleDescription=null, alignment=null, assigned=false) { + constructor (id, cookie, name, userType, gameRole = null, gameRoleDescription = null, alignment = null, assigned = false) { this.id = id; - this.cookie = cookie + this.cookie = cookie; this.socketId = null; this.name = name; this.userType = userType; diff --git a/server/modules/ActiveGameRunner.js b/server/modules/ActiveGameRunner.js index ba0d32a..66b79f8 100644 --- a/server/modules/ActiveGameRunner.js +++ b/server/modules/ActiveGameRunner.js @@ -58,7 +58,7 @@ class ActiveGameRunner { minutes: game.timerParams.minutes }); game.startTime = new Date().toJSON(); - } + }; } class Singleton { diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 6438a62..5c36be1 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -50,7 +50,7 @@ class GameManager { }); socket.on(globals.CLIENT_COMMANDS.START_GAME, (accessCode) => { - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game && game.isFull) { game.status = globals.STATUS.IN_PROGRESS; if (game.hasTimer) { @@ -63,9 +63,9 @@ class GameManager { socket.on(globals.CLIENT_COMMANDS.PAUSE_TIMER, (accessCode) => { this.logger.trace(accessCode); - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game) { - let thread = this.activeGameRunner.timerThreads[accessCode]; + const thread = this.activeGameRunner.timerThreads[accessCode]; if (thread) { this.logger.debug('Timer thread found for game ' + accessCode); thread.send({ @@ -79,9 +79,9 @@ class GameManager { socket.on(globals.CLIENT_COMMANDS.RESUME_TIMER, (accessCode) => { this.logger.trace(accessCode); - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game) { - let thread = this.activeGameRunner.timerThreads[accessCode]; + const thread = this.activeGameRunner.timerThreads[accessCode]; if (thread) { this.logger.debug('Timer thread found for game ' + accessCode); thread.send({ @@ -94,9 +94,9 @@ class GameManager { }); socket.on(globals.CLIENT_COMMANDS.GET_TIME_REMAINING, (accessCode) => { - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game) { - let thread = this.activeGameRunner.timerThreads[accessCode]; + const thread = this.activeGameRunner.timerThreads[accessCode]; if (thread) { thread.send({ command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, @@ -113,17 +113,17 @@ class GameManager { }); socket.on(globals.CLIENT_COMMANDS.KILL_PLAYER, (accessCode, personId) => { - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game) { - let person = game.people.find((person) => person.id === personId) + const person = game.people.find((person) => person.id === personId); this.killPlayer(game, person, namespace, this.logger); } }); socket.on(globals.CLIENT_COMMANDS.REVEAL_PLAYER, (accessCode, personId) => { - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game) { - let person = game.people.find((person) => person.id === personId) + const person = game.people.find((person) => person.id === personId); if (person && !person.revealed) { this.logger.debug('game ' + accessCode + ': revealing player ' + person.name); person.revealed = true; @@ -133,51 +133,50 @@ class GameManager { id: person.id, gameRole: person.gameRole, alignment: person.alignment - }) + }); } } }); socket.on(globals.CLIENT_COMMANDS.TRANSFER_MODERATOR, (accessCode, personId) => { - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game) { - let person = game.people.find((person) => person.id === personId) + let person = game.people.find((person) => person.id === personId); if (!person) { - person = game.spectators.find((spectator) => spectator.id === personId) + person = game.spectators.find((spectator) => spectator.id === personId); } this.transferModeratorPowers(game, person, namespace, this.logger); } }); socket.on(globals.CLIENT_COMMANDS.CHANGE_NAME, (accessCode, data, ackFn) => { - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game) { - let person = findPersonById(game, data.personId); + const person = findPersonById(game, data.personId); if (person) { if (!isNameTaken(game, data.name)) { - ackFn("changed"); + ackFn('changed'); person.name = data.name.trim(); person.hasEnteredName = true; namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.CHANGE_NAME, person.id, person.name); } else { - ackFn("taken"); + ackFn('taken'); } } } }); socket.on(globals.CLIENT_COMMANDS.END_GAME, (accessCode) => { - let game = this.activeGameRunner.activeGames[accessCode]; + const game = this.activeGameRunner.activeGames[accessCode]; if (game) { game.status = globals.STATUS.ENDED; - for (let person of game.people) { + for (const person of game.people) { person.revealed = true; } namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.END_GAME, GameStateCurator.mapPeopleForModerator(game.people)); } - }) - } - + }); + }; createGame = (gameParams) => { const expectedKeys = ['deck', 'hasTimer', 'timerParams']; @@ -190,7 +189,7 @@ class GameManager { // to avoid excessive memory build-up, every time a game is created, check for and purge any stale games. pruneStaleGames(this.activeGameRunner.activeGames, this.activeGameRunner.timerThreads, this.logger); const newAccessCode = this.generateAccessCode(); - let moderator = initializeModerator(UsernameGenerator.generate(), gameParams.hasDedicatedModerator); + const moderator = initializeModerator(UsernameGenerator.generate(), gameParams.hasDedicatedModerator); if (gameParams.timerParams !== null) { gameParams.timerParams.paused = false; } @@ -206,12 +205,12 @@ class GameManager { this.activeGameRunner.activeGames[newAccessCode].createTime = new Date().toJSON(); return Promise.resolve(newAccessCode); } - } + }; joinGame = (code) => { - let game = this.activeGameRunner.activeGames[code]; + const game = this.activeGameRunner.activeGames[code]; if (game) { - let unassignedPerson = game.people.find((person) => person.assigned === false); + const unassignedPerson = game.people.find((person) => person.assigned === false); if (!unassignedPerson) { return Promise.resolve(new Error(globals.ERROR_MESSAGE.GAME_IS_FULL)); } else { @@ -220,7 +219,7 @@ class GameManager { } else { return Promise.resolve(404); } - } + }; generateAccessCode = () => { const numLetters = globals.ACCESS_CODE_CHAR_POOL.length; @@ -231,7 +230,7 @@ class GameManager { codeDigits.push(globals.ACCESS_CODE_CHAR_POOL[getRandomInt(numLetters)]); } return codeDigits.join(''); - } + }; transferModeratorPowers = (game, person, namespace, logger) => { if (person && (person.out || person.userType === globals.USER_TYPES.SPECTATOR)) { @@ -259,7 +258,7 @@ class GameManager { namespace.in(game.accessCode).emit(globals.EVENTS.SYNC_GAME_STATE); } - } + }; killPlayer = (game, person, namespace, logger) => { if (person && !person.out) { @@ -274,25 +273,24 @@ class GameManager { this.transferModeratorPowers(game, person, namespace, logger); } } - } + }; - - handleRequestForGameState = (namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket, handleNoMatch=true) => { + handleRequestForGameState = (namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket, handleNoMatch = true) => { const game = gameRunner.activeGames[accessCode]; if (game) { let matchingPerson = game.people.find((person) => person.cookie === personCookie); if (!matchingPerson) { matchingPerson = game.spectators.find((spectator) => spectator.cookie === personCookie); } - if (!matchingPerson && game.moderator.cookie === personCookie) { + if (!matchingPerson && game.moderator.cookie === personCookie) { matchingPerson = game.moderator; } if (matchingPerson) { if (matchingPerson.socketId === socket.id) { - logger.trace("matching person found with an established connection to the room: " + matchingPerson.name); + logger.trace('matching person found with an established connection to the room: ' + matchingPerson.name); ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } else { - logger.trace("matching person found with a new connection to the room: " + matchingPerson.name); + logger.trace('matching person found with a new connection to the room: ' + matchingPerson.name); socket.join(accessCode); matchingPerson.socketId = socket.id; ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); @@ -304,23 +302,23 @@ class GameManager { rejectClientRequestForGameState(ackFn); logger.trace('the game ' + accessCode + ' was not found'); } - } + }; handleRequestFromNonMatchingPerson = (game, socket, gameRunner, ackFn, logger) => { - let personWithMatchingSocketId = findPersonWithMatchingSocketId(game, socket.id); + const personWithMatchingSocketId = findPersonWithMatchingSocketId(game, socket.id); if (personWithMatchingSocketId) { - logger.trace("matching person found whose cookie got cleared after establishing a connection to the room: " + personWithMatchingSocketId.name); + logger.trace('matching person found whose cookie got cleared after establishing a connection to the room: ' + personWithMatchingSocketId.name); ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, personWithMatchingSocketId, gameRunner, socket, logger)); } else { - let unassignedPerson = game.moderator.assigned === false + const unassignedPerson = game.moderator.assigned === false ? game.moderator : game.people.find((person) => person.assigned === false); if (unassignedPerson) { - logger.trace("completely new person with a first connection to the room: " + unassignedPerson.name); + logger.trace('completely new person with a first connection to the room: ' + unassignedPerson.name); unassignedPerson.assigned = true; unassignedPerson.socketId = socket.id; ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson, gameRunner, socket, logger)); - let isFull = isGameFull(game); + const isFull = isGameFull(game); game.isFull = isFull; socket.to(game.accessCode).emit( globals.EVENTS.PLAYER_JOINED, @@ -328,29 +326,29 @@ class GameManager { isFull ); } else { // if the game is full, make them a spectator. - let spectator = new Person( + const spectator = new Person( createRandomId(), createRandomId(), UsernameGenerator.generate(), globals.USER_TYPES.SPECTATOR ); - logger.trace("new spectator: " + spectator.name); + logger.trace('new spectator: ' + spectator.name); game.spectators.push(spectator); ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, spectator, gameRunner, socket, logger)); } socket.join(game.accessCode); } - } + }; - removeClientFromLobbyIfApplicable(socket) { + removeClientFromLobbyIfApplicable (socket) { socket.rooms.forEach((room) => { if (this.activeGameRunner.activeGames[room]) { this.logger.trace('disconnected socket is in a game'); - let game = this.activeGameRunner.activeGames[room]; + const game = this.activeGameRunner.activeGames[room]; if (game.status === globals.STATUS.LOBBY) { - let matchingPlayer = findPlayerBySocketId(game.people, socket.id); + const matchingPlayer = findPlayerBySocketId(game.people, socket.id); if (matchingPlayer) { - this.logger.trace("un-assigning disconnected player: " + matchingPlayer.name); + this.logger.trace('un-assigning disconnected player: ' + matchingPlayer.name); matchingPlayer.assigned = false; matchingPlayer.socketId = null; matchingPlayer.cookie = createRandomId(); @@ -364,7 +362,7 @@ class GameManager { } } } - }) + }); } } @@ -372,21 +370,21 @@ function getRandomInt (max) { return Math.floor(Math.random() * Math.floor(max)); } -function initializeModerator(name, hasDedicatedModerator) { +function initializeModerator (name, hasDedicatedModerator) { const userType = hasDedicatedModerator ? globals.USER_TYPES.MODERATOR : globals.USER_TYPES.TEMPORARY_MODERATOR; - return new Person(createRandomId(), createRandomId(), name, userType);; + return new Person(createRandomId(), createRandomId(), name, userType); ; } -function initializePeopleForGame(uniqueCards, moderator) { - let people = []; +function initializePeopleForGame (uniqueCards, moderator) { + const people = []; let cards = []; // this will contain copies of each card equal to the quantity. let numberOfRoles = 0; - for (let card of uniqueCards) { - for (let i = 0; i < card.quantity; i ++) { + for (const card of uniqueCards) { + for (let i = 0; i < card.quantity; i++) { cards.push(card); - numberOfRoles ++; + numberOfRoles++; } } @@ -399,11 +397,11 @@ function initializePeopleForGame(uniqueCards, moderator) { moderator.gameRoleDescription = cards[j].description; moderator.alignment = cards[j].team; people.push(moderator); - j ++; + j++; } while (j < numberOfRoles) { - let person = new Person( + const person = new Person( createRandomId(), createRandomId(), UsernameGenerator.generate(), @@ -415,7 +413,7 @@ function initializePeopleForGame(uniqueCards, moderator) { person.customRole = cards[j].custom; person.hasEnteredName = false; people.push(person); - j ++; + j++; } return people; @@ -439,11 +437,11 @@ function createRandomId () { return id; } -function rejectClientRequestForGameState(acknowledgementFunction) { +function rejectClientRequestForGameState (acknowledgementFunction) { return acknowledgementFunction(null); } -function findPersonWithMatchingSocketId(game, socketId) { +function findPersonWithMatchingSocketId (game, socketId) { let person = game.people.find((person) => person.socketId === socketId); if (!person) { person = game.spectators.find((spectator) => spectator.socketId === socketId); @@ -454,15 +452,15 @@ function findPersonWithMatchingSocketId(game, socketId) { return person; } -function findPlayerBySocketId(people, socketId) { +function findPlayerBySocketId (people, socketId) { return people.find((person) => person.socketId === socketId && person.userType === globals.USER_TYPES.PLAYER); } -function isGameFull(game) { +function isGameFull (game) { return game.moderator.assigned === true && !game.people.find((person) => person.assigned === false); } -function findPersonById(game, id) { +function findPersonById (game, id) { let person; if (id === game.moderator.id) { person = game.moderator; @@ -471,22 +469,22 @@ function findPersonById(game, id) { person = game.people.find((person) => person.id === id); } if (!person) { - person = game.spectators.find((spectator) => spectator.id === id) + person = game.spectators.find((spectator) => spectator.id === id); } return person; } -function isNameTaken(game, name) { - let processedName = name.toLowerCase().trim(); +function isNameTaken (game, name) { + const processedName = name.toLowerCase().trim(); return (game.people.find((person) => person.name.toLowerCase().trim() === processedName)) || (game.moderator.name.toLowerCase().trim() === processedName) - || (game.spectators.find((spectator) => spectator.name.toLowerCase().trim() === processedName)) + || (game.spectators.find((spectator) => spectator.name.toLowerCase().trim() === processedName)); } -function pruneStaleGames(activeGames, timerThreads, logger) { +function pruneStaleGames (activeGames, timerThreads, logger) { for (const [accessCode, game] of Object.entries(activeGames)) { if (game.createTime) { - let createDate = new Date(game.createTime); + const createDate = new Date(game.createTime); if (createDate.setHours(createDate.getHours() + globals.STALE_GAME_HOURS) < Date.now()) { // clear games created more than 12 hours ago logger.info('PRUNING STALE GAME ' + accessCode); delete activeGames[accessCode]; @@ -513,5 +511,4 @@ class Singleton { } } - module.exports = Singleton; diff --git a/server/modules/GameProcess.js b/server/modules/GameProcess.js index bf1227c..dd44e5e 100644 --- a/server/modules/GameProcess.js +++ b/server/modules/GameProcess.js @@ -24,7 +24,7 @@ process.on('message', (msg) => { case globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER: timer.stopTimer(); logger.trace('CHILD PROCESS ' + msg.accessCode + ': PAUSE TIMER'); - process.send({ command: globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, timeRemaining: timer.currentTimeInMillis}); + process.send({ command: globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, timeRemaining: timer.currentTimeInMillis }); break; @@ -35,7 +35,7 @@ process.on('message', (msg) => { process.exit(0); }); logger.trace('CHILD PROCESS ' + msg.accessCode + ': RESUME TIMER'); - process.send({ command: globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, timeRemaining: timer.currentTimeInMillis}); + process.send({ command: globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, timeRemaining: timer.currentTimeInMillis }); break; @@ -50,4 +50,3 @@ process.on('message', (msg) => { break; } }); - diff --git a/server/modules/GameStateCurator.js b/server/modules/GameStateCurator.js index 74859f7..8d24ceb 100644 --- a/server/modules/GameStateCurator.js +++ b/server/modules/GameStateCurator.js @@ -1,4 +1,4 @@ -const globals = require("../config/globals") +const globals = require('../config/globals'); /* The purpose of this component is to only return the game state information that is necessary. For example, we only want to return player role information to moderators. This avoids any possibility of a player having access to @@ -12,7 +12,7 @@ const GameStateCurator = { mapPeopleForModerator: (people) => { return people .filter((person) => { - return person.assigned === true + return person.assigned === true; }) .map((person) => ({ name: person.name, @@ -40,10 +40,10 @@ const GameStateCurator = { return { name: person.name, id: person.id, userType: person.userType, out: person.out, revealed: person.revealed }; } } -} +}; -function getGameStateBasedOnPermissions(game, person, gameRunner) { - let client = game.status === globals.STATUS.LOBBY // people won't be able to know their role until past the lobby stage. +function getGameStateBasedOnPermissions (game, person, gameRunner) { + const client = game.status === globals.STATUS.LOBBY // people won't be able to know their role until past the lobby stage. ? { name: person.name, hasEnteredName: person.hasEnteredName, id: person.id, cookie: person.cookie, userType: person.userType } : { name: person.name, @@ -56,11 +56,11 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { customRole: person.customRole, alignment: person.alignment, out: person.out - } + }; switch (person.userType) { case globals.USER_TYPES.PLAYER: - case globals.USER_TYPES.KILLED_PLAYER: - let state = { + case globals.USER_TYPES.KILLED_PLAYER: { + const state = { accessCode: game.accessCode, status: game.status, moderator: GameStateCurator.mapPerson(game.moderator), @@ -68,18 +68,19 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { deck: game.deck, people: game.people .filter((person) => { - return person.assigned === true + return person.assigned === true; }) .map((filteredPerson) => GameStateCurator.mapPerson(filteredPerson) ), timerParams: game.timerParams, - isFull: game.isFull, - } + isFull: game.isFull + }; if (game.status === globals.STATUS.ENDED) { state.people = GameStateCurator.mapPeopleForModerator(game.people); } return state; + } case globals.USER_TYPES.MODERATOR: return { accessCode: game.accessCode, @@ -91,7 +92,7 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { timerParams: game.timerParams, isFull: game.isFull, spectators: game.spectators - } + }; case globals.USER_TYPES.TEMPORARY_MODERATOR: return { accessCode: game.accessCode, @@ -101,12 +102,12 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { deck: game.deck, people: game.people .filter((person) => { - return person.assigned === true + return person.assigned === true; }) .map((filteredPerson) => GameStateCurator.mapPerson(filteredPerson)), timerParams: game.timerParams, isFull: game.isFull - } + }; case globals.USER_TYPES.SPECTATOR: return { accessCode: game.accessCode, @@ -116,12 +117,12 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) { deck: game.deck, people: game.people .filter((person) => { - return person.assigned === true + return person.assigned === true; }) .map((filteredPerson) => GameStateCurator.mapPerson(filteredPerson)), timerParams: game.timerParams, - isFull: game.isFull, - } + isFull: game.isFull + }; default: break; } diff --git a/server/modules/Logger.js b/server/modules/Logger.js index 49429be..80d5d96 100644 --- a/server/modules/Logger.js +++ b/server/modules/Logger.js @@ -31,7 +31,7 @@ module.exports = function (logLevel = globals.LOG_LEVEL.INFO) { console.debug('DEBUG ', now.toGMTString(), ': ', message); }, - trace(message = '') { + trace (message = '') { if ( logLevel === globals.LOG_LEVEL.INFO || logLevel === globals.LOG_LEVEL.WARN diff --git a/server/modules/ServerBootstrapper.js b/server/modules/ServerBootstrapper.js index a946de3..a59f3cb 100644 --- a/server/modules/ServerBootstrapper.js +++ b/server/modules/ServerBootstrapper.js @@ -3,12 +3,12 @@ const http = require('http'); const https = require('https'); const path = require('path'); const fs = require('fs'); -const cors = require('cors') +const cors = require('cors'); const ServerBootstrapper = { processCLIArgs: () => { try { - let args = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())); + const args = Array.from(process.argv.map((arg) => arg.trim().toLowerCase())); const useHttps = args.includes('protocol=https'); const port = process.env.PORT || args .filter((arg) => { @@ -31,7 +31,7 @@ const ServerBootstrapper = { logLevel: logLevel }; } catch (e) { - throw new Error("Your server run command is malformed. Consult the codebase wiki for proper usage. Error: " + e); + throw new Error('Your server run command is malformed. Consult the codebase wiki for proper usage. Error: ' + e); } }, @@ -67,40 +67,40 @@ const ServerBootstrapper = { let io; if (process.env.NODE_ENV.trim() === 'development') { const corsOptions = { - origin: "http://localhost:" + port, + origin: 'http://localhost:' + port, optionsSuccessStatus: 200, - methods: ["GET", "POST"] - } + methods: ['GET', 'POST'] + }; app.use(cors(corsOptions)); - io = require("socket.io")(main, { + io = require('socket.io')(main, { cors: { - origin: "http://localhost:" + port, - methods: ["GET", "POST"], - allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], + origin: 'http://localhost:' + port, + methods: ['GET', 'POST'], + allowedHeaders: ['Content-Type', 'X-Requested-With', 'Accept'], credentials: false } }); } else { const corsOptions = { - origin: ["https://playwerewolf.uk.r.appspot.com"], - methods: ["GET", "POST"], - allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], - optionsSuccessStatus: 200, - } + origin: ['https://playwerewolf.uk.r.appspot.com'], + methods: ['GET', 'POST'], + allowedHeaders: ['Content-Type', 'X-Requested-With', 'Accept'], + optionsSuccessStatus: 200 + }; app.use(cors(corsOptions)); - io = require("socket.io")(main, { + io = require('socket.io')(main, { cors: { - origin: ["https://playwerewolf.uk.r.appspot.com", "wss://playwerewolf.uk.r.appspot.com"], - methods: ["GET", "POST"], - allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"], + origin: ['https://playwerewolf.uk.r.appspot.com', 'wss://playwerewolf.uk.r.appspot.com'], + methods: ['GET', 'POST'], + allowedHeaders: ['Content-Type', 'X-Requested-With', 'Accept'], credentials: true }, - transports: ["polling"] + transports: ['polling'] }); } return io.of('/in-game'); } -} +}; module.exports = ServerBootstrapper; diff --git a/server/modules/ServerTimer.js b/server/modules/ServerTimer.js index 603a93d..ad29705 100644 --- a/server/modules/ServerTimer.js +++ b/server/modules/ServerTimer.js @@ -4,8 +4,8 @@ function stepFn (serverTimerInstance, expected) { serverTimerInstance.currentTimeInMillis = serverTimerInstance.totalTime - (now - serverTimerInstance.start); if (now - serverTimerInstance.start >= serverTimerInstance.totalTime) { // check if the time has elapsed serverTimerInstance.logger.debug( - 'ELAPSED: ' + (now - serverTimerInstance.start) + 'ms (~' - + (Math.abs(serverTimerInstance.totalTime - (now - serverTimerInstance.start)) / serverTimerInstance.totalTime).toFixed(3) + '% error).' + 'ELAPSED: ' + (now - serverTimerInstance.start) + 'ms (~' + + (Math.abs(serverTimerInstance.totalTime - (now - serverTimerInstance.start)) / serverTimerInstance.totalTime).toFixed(3) + '% error).' ); serverTimerInstance.timesUpResolver(); // this is a reference to the callback defined in the construction of the promise in runTimer() clearTimeout(serverTimerInstance.ticking); @@ -22,7 +22,6 @@ function stepFn (serverTimerInstance, expected) { } class ServerTimer { - constructor (hours, minutes, tickInterval, logger) { this.hours = hours; this.minutes = minutes; @@ -36,8 +35,8 @@ class ServerTimer { this.totalTime = null; } - runTimer (pausedInitially=true) { - let total = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); + runTimer (pausedInitially = true) { + const total = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); this.totalTime = total; this.currentTimeInMillis = total; this.logger.debug('STARTING TIMER FOR ' + this.totalTime + 'ms'); @@ -59,14 +58,13 @@ class ServerTimer { return this.timesUpPromise; } - stopTimer() { + stopTimer () { if (this.ticking) { clearTimeout(this.ticking); } - let now = Date.now(); } - resumeTimer() { + resumeTimer () { this.logger.debug('RESUMING TIMER FOR ' + this.currentTimeInMillis + 'ms'); this.start = Date.now(); this.totalTime = this.currentTimeInMillis; @@ -83,11 +81,11 @@ class ServerTimer { } } -function convertFromMinutesToMilliseconds(minutes) { +function convertFromMinutesToMilliseconds (minutes) { return minutes * 60 * 1000; } -function convertFromHoursToMilliseconds(hours) { +function convertFromHoursToMilliseconds (hours) { return hours * 60 * 60 * 1000; } diff --git a/server/modules/UsernameGenerator.js b/server/modules/UsernameGenerator.js index 498c202..81a839a 100644 --- a/server/modules/UsernameGenerator.js +++ b/server/modules/UsernameGenerator.js @@ -3,9 +3,9 @@ const usernameGenerator = { const randAdjIndex = Math.floor(Math.random() * adjectives.length); const randAnimalIndex = Math.floor(Math.random() * animals.length); const randNumber = Math.floor(Math.random() * 100); - return adjectives[randAdjIndex].charAt(0).toUpperCase() + adjectives[randAdjIndex].slice(1) - + animals[randAnimalIndex].replace(/ /g, '') - + randNumber; + return adjectives[randAdjIndex].charAt(0).toUpperCase() + adjectives[randAdjIndex].slice(1) + + animals[randAnimalIndex].replace(/ /g, '') + + randNumber; } }; diff --git a/server/routes/router.js b/server/routes/router.js index d60c298..9ec2a57 100644 --- a/server/routes/router.js +++ b/server/routes/router.js @@ -26,5 +26,4 @@ router.get('/readiness_check', (req, res) => { res.sendStatus(200); }); - module.exports = router; diff --git a/server/routes/util.js b/server/routes/util.js index 56b38c7..4d1626f 100644 --- a/server/routes/util.js +++ b/server/routes/util.js @@ -1,9 +1,9 @@ const fs = require('fs'); -function checkIfFileExists(file) { +function checkIfFileExists (file) { return fs.promises.access(file, fs.constants.F_OK) .then(() => true) - .catch((e) => { console.error(e); return false }); + .catch((e) => { console.error(e); return false; }); } module.exports = checkIfFileExists;

veQrI+3u9$>rQxm*p!j zx+T6@bV_6L-1nW^tQBrv4H0U+w}5w|^OF(}=2?o@cI=9@JhpP`A(GtJWhd}<}5bagAOcN9JDlXU@wwW%q>#3@%-b1Mw>2V!5u1)vu*`a+#VSAFQ z=L8ia&jntd>bBNDO*_<6PyLp539!6db#N0;-H89TZt_*X>*#RU#y( zXyd`O`hbG#iO)YnI%48%W|oWfh$$_3!18&*nNM6|;_KFy>N-zzm8xv{T#>7}#ARb! z`-T+8=2>NdZ7JK7{aj>NIA=PatDeYY{=F|@PfTC4fc6A7j&(`@j0|@ei#$+VE-D|m zT;+P{nz#bXuodnOQ*CPxPO&IrX!yP^xXa1MdgI$0ufJB+K73uf`R!tvRE~ysTi)*e zbwKEj(vkCpQ4>#GUO_QmvAUQh^kMk%6J5u7Rblp=pSLiIt&=m9eQVkYQj@_3!>t6b-rg aDVb@NxHWJuPL~8~VDNPHb6Mw<&;$UYPM@~` diff --git a/assets/images/roles-small/Seer.png b/assets/images/roles-small/Seer.png deleted file mode 100644 index 3e3373ba3cd2ecf9b3b8985373612d5a2dfa5a4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 871 zcmeAS@N?(olHy`uVBq!ia0vp^DIm14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>xEN&G77mKKmYeX_1biK%Z7wyLfjW(4@_u1 z?@^~Z$8V;O+T@cdMl;XIb0|J^aZHj`%nDWC(i7z@YSegQ&V*?z9c`m}_h0JmoT%v` zdCa3j$-{ifl+M7O?|UK?-An>+WEHx5bY}=nYYXHt;?dB#{HXhdo{GuJw~azA2?bey z*^7A&xuE`{|Sf6``G@~29)a`R29JMKMOO^>-U>ZV{~ zPDu4c3nhOo^Jaq;J-?P2wur~Kh<#bjE%m2z-igIioE5p3$tA~|ESn?#Dd3pIj;z^b zTnBcoJ2vG~f$DEX1ChX_P`8O`-3K@`OC{b!aOk zv?Nc(aSBMx&Q|>`c<1@wg|Vsb^K2sC7%8rPlKAN9k?+<@tM}=BQ>}@Psd!hKe^GGb z+)L)k?lJG&s&DHhpN>D)HhuDe_tU25Yrp;bu;GpHyB|mIh8xx1and{e%DvAQO` zioSn8nf>sT0vD0|C*6u&@1N_3y)yUs^1s^%8bkk$=Zkd6UMfB3bsZS*iY2ZQCC){u zi6xo&c?uz!xv30B28Nba29{Qah6)A-Rz`+aCT6@LQ~v@LtAZ4#B$lLFC4+Q8lKdAc7?@ZYnpzo{=>i!B23pZ8pQ32U%}>cps|1^kRYNqJU?fljgQu&X%Q~loCID&c BMw0*l diff --git a/assets/images/roles-small/Shadow.png b/assets/images/roles-small/Shadow.png deleted file mode 100644 index a591ff093e05ca96499bb2983c06c640fa8e7766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 824 zcmeAS@N?(olHy`uVBq!ia0vp^DImNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lrRYJ32_C|Kp-O{6BiRB zC@8prg@uic?Ypt@eR=tycJKXsd~?{?>e$#4SXi7TIR5|t-?B4uJ5U2-NswPKgTu2M zX&_FLx4R3!#4d)LKn`btM`SSr1K(i~W;~w10_0})5>H=O_Q#z3Ec_~4pFYe4>e=S$ z;usQf`0Z3fzrzj!u8*Y+wgp{KsJ!OolG!hP>&PPigRBmZ7&50xA1Qhnvd=LkuVJU- zA>IWiSc}-wt{>~r+RW>^C4SqJL;nBYpF8s|<z?K8$_9dJ7OlV z8>|=MoG|g_DH}hJ_1q5Als8S7Dla58F(ySg;ZtzUf|x6*ifS$|9iB|GG*+G?Ub|(D zfMS|L=)RQs9Y*tJd_MWcve9O;O+xD_6?KPxjX3jL+O8zN`Q){wzkl309n;gBRlrlWTeQTdGxe0?F*SO)A!vOwbdS1{)#;edbeZBD^H_oHS(?762014Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>TbOxFY)0H2;b}R-BD*~#MpX!zW@LK|E)CdSQ6wH z%;50s2FU4*N#5=*ibryI#ef|45>H=O_Q#z3ECQUi37eZ37?^H&x;Tbp+TCQ@A;)1YHPZ;oFc>Fi=`d96a z=Kd?k9~*8~>YRCTj^FWJCw&wixk^j-D!bpJA zZT7R8JR`A9T?>>ZZ=Y~Db=`vEb18qSRqD4-^O%|)Xp`bk1X6PPXGB<1vo$J+3~c7uYAXIi#QJn`k-b2hE^0L3TOF`D}c!(?S=nbdERJEwQy+<$|>(I7yY?J^`V}p z`wQvN&Ar>D09&`Ml!YaP#=uhjv3fzL; zE-Fe6sl2jfE{eIk--v0=QLQawoND^zQy>41R}cNR9Q-utmn-ijO_dtvasT1f zRYD5SME$2L@!M_qd^4|f@1CgAIR{;Nj6$CIY`XljV{T>Dx7)er(tnxqh-Mr)bNP1d zrY4T}3>Ax}I4T|ViC5V7vB&66TVmsv*KdV3Uw;?c_OR^~&&#wEZ5zuYmm~__aB0ww zO7*_ESmTJsHS>+@zZYHy ze($(&;*HCl6Z7hPy_-62U(Z_p##t_H&9VNE*Z3YYarU@8DOP#h?v&8CA;!a0sVRPy zduc;8$LY=kH~KOTFmX=qDEKRVM4@KtLmTcxSC7xF+BWHfxR;I5-w9FvlM+RyS)ER9 z{Twd6%30ss3k?2k|HHa)SJ+3leOY&a$zHX@HKHUXu_VKdAc u7?@ZYnppwaK!$;Vxk?g0iiX_$l+3hB+#1{}Z*2x@VDNPHb6Mw<&;$SwYRK#W diff --git a/assets/images/roles-small/Villager.png b/assets/images/roles-small/Villager.png deleted file mode 100644 index 281636071ac970521992ebfa76f52e239d0630b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1012 zcmeAS@N?(olHy`uVBq!ia0vp^DIm14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>x#u7^9*k?Y26 zZ(*kjwU`-C=UD&$|KIcnPaaSkV@Z%-FoVOh8zA>FCV9KNNX*V`l>>6vOFVsD*&lQA zvj}Li@lEz(U|^c*>EalYaqsQ*+eNn(1lSTzt~ke)D5ULH```R+kzHC;;|JfL>2m^Y zKGwyFrKHbt1A~9d&%9@NHt$r9-1c`K=fiSx@4BiV)xCVD@L#Hw`d zU0DWG<-9h<%@sS7zq&8DGkRy%&JNE@r+WHtmmX94d|IA4n}~ zIPt4!ZrTo>!eet=x}uh^7I`$$!Eh(fPIk|U-BV7Sn40Dy-^bF)=d*axDdh+_WMmNL5c_OD>R*Y zc}e=1;SaUR7hbGjFsNCVB%a@&d&@^#RVAy%kk2$d^|p`klVIhPul9mJc|;z}xvIcn zv&Cs&*NN%w4vM13>#A4e>=K@(VDgH^;;`GQ3C};T6xg{p!){W>R)LvX?-Ce;Zp2-D zc4+U>O4&`hmo${8EUWfbH0?50+msyU!-gzEYvgm)>aOo-8NsQ?DLVnBF)7y;d0m8l<;81QxiAt7O#<)i4u(EJ8-x5 zV9QNAhMK37yPSM1)%ezYt=e1n_^V>f&n5jHNuqk=<$iYv>4Kc|X2G-dZyHty%J?iXGiZ?eOUfn}QQ z6KB_Wj-M*oW+qDZA-O+w76}H*-}*GiC*NV&I*#OD+wue!wiU=+Ots$hD*D|mVa9d# zHdRVLcz4yS|Ek{gE1u!a!Gh0aF~*CmXI8z_Tj?^l#7V8(uW2b4%b8Y=6}Q918lL@r zJf(i(iA#Ph9>@2v@62%hx9p2KG=Z7cGi*KL*XFYJ<5^(3RV{IiC`m~yNwrEYN(E93 zMh1qKx(1fIhNd9~CRT=KRwjnJK!$-qL!;6p6b-rgDVb@NxHWvdGfNMsfx*+&&t;uc GLK6UvH=26@ diff --git a/assets/images/roles-small/wolf_logo.png b/assets/images/roles-small/wolf_logo.png deleted file mode 100644 index f57147a48c802077b395685e7b228dc96552e250..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3921 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 m5a_oK0Qm2L@qM(Iv$CfDJi$V diff --git a/assets/images/roles/DreamWolf.png b/assets/images/roles/DreamWolf.png deleted file mode 100644 index bf5b085ed82fe913ddd4c27f7ab4a049085c488e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8886 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 diff --git a/assets/images/roles/Hunter.png b/assets/images/roles/Hunter.png deleted file mode 100644 index c4251e3204003c7642b172cda4a5a867f4af3f22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1875 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 diff --git a/assets/images/roles/Minion.png b/assets/images/roles/Minion.png deleted file mode 100644 index e48ec91a434b4f4863507bff978a7b3862951db1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1810 zcmZ`)Yfw^Y82(U{5{b6m#3;qubgU%E%o0-~voz8&&2hxSG;OsAE74L`3{z_Dva`)* znZ=?xUN&_rH7_X;TTaa~Wf$>Q%9>(c^0;Ll6*+qVXZKe#=gc?n`+d*%KF@RBdC&Ph z!8-^xWE%hgAuwRuUH~vc<2%n1&CGZ~FuGt5vw~Ov+`Wv~AGAVar`UkKK>(z=0FZeW zfN3<9IS7D&4!}?}0E}w@kdg`-LN=ot!FzUv`J?L{45q`?6>9rC~NT@$L_2Csp`IRG0KGw6Zf!&sSTqVySENTa&%ammgE_k zI#-|5*xH$PCmd_64P{Z^+BQ}Xisj2qrR086yCEOGdZ26;>hCMA&*hcam{?0zE0}`( zrHVx{1cQ6XbW@A@V&(RBDXBBI)nO)XP~bXyi{fHte3YjDvLX`CEL8E$lw@rRlD@|B zT35i5EsIS;iTk!_daITw@_V`FBg$HZ#d*wCYHTZS3T(E z*I!R5e`O2IPTs{wTui0vyS-{{`!wwDGp0nH1Vd{U5fQ@d+=Qt(i4W9rQtjirG1J1( zwNMB{LWZk$OnPk6eE??RaDH_-0NkUf-^hO$#NB zXKYYB5y)=^Io zn;z42Z^g*O%S`~?Qc2_v&29~$miEUD%Z9XGtE9*<45O*T6`OnTZsK z=5lFuqoDThypu=wC*g%1hkiY%Kso#BCt%UOAZ;Gz^V&*YMRRG2+WPk=dM+Ti##OMk&Y(vNQ5g_2h69wLN(GyJ+6yD&{8XBj#M4O_>`Y4y0RK&g+P33V%Z> z+{L?Rc4ivmrlb*mYmP`6j^PwN!1HEU&ty20i>Z|l+iPfW{>8S=?3DRUhT?O@la?qi z3BjO*hL@H;X(pW00AxCtl(bqZ1eM*c$_yJk>JNDU%sUGhB7kw_;Er z^BS2}UD*Ih$iT>$2@K0#ws*a_EF%x3zv=Ot81K8=ce2f?=iZA)CJ%UUv17m){&>1- zbqhCFE|B@mDY_uXhSF-;Nr>$mcyg=ptPpNd^?aK3d2hD%0u%QsCE?E~{Asnzk=?Fn zv62ccb7$PQ_pRhnVd+FYXX8Hh*!ocd5+X)Bq}yeFplnD_J$kce5V_Fh?qC5Qa5wge{RU>7&f8@E^(grYuy!Dlq~q>*AH! zM5G;M9jv!_m|QecBcIoVJ%B?$+bgWGXWpe$`iMF$4A+gKmnnw5uX>)dGLMvs#~Sas z=`uI2psWrEJ2#3Hrh!R^zcyI^%i~5wXJ=z>Mm{C2LPxrDvVTPKp@YeBjMx)#=mOm7 z>%6FRFX}pvFn2n`!=16gol5s$(CI%Z@Wx-){{SBUP(u9auLEXo-j|~Q5a=Jgt#WI$ F_+M+3T{8dx diff --git a/assets/images/roles/Seer.png b/assets/images/roles/Seer.png deleted file mode 100644 index cc4ed3f09be68266d0da5ca6f2a42a0ba46470e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1401 zcmaJ=X;4#F7`Pw zl%yCN%XO9j0Gqhjs8j$5eEeHQ#F*E?S{U2R^4W=O09wUXNDc|Z^xW9gL;#My2Y~2v z0CX57nggJm0l-2w0L*g$&<ykH&UvIl1L|$&kwi7SOh@W0C%jXjkc7dSCNXJzuie8@l2Nx4~sYz)d{OLFs2YcgV zqNvtgyXrAO=EWsOlhqch?HOwwg_NUMUhlXlcKRoii;sp&rarL!VtBk{IR`5toc?^amW`OH z$ki?z1+gyrnSlb^`7z!62@KZ31d03WKYT?lQ3xG6CZ`limizX5wz1)%mn+zgm1l35 zzg<9Zx*cK^xK=2IH(zzMO>@D2qF_s*9BiN)+r5OA?<~hlOZJ~_X0=bVjkf2#r!tqu zz7>GQsvE-=^-C4I>hWmRE&*Ack{KOyX!CRpE9=($^3Ps)3OiPT1M0~S5gl5kQkX0X z?@FO5N^-MD2hkQaGgi%c3Y-pi^-vxwcHGZEsJJi|Y3zaZKZQ|c_3f~SF)~{M%X*F^ zBUuks4W^XSRMZ;Mn1KEYJm^exw8g}9C~~1`G?3rVI@tQqOQ6pgpfw#fU0mOE9hNED zX$i&03e?-S=(KuLXCANxd9-j z!tDY-lr$ViyL_D@oO46=IKO`IELMu?@eVPQS_v$f>k)ur_b zHD@p}h;tM0;+0Z(u_CR)_5kimh8jnaBiwGc5C<^^*r!u~aEm zaRka5+|egcB%!`9=KuB)xrK(NRjt%<3P3q~q)54~%8vsjU)jgM8lIv+{esBAn`NA2ru^<~sDcS@{^4avFN; z5_6Eq(P%@VfI5NZW7SDvqN-}3DWe^Iae#MisbY7naax!BRaPul+ zo4dMfjSb651c#WWP+Y5^gd@(zXP4r#Fs#)Bxxy%}>YxQw=JA!uMlIp3ui12%?T!*T z>!#_XAG_CJlshkaCoeCDw~v`ywh!BYAHz4;n-T2o8<6hDUPAHNXa52W>v0zV diff --git a/assets/images/roles/Shadow.png b/assets/images/roles/Shadow.png deleted file mode 100644 index 11f5e645d9a8d52e70a812c625ee8edc1f1dce0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1178 zcmeAS@N?(olHy`uVBq!ia0y~yV5|XQ7G|J`-AUP>K#C>Z(btiIVPik{pF~z5Um@8e z$d`ekN{xY`p@o6r7f`6-1p`B=0RzLU1O^7H84L{K1#@-<+5jaC0(?STfiw`v$jHRS z#0UxsZeU?yV`KYnY_!@hljQC0!Y{Fl;UPsCUysVAnHfq^;7)5S5Q;?~Yp*C!E?+{?22PiYGSob;0iDgAeL1bNtD>mih1Q!RChrKbEX!{_s)z z61(@G0?UL8Ta|nI<0a)^RLWW%`td{bWwTICI@^QC;m4+5{w$xcc3wmOe!)5u-`n$_ z%IeAa->Pf)_|g1Wzqose_Ur-+-mO&(c6CqRx&GL)Rlc=Y&tulxu8{y32b54EFz{IGp4zn1re{Nu;pJLThM^wuYKvmg0? z?D%`@|GPeX{1`242Lb==KWtBL-~Q~?-Esjhr|H225lGTsBn*Z26> zr`-Maz|Xi&8|du(4*7L`c{RT;mVPg>HN78S9z9i8w)^XjDfW>)a~?h}pI84i z*KF}2-|LTg$|~~uF z*&jUqpZ~`I7#s0__CG!hjJ|yz4tIxcf4u$4Q_Vlyt`zRWp3K2%|4KnULy~6KyOsHO+t(etV|89jLmcnOsotHCPg}hplHa= bPsvQH#I4~m%P(Ld!oc9^>gTe~DWM4fZPXPt diff --git a/assets/images/roles/Sorcerer.png b/assets/images/roles/Sorcerer.png deleted file mode 100644 index a85b4da539bfca29fb5e7002acf530cba00aa08b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1903 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 z 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%0DqR6^OVeFAx-? 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