summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc3
-rw-r--r--DiscoBot/session.json75
-rw-r--r--WebInterface/NodeJSServer/dist/about/index.html2
-rw-r--r--WebInterface/NodeJSServer/dist/script/about.js2
-rw-r--r--WebInterface/NodeJSServer/dist/script/index.js4
-rw-r--r--WebInterface/NodeJSServer/dist/script/play.js17
-rw-r--r--WebInterface/NodeJSServer/src/about.js4
-rw-r--r--WebInterface/NodeJSServer/src/index.js18
-rw-r--r--WebInterface/NodeJSServer/src/js/about.js6
-rw-r--r--WebInterface/NodeJSServer/src/js/index.js12
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/interface.js82
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/networking/commands/_command.js28
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/networking/commands/login/createServer.js23
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/networking/commands/login/listServers.js43
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/networking/commands/login/login.js54
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/networking/commands/loginCmds.js36
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/networking/commands/playCmds.js31
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/networking/hash.js (renamed from WebInterface/NodeJSServer/src/modules/hash.js)0
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/networking/networker.js89
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/collections/about.js14
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/collections/login.js24
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/collections/play.js19
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/components/backdrop.js (renamed from WebInterface/NodeJSServer/src/modules/ui/backdrop.js)50
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/components/modal/login-modal.js139
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/components/modal/modal.js (renamed from WebInterface/NodeJSServer/src/modules/ui/modal.js)0
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/components/notification-banner.js (renamed from WebInterface/NodeJSServer/src/modules/ui/notification-banner.js)100
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/components/router.js44
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/components/server-listing.js91
-rw-r--r--WebInterface/NodeJSServer/src/js/modules/ui/uiManager.js60
-rw-r--r--WebInterface/NodeJSServer/src/js/play.js12
-rw-r--r--WebInterface/NodeJSServer/src/js/src_old/modules/playModule.js (renamed from WebInterface/NodeJSServer/src/modules/playModule.js)0
-rw-r--r--WebInterface/NodeJSServer/src/js/src_old/modules/server-client.js (renamed from WebInterface/NodeJSServer/src/modules/server-client.js)0
-rw-r--r--WebInterface/NodeJSServer/src/js/src_old/modules/ui/login-modal.js (renamed from WebInterface/NodeJSServer/src/modules/ui/login-modal.js)0
-rw-r--r--WebInterface/NodeJSServer/src/js/src_old/modules/ui/modal.js67
-rw-r--r--WebInterface/NodeJSServer/src/js/src_old/modules/ui/server-listing.js (renamed from WebInterface/NodeJSServer/src/modules/ui/server-listing.js)0
-rw-r--r--WebInterface/NodeJSServer/src/play.js11
-rw-r--r--WebInterface/NodeJSServer/webpack.config.js6
37 files changed, 981 insertions, 185 deletions
diff --git a/.eslintrc b/.eslintrc
index 4250c16..bdf49cf 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -7,6 +7,7 @@
},
"rules": {
"no-unused-vars": "off",
- "no-extend-native": "off"
+ "no-extend-native": "off",
+ "arrow-parens": "off"
}
}
diff --git a/DiscoBot/session.json b/DiscoBot/session.json
index 03c46f3..c81cbf9 100644
--- a/DiscoBot/session.json
+++ b/DiscoBot/session.json
@@ -1,79 +1,6 @@
{
"GeneralContext": null,
"Relation": {},
- "Chars": [
- {
- "Name": "Felis Exodus Schattenwald",
- "Lebenspunkte_Aktuell": 30,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 20
- },
- {
- "Name": "Gardist",
- "Lebenspunkte_Aktuell": 29,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 0
- },
- {
- "Name": "Hartmut Reiher",
- "Lebenspunkte_Aktuell": 31,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 0
- },
- {
- "Name": "Helga vom Drachenei, Tausendsasserin",
- "Lebenspunkte_Aktuell": 21,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 35
- },
- {
- "Name": "Krenko",
- "Lebenspunkte_Aktuell": 25,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 0
- },
- {
- "Name": "Ledur Torfinson",
- "Lebenspunkte_Aktuell": 39,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 0
- },
- {
- "Name": "Morla",
- "Lebenspunkte_Aktuell": 26,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 13
- },
- {
- "Name": "Numeri Illuminus",
- "Lebenspunkte_Aktuell": 28,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 40
- },
- {
- "Name": "Potus",
- "Lebenspunkte_Aktuell": 39,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 16
- },
- {
- "Name": "Pump aus der Gosse",
- "Lebenspunkte_Aktuell": 18,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 13
- },
- {
- "Name": "Rhoktar4",
- "Lebenspunkte_Aktuell": 34,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 17
- },
- {
- "Name": "Volant",
- "Lebenspunkte_Aktuell": 28,
- "Ausdauer_Aktuell": 0,
- "Astralpunkte_Aktuell": 43
- }
- ],
+ "Chars": [],
"SessionName": null
} \ No newline at end of file
diff --git a/WebInterface/NodeJSServer/dist/about/index.html b/WebInterface/NodeJSServer/dist/about/index.html
index c490415..d7294b7 100644
--- a/WebInterface/NodeJSServer/dist/about/index.html
+++ b/WebInterface/NodeJSServer/dist/about/index.html
@@ -8,7 +8,7 @@
<meta name="author" content="Timon Scholz">
<meta name="theme-color" content="#546e7a">
<title>Helden Online</title>
- <link rel="icon" href="ressources/DSALogo.png">
+ <link rel="icon" href="../ressources/DSALogo.png">
<link rel="stylesheet" href="../style/about.css">
<!-- <link rel="manifest" href="manifest.webmanifest"> -->
<link href="https://fonts.googleapis.com/css?family=Roboto|Roboto+Condensed" rel="stylesheet">
diff --git a/WebInterface/NodeJSServer/dist/script/about.js b/WebInterface/NodeJSServer/dist/script/about.js
index ce7e1e2..dea88f8 100644
--- a/WebInterface/NodeJSServer/dist/script/about.js
+++ b/WebInterface/NodeJSServer/dist/script/about.js
@@ -1 +1 @@
-!function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="../script/",n(n.s=0)}([function(t,e,n){"use strict";n.r(e);new class{constructor(t,e,n){this.ids={backdropMenu:t,frontLayer:e,menuButton:n},this.backdrop=document.getElementById(t),this.frontLayer=document.getElementById(e),this.menuButton=document.getElementById(n)}register(){this.registerButtonEvent(),this.registerFrontLayerEvent()}refresh(){this.backdrop=document.getElementById(this.ids.backdropMenu),this.frontLayer=document.getElementById(this.ids.frontLayer),this.menuButton=document.getElementById(this.ids.menuButton),this.register()}registerButtonEvent(){this.menuButton.addEventListener("click",()=>{this.backdrop.classList.contains("hidden")?this.backdrop.classList.remove("hidden"):this.backdrop.classList.add("hidden"),this.menuButton.classList.contains("open")?this.menuButton.classList.remove("open"):this.menuButton.classList.add("open")})}registerFrontLayerEvent(){this.frontLayer.addEventListener("click",()=>{this.backdrop.classList.add("hidden"),this.menuButton.classList.remove("open")})}}("menu","front-layer","show-menu").register()}]); \ No newline at end of file
+!function(t){var e={};function n(i){if(e[i])return e[i].exports;var s=e[i]={i:i,l:!1,exports:{}};return t[i].call(s.exports,s,s.exports,n),s.l=!0,s.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var s in t)n.d(i,s,function(e){return t[e]}.bind(null,s));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="../script/",n(n.s=0)}([function(t,e,n){"use strict";n.r(e);class i{constructor(t,e){this.object=t,this.publicMethods=e}execute(t,...e){return this.publicMethods.includes(t)?"function"!=typeof this.object[t]?2:(this.object[t](...e),0):1}}class s{constructor(t,e,n){this.ids={backdropMenu:t,frontLayer:e,menuButton:n}}initialize(){this.open=!1,this.backdrop=document.getElementById(this.ids.backdropMenu),this.frontLayer=document.getElementById(this.ids.frontLayer),this.menuButton=document.getElementById(this.ids.menuButton),this.registerEvents()}registerEvents(){this.registerButtonEvent(),this.registerFrontLayerEvent()}registerButtonEvent(){this.menuButton.addEventListener("click",()=>{this.open=!this.open,this.open?this.backdrop.classList.remove("hidden"):this.backdrop.classList.add("hidden"),this.open?this.menuButton.classList.add("open"):this.menuButton.classList.remove("open")})}registerFrontLayerEvent(){this.frontLayer.addEventListener("click",()=>{this.open&&(this.open=!1,this.backdrop.classList.add("hidden"),this.menuButton.classList.remove("open"))})}}class r{constructor(){this.backdrop=new s("menu","front-layer","show-menu"),this.backdrop.initialize()}}class o{constructor(t,e,n){this.name=t,this.content=e,this.html=n}}class a{constructor(t,e,n,i,s){t.addObject(this,"notifications",["show","hide"]),this.iface=t,this.ids={bannerId:e,textP:n,dismissBtn:i,badge:s}}initialize(){this.banner=document.getElementById(this.ids.bannerId),this.bannerText=document.getElementById(this.ids.textP),this.dismissBtn=document.getElementById(this.ids.dismissBtn),this.notificationBadge=document.getElementById(this.ids.badge),this.bannerMsgs=[],this.banner.classList.add("hidden"),this.registerEvents()}registerEvents(){this.registerDismissEvent()}registerDismissEvent(){this.dismissBtn.addEventListener("click",()=>{this.dismissCurrent()})}show(t,e){let n=new o(t,e,!1);this.bannerMsgs.push(n),this.update()}hide(t){this.bannerMsgs=t?this.bannerMsgs.filter(e=>e.name!=t):[],this.update()}dismissCurrent(){this.hide(this.current)}update(){if(0===this.bannerMsgs.length)return void this.banner.classList.add("hidden");const t=this.bannerMsgs[this.bannerMsgs.length-1],e=t.name,n=t.content,i=t.html;this.banner.classList.remove("hidden"),i?this.bannerText.innerHTML=n:this.bannerText.innerText=n,this.current=e,this.updateNotificationBadge()}updateNotificationBadge(){this.bannerMsgs.length<2?this.notificationBadge.classList.add("hidden"):this.bannerMsgs.length>9?(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent="∞"):(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent=this.bannerMsgs.length.toString())}}class c{constructor(t,e,n){this.ids={serverListId:e,refreshBtnId:n},t.addObject(this,"serverListing",["flushElements","addElements"]),this.iface=t}initialize(){this.serverListing=document.getElementById(this.ids.serverListId),this.refreshBtn=document.getElementById(this.ids.refreshBtnId),this.registerEvents()}registerEvents(){this.registerRefreshEvent()}registerRefreshEvent(){this.refreshBtn.addEventListener("click",()=>{this.iface.callMethod("listServers","listServers")})}flushElements(){this.serverListing.innerHTML=""}addElements(t){for(let e of t){const t=e.name,n=e.userCount;let i=document.createElement("div"),s=document.createElement("span"),r=document.createElement("div"),o=document.createElement("div"),a=document.createElement("span"),c=document.createElement("span"),h=document.createElement("button");i.className="server",s.className="server-name",r.className="right-aligned-items",o.className="player-count-dot",a.className="player-count",c.className="player-count-static",h.className="btn join-btn",h.id="join",s.textContent=t,a.textContent=n,c.textContent="Spieler online",h.textContent="Beitreten",h.addEventListener("click",()=>{this.iface.callMethod("login","showLogin",t)}),r.appendChild(o),r.appendChild(a),r.appendChild(c),r.appendChild(h),i.appendChild(s),i.appendChild(r),this.serverListing.appendChild(i)}}}class h{constructor(t){this.backdrop=new s("menu","front-layer","show-menu"),this.bannerController=new a(t,"notifications","banner-info","dismiss-banner","notification-amount"),this.serverListing=new c(t,"server-list","refresh-button"),this.backdrop.initialize(),this.bannerController.initialize(),this.serverListing.initialize()}}class d{constructor(){this.backdrop=new s("menu","front-layer","show-menu"),this.bannerController=new a(iface,"notifications","banner-info","dismiss-banner","notification-amount"),this.backdrop.initialize(),this.bannerController.initialize()}}new class{constructor(t){this.currentUI=null,t.addObject(this,"uiMananger",["initAbout","initLogin","initPlay"]),this.iface=t}initAbout(){this.clearComponents(),this.about=new r(this.iface),this.currentUI="about"}initLogin(){this.clearComponents(),this.login=new h(this.iface),this.currentUI="login"}initPlay(){this.clearComponents(),this.play=new d(this.iface),this.currentUI="play"}clearComponents(){switch(this.currentUI){case null:return;case"about":this.about=null;break;case"login":this.login=null;break;case"play":this.play=null}this.currentUI=null}}(new class{constructor(){this.objects={}}addObject(t,e,n){this.objects[e]||(this.objects[e]=[]),this.objects[e].push(new i(t,n))}removeObject(t,e){this.objects[e]&&(objects[e]=objects[e].filter(e=>e.object!=t),0==objects[e].length&&(objects[e]=void 0))}callMethod(t,e,...n){if(!this.objects[t])return 1;let i=0;for(let s of this.objects[t])0!=s.execute(e,...n)&&(i=2);return i}}).initAbout()}]); \ No newline at end of file
diff --git a/WebInterface/NodeJSServer/dist/script/index.js b/WebInterface/NodeJSServer/dist/script/index.js
index 86eb6b6..444da88 100644
--- a/WebInterface/NodeJSServer/dist/script/index.js
+++ b/WebInterface/NodeJSServer/dist/script/index.js
@@ -1,4 +1,4 @@
-!function(t){function e(e){for(var n,r,s=e[0],i=e[1],a=0,l=[];a<s.length;a++)r=s[a],o[r]&&l.push(o[r][0]),o[r]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(t[n]=i[n]);for(c&&c(e);l.length;)l.shift()()}var n={},o={0:0};function r(e){if(n[e])return n[e].exports;var o=n[e]={i:e,l:!1,exports:{}};return t[e].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.e=function(t){var e=[],n=o[t];if(0!==n)if(n)e.push(n[2]);else{var s=new Promise(function(e,r){n=o[t]=[e,r]});e.push(n[2]=s);var i,a=document.getElementsByTagName("head")[0],c=document.createElement("script");c.charset="utf-8",c.timeout=120,r.nc&&c.setAttribute("nonce",r.nc),c.src=function(t){return r.p+""+({1:"/playModule"}[t]||t)+".js"}(t),i=function(e){c.onerror=c.onload=null,clearTimeout(l);var n=o[t];if(0!==n){if(n){var r=e&&("load"===e.type?"missing":e.type),s=e&&e.target&&e.target.src,i=new Error("Loading chunk "+t+" failed.\n("+r+": "+s+")");i.type=r,i.request=s,n[1](i)}o[t]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:c})},12e4);c.onerror=c.onload=i,a.appendChild(c)}return Promise.all(e)},r.m=t,r.c=n,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="./script/",r.oe=function(t){throw console.error(t),t};var s=window.webpackJsonp=window.webpackJsonp||[],i=s.push.bind(s);s.push=e,s=s.slice();for(var a=0;a<s.length;a++)e(s[a]);var c=i;r(r.s=1)}([function(t,e){String.prototype.getHash=async function(){let t=new ArrayBuffer(2*this.length),e=new Uint16Array(t);for(let t=0;t<this.length;t++)e[t]=this.charCodeAt(t);let n=await crypto.subtle.digest("SHA-256",e),o=new Uint8Array(n),r="";for(let t of o)r+=String.fromCharCode(t);return btoa(r)}},function(t,e,n){"use strict";n.r(e);
+!function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="./script/",n(n.s=1)}([function(t,e){String.prototype.getHash=async function(){let t=new ArrayBuffer(2*this.length),e=new Uint16Array(t);for(let t=0;t<this.length;t++)e[t]=this.charCodeAt(t);let n=await crypto.subtle.digest("SHA-256",e),o=new Uint8Array(n),r="";for(let t of o)r+=String.fromCharCode(t);return btoa(r)}},function(t,e,n){"use strict";n.r(e);class o{constructor(t,e){this.object=t,this.publicMethods=e}execute(t,...e){return this.publicMethods.includes(t)?"function"!=typeof this.object[t]?2:(this.object[t](...e),0):1}}class r{constructor(t,e,n){this.ids={backdropMenu:t,frontLayer:e,menuButton:n}}initialize(){this.open=!1,this.backdrop=document.getElementById(this.ids.backdropMenu),this.frontLayer=document.getElementById(this.ids.frontLayer),this.menuButton=document.getElementById(this.ids.menuButton),this.registerEvents()}registerEvents(){this.registerButtonEvent(),this.registerFrontLayerEvent()}registerButtonEvent(){this.menuButton.addEventListener("click",()=>{this.open=!this.open,this.open?this.backdrop.classList.remove("hidden"):this.backdrop.classList.add("hidden"),this.open?this.menuButton.classList.add("open"):this.menuButton.classList.remove("open")})}registerFrontLayerEvent(){this.frontLayer.addEventListener("click",()=>{this.open&&(this.open=!1,this.backdrop.classList.add("hidden"),this.menuButton.classList.remove("open"))})}}class s{constructor(){this.backdrop=new r("menu","front-layer","show-menu"),this.backdrop.initialize()}}class i{constructor(t,e,n){this.name=t,this.content=e,this.html=n}}class a{constructor(t,e,n,o,r){t.addObject(this,"notifications",["show","hide"]),this.iface=t,this.ids={bannerId:e,textP:n,dismissBtn:o,badge:r}}initialize(){this.banner=document.getElementById(this.ids.bannerId),this.bannerText=document.getElementById(this.ids.textP),this.dismissBtn=document.getElementById(this.ids.dismissBtn),this.notificationBadge=document.getElementById(this.ids.badge),this.bannerMsgs=[],this.banner.classList.add("hidden"),this.registerEvents()}registerEvents(){this.registerDismissEvent()}registerDismissEvent(){this.dismissBtn.addEventListener("click",()=>{this.dismissCurrent()})}show(t,e){let n=new i(t,e,!1);this.bannerMsgs.push(n),this.update()}hide(t){this.bannerMsgs=t?this.bannerMsgs.filter(e=>e.name!=t):[],this.update()}dismissCurrent(){this.hide(this.current)}update(){if(0===this.bannerMsgs.length)return void this.banner.classList.add("hidden");const t=this.bannerMsgs[this.bannerMsgs.length-1],e=t.name,n=t.content,o=t.html;this.banner.classList.remove("hidden"),o?this.bannerText.innerHTML=n:this.bannerText.innerText=n,this.current=e,this.updateNotificationBadge()}updateNotificationBadge(){this.bannerMsgs.length<2?this.notificationBadge.classList.add("hidden"):this.bannerMsgs.length>9?(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent="∞"):(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent=this.bannerMsgs.length.toString())}}class c{constructor(t,e,n){this.ids={serverListId:e,refreshBtnId:n},t.addObject(this,"serverListing",["flushElements","addElements"]),this.iface=t}initialize(){this.serverListing=document.getElementById(this.ids.serverListId),this.refreshBtn=document.getElementById(this.ids.refreshBtnId),this.registerEvents()}registerEvents(){this.registerRefreshEvent()}registerRefreshEvent(){this.refreshBtn.addEventListener("click",()=>{this.iface.callMethod("listServers","listServers")})}flushElements(){this.serverListing.innerHTML=""}addElements(t){for(let e of t){const t=e.name,n=e.userCount;let o=document.createElement("div"),r=document.createElement("span"),s=document.createElement("div"),i=document.createElement("div"),a=document.createElement("span"),c=document.createElement("span"),l=document.createElement("button");o.className="server",r.className="server-name",s.className="right-aligned-items",i.className="player-count-dot",a.className="player-count",c.className="player-count-static",l.className="btn join-btn",l.id="join",r.textContent=t,a.textContent=n,c.textContent="Spieler online",l.textContent="Beitreten",l.addEventListener("click",()=>{this.iface.callMethod("login","showLogin",t)}),s.appendChild(i),s.appendChild(a),s.appendChild(c),s.appendChild(l),o.appendChild(r),o.appendChild(s),this.serverListing.appendChild(o)}}}class l{constructor(t){this.backdrop=new r("menu","front-layer","show-menu"),this.bannerController=new a(t,"notifications","banner-info","dismiss-banner","notification-amount"),this.serverListing=new c(t,"server-list","refresh-button"),this.backdrop.initialize(),this.bannerController.initialize(),this.serverListing.initialize()}}class h{constructor(){this.backdrop=new r("menu","front-layer","show-menu"),this.bannerController=new a(iface,"notifications","banner-info","dismiss-banner","notification-amount"),this.backdrop.initialize(),this.bannerController.initialize()}}
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
@@ -13,4 +13,4 @@ MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
-var o=function(t,e){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(t,e)};function r(t,e){function n(){this.constructor=t}o(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)}var s=function(){return(s=Object.assign||function(t){for(var e,n=1,o=arguments.length;n<o;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t}).apply(this,arguments)};function i(t,e,n,o){return new(n||(n=Promise))(function(r,s){function i(t){try{c(o.next(t))}catch(t){s(t)}}function a(t){try{c(o.throw(t))}catch(t){s(t)}}function c(t){t.done?r(t.value):new n(function(e){e(t.value)}).then(i,a)}c((o=o.apply(t,e||[])).next())})}function a(t,e){var n,o,r,s,i={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,o&&(r=2&s[0]?o.return:s[0]?o.throw||((r=o.return)&&r.call(o),0):o.next)&&!(r=r.call(o,s[1])).done)return r;switch(o=0,r&&(s=[2&s[0],r.value]),s[0]){case 0:case 1:r=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,o=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!(r=(r=i.trys).length>0&&r[r.length-1])&&(6===s[0]||2===s[0])){i=0;continue}if(3===s[0]&&(!r||s[1]>r[0]&&s[1]<r[3])){i.label=s[1];break}if(6===s[0]&&i.label<r[1]){i.label=r[1],r=s;break}if(r&&i.label<r[2]){i.label=r[2],i.ops.push(s);break}r[2]&&i.ops.pop(),i.trys.pop();continue}s=e.call(t,i)}catch(t){s=[6,t],o=0}finally{n=r=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}([s,a])}}}var c,l=function(t){function e(e,n){var o=this,r=this.constructor.prototype;return(o=t.call(this,e)||this).statusCode=n,o.__proto__=r,o}return r(e,t),e}(Error),u=function(t){function e(e){var n=this.constructor;void 0===e&&(e="A timeout occurred.");var o=this,r=n.prototype;return(o=t.call(this,e)||this).__proto__=r,o}return r(e,t),e}(Error);!function(t){t[t.Trace=0]="Trace",t[t.Debug=1]="Debug",t[t.Information=2]="Information",t[t.Warning=3]="Warning",t[t.Error=4]="Error",t[t.Critical=5]="Critical",t[t.None=6]="None"}(c||(c={}));var h,d=function(){return function(t,e,n){this.statusCode=t,this.statusText=e,this.content=n}}(),p=function(t){function e(e){var n=t.call(this)||this;return n.logger=e,n}return r(e,t),e.prototype.send=function(t){var e=this;return new Promise(function(n,o){var r=new XMLHttpRequest;r.open(t.method,t.url,!0),r.withCredentials=!0,r.setRequestHeader("X-Requested-With","XMLHttpRequest"),r.setRequestHeader("Content-Type","text/plain;charset=UTF-8"),t.headers&&Object.keys(t.headers).forEach(function(e){return r.setRequestHeader(e,t.headers[e])}),t.responseType&&(r.responseType=t.responseType),t.abortSignal&&(t.abortSignal.onabort=function(){r.abort()}),t.timeout&&(r.timeout=t.timeout),r.onload=function(){t.abortSignal&&(t.abortSignal.onabort=null),r.status>=200&&r.status<300?n(new d(r.status,r.statusText,r.response||r.responseText)):o(new l(r.statusText,r.status))},r.onerror=function(){e.logger.log(c.Warning,"Error from HTTP request. "+r.status+": "+r.statusText),o(new l(r.statusText,r.status))},r.ontimeout=function(){e.logger.log(c.Warning,"Timeout from HTTP request."),o(new u)},r.send(t.content||"")})},e}(function(){function t(){}return t.prototype.get=function(t,e){return this.send(s({},e,{method:"GET",url:t}))},t.prototype.post=function(t,e){return this.send(s({},e,{method:"POST",url:t}))},t.prototype.delete=function(t,e){return this.send(s({},e,{method:"DELETE",url:t}))},t}()),g=function(){function t(){}return t.write=function(e){return""+e+t.RecordSeparator},t.parse=function(e){if(e[e.length-1]!==t.RecordSeparator)throw new Error("Message is incomplete.");var n=e.split(t.RecordSeparator);return n.pop(),n},t.RecordSeparatorCode=30,t.RecordSeparator=String.fromCharCode(t.RecordSeparatorCode),t}(),f=function(){function t(){}return t.prototype.writeHandshakeRequest=function(t){return g.write(JSON.stringify(t))},t.prototype.parseHandshakeResponse=function(t){var e,n;if(t instanceof ArrayBuffer){var o=new Uint8Array(t);if(-1===(s=o.indexOf(g.RecordSeparatorCode)))throw new Error("Message is incomplete.");var r=s+1;e=String.fromCharCode.apply(null,o.slice(0,r)),n=o.byteLength>r?o.slice(r).buffer:null}else{var s,i=t;if(-1===(s=i.indexOf(g.RecordSeparator)))throw new Error("Message is incomplete.");r=s+1;e=i.substring(0,r),n=i.length>r?i.substring(r):null}var a=g.parse(e);return[n,JSON.parse(a[0])]},t}();!function(t){t[t.Invocation=1]="Invocation",t[t.StreamItem=2]="StreamItem",t[t.Completion=3]="Completion",t[t.StreamInvocation=4]="StreamInvocation",t[t.CancelInvocation=5]="CancelInvocation",t[t.Ping=6]="Ping",t[t.Close=7]="Close"}(h||(h={}));var m=function(){function t(){}return t.prototype.log=function(t,e){},t.instance=new t,t}(),v=function(){function t(){}return t.isRequired=function(t,e){if(null===t||void 0===t)throw new Error("The '"+e+"' argument is required.")},t.isIn=function(t,e,n){if(!(t in e))throw new Error("Unknown "+n+" value: "+t+".")},t}();function b(t,e){var n=null;return t instanceof ArrayBuffer?(n="Binary data of length "+t.byteLength,e&&(n+=". Content: '"+function(t){var e="";return new Uint8Array(t).forEach(function(t){e+="0x"+(t<16?"0":"")+t.toString(16)+" "}),e.substr(0,e.length-1)}(t)+"'")):"string"==typeof t&&(n="String data of length "+t.length,e&&(n+=". Content: '"+t+"'.")),n}function y(t,e,n,o,r,s,l){return i(this,void 0,void 0,function(){var i,u,h,d;return a(this,function(a){switch(a.label){case 0:return[4,r()];case 1:return(u=a.sent())&&((d={}).Authorization="Bearer "+u,i=d),t.log(c.Trace,"("+e+" transport) sending data. "+b(s,l)+"."),[4,n.post(o,{content:s,headers:i})];case 2:return h=a.sent(),t.log(c.Trace,"("+e+" transport) request complete. Response status: "+h.statusCode+"."),[2]}})})}var w,E,C=function(){function t(t){this.observers=[],this.cancelCallback=t}return t.prototype.next=function(t){for(var e=0,n=this.observers;e<n.length;e++){n[e].next(t)}},t.prototype.error=function(t){for(var e=0,n=this.observers;e<n.length;e++){var o=n[e];o.error&&o.error(t)}},t.prototype.complete=function(){for(var t=0,e=this.observers;t<e.length;t++){var n=e[t];n.complete&&n.complete()}},t.prototype.subscribe=function(t){return this.observers.push(t),new S(this,t)},t}(),S=function(){function t(t,e){this.subject=t,this.observer=e}return t.prototype.dispose=function(){var t=this.subject.observers.indexOf(this.observer);t>-1&&this.subject.observers.splice(t,1),0===this.subject.observers.length&&this.subject.cancelCallback().catch(function(t){})},t}(),k=function(){function t(t){this.minimumLogLevel=t}return t.prototype.log=function(t,e){if(t>=this.minimumLogLevel)switch(t){case c.Critical:case c.Error:console.error(c[t]+": "+e);break;case c.Warning:console.warn(c[t]+": "+e);break;case c.Information:console.info(c[t]+": "+e);break;default:console.log(c[t]+": "+e)}},t}(),T=3e4,I=function(){function t(t,e,n){var o=this;v.isRequired(t,"connection"),v.isRequired(e,"logger"),v.isRequired(n,"protocol"),this.serverTimeoutInMilliseconds=T,this.logger=e,this.protocol=n,this.connection=t,this.handshakeProtocol=new f,this.connection.onreceive=function(t){return o.processIncomingData(t)},this.connection.onclose=function(t){return o.connectionClosed(t)},this.callbacks={},this.methods={},this.closedCallbacks=[],this.id=0}return t.create=function(e,n,o){return new t(e,n,o)},t.prototype.start=function(){return i(this,void 0,void 0,function(){var t;return a(this,function(e){switch(e.label){case 0:return t={protocol:this.protocol.name,version:this.protocol.version},this.logger.log(c.Debug,"Starting HubConnection."),this.receivedHandshakeResponse=!1,[4,this.connection.start(this.protocol.transferFormat)];case 1:return e.sent(),this.logger.log(c.Debug,"Sending handshake request."),[4,this.connection.send(this.handshakeProtocol.writeHandshakeRequest(t))];case 2:return e.sent(),this.logger.log(c.Information,"Using HubProtocol '"+this.protocol.name+"'."),this.cleanupTimeout(),this.configureTimeout(),[2]}})})},t.prototype.stop=function(){return this.logger.log(c.Debug,"Stopping HubConnection."),this.cleanupTimeout(),this.connection.stop()},t.prototype.stream=function(t){for(var e=this,n=[],o=1;o<arguments.length;o++)n[o-1]=arguments[o];var r=this.createStreamInvocation(t,n),s=new C(function(){var t=e.createCancelInvocation(r.invocationId),n=e.protocol.writeMessage(t);return delete e.callbacks[r.invocationId],e.connection.send(n)});this.callbacks[r.invocationId]=function(t,e){e?s.error(e):t.type===h.Completion?t.error?s.error(new Error(t.error)):s.complete():s.next(t.item)};var i=this.protocol.writeMessage(r);return this.connection.send(i).catch(function(t){s.error(t),delete e.callbacks[r.invocationId]}),s},t.prototype.send=function(t){for(var e=[],n=1;n<arguments.length;n++)e[n-1]=arguments[n];var o=this.createInvocation(t,e,!0),r=this.protocol.writeMessage(o);return this.connection.send(r)},t.prototype.invoke=function(t){for(var e=this,n=[],o=1;o<arguments.length;o++)n[o-1]=arguments[o];var r=this.createInvocation(t,n,!1);return new Promise(function(t,n){e.callbacks[r.invocationId]=function(e,o){if(o)n(o);else if(e.type===h.Completion){var r=e;r.error?n(new Error(r.error)):t(r.result)}else n(new Error("Unexpected message type: "+e.type))};var o=e.protocol.writeMessage(r);e.connection.send(o).catch(function(t){n(t),delete e.callbacks[r.invocationId]})})},t.prototype.on=function(t,e){t&&e&&(t=t.toLowerCase(),this.methods[t]||(this.methods[t]=[]),-1===this.methods[t].indexOf(e)&&this.methods[t].push(e))},t.prototype.off=function(t,e){if(t){t=t.toLowerCase();var n=this.methods[t];if(n)if(e){var o=n.indexOf(e);-1!==o&&(n.splice(o,1),0===n.length&&delete this.methods[t])}else delete this.methods[t]}},t.prototype.onclose=function(t){t&&this.closedCallbacks.push(t)},t.prototype.processIncomingData=function(t){if(this.cleanupTimeout(),this.receivedHandshakeResponse||(t=this.processHandshakeResponse(t),this.receivedHandshakeResponse=!0),t)for(var e=0,n=this.protocol.parseMessages(t,this.logger);e<n.length;e++){var o=n[e];switch(o.type){case h.Invocation:this.invokeClientMethod(o);break;case h.StreamItem:case h.Completion:var r=this.callbacks[o.invocationId];null!=r&&(o.type===h.Completion&&delete this.callbacks[o.invocationId],r(o));break;case h.Ping:break;case h.Close:this.logger.log(c.Information,"Close message received from server."),this.connection.stop(o.error?new Error("Server returned an error on close: "+o.error):null);break;default:this.logger.log(c.Warning,"Invalid message type: "+o.type)}}this.configureTimeout()},t.prototype.processHandshakeResponse=function(t){var e,n,o;try{n=(o=this.handshakeProtocol.parseHandshakeResponse(t))[0],e=o[1]}catch(t){var r="Error parsing handshake response: "+t;this.logger.log(c.Error,r);var s=new Error(r);throw this.connection.stop(s),s}if(e.error){r="Server returned handshake error: "+e.error;this.logger.log(c.Error,r),this.connection.stop(new Error(r))}else this.logger.log(c.Debug,"Server handshake complete.");return n},t.prototype.configureTimeout=function(){var t=this;this.connection.features&&this.connection.features.inherentKeepAlive||(this.timeoutHandle=setTimeout(function(){return t.serverTimeout()},this.serverTimeoutInMilliseconds))},t.prototype.serverTimeout=function(){this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server."))},t.prototype.invokeClientMethod=function(t){var e=this,n=this.methods[t.target.toLowerCase()];if(n){if(n.forEach(function(n){return n.apply(e,t.arguments)}),t.invocationId){var o="Server requested a response, which is not supported in this version of the client.";this.logger.log(c.Error,o),this.connection.stop(new Error(o))}}else this.logger.log(c.Warning,"No client method with the name '"+t.target+"' found.")},t.prototype.connectionClosed=function(t){var e=this,n=this.callbacks;this.callbacks={},Object.keys(n).forEach(function(e){(0,n[e])(void 0,t||new Error("Invocation canceled due to connection being closed."))}),this.cleanupTimeout(),this.closedCallbacks.forEach(function(n){return n.apply(e,[t])})},t.prototype.cleanupTimeout=function(){this.timeoutHandle&&clearTimeout(this.timeoutHandle)},t.prototype.createInvocation=function(t,e,n){if(n)return{arguments:e,target:t,type:h.Invocation};var o=this.id;return this.id++,{arguments:e,invocationId:o.toString(),target:t,type:h.Invocation}},t.prototype.createStreamInvocation=function(t,e){var n=this.id;return this.id++,{arguments:e,invocationId:n.toString(),target:t,type:h.StreamInvocation}},t.prototype.createCancelInvocation=function(t){return{invocationId:t,type:h.CancelInvocation}},t}();!function(t){t[t.None=0]="None",t[t.WebSockets=1]="WebSockets",t[t.ServerSentEvents=2]="ServerSentEvents",t[t.LongPolling=4]="LongPolling"}(w||(w={})),function(t){t[t.Text=1]="Text",t[t.Binary=2]="Binary"}(E||(E={}));var L=function(){function t(){this.isAborted=!1}return t.prototype.abort=function(){this.isAborted||(this.isAborted=!0,this.onabort&&this.onabort())},Object.defineProperty(t.prototype,"signal",{get:function(){return this},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"aborted",{get:function(){return this.isAborted},enumerable:!0,configurable:!0}),t}(),P=5e3,x=function(){function t(t,e,n,o,r){this.httpClient=t,this.accessTokenFactory=e||function(){return null},this.logger=n,this.pollAbort=new L,this.logMessageContent=o,this.shutdownTimeout=r||P}return Object.defineProperty(t.prototype,"pollAborted",{get:function(){return this.pollAbort.aborted},enumerable:!0,configurable:!0}),t.prototype.connect=function(t,e){return i(this,void 0,void 0,function(){var n,o,r,s,i;return a(this,function(a){switch(a.label){case 0:if(v.isRequired(t,"url"),v.isRequired(e,"transferFormat"),v.isIn(e,E,"transferFormat"),this.url=t,this.logger.log(c.Trace,"(LongPolling transport) Connecting"),e===E.Binary&&"string"!=typeof(new XMLHttpRequest).responseType)throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");return n={abortSignal:this.pollAbort.signal,headers:{},timeout:9e4},e===E.Binary&&(n.responseType="arraybuffer"),[4,this.accessTokenFactory()];case 1:return o=a.sent(),this.updateHeaderToken(n,o),s=t+"&_="+Date.now(),this.logger.log(c.Trace,"(LongPolling transport) polling: "+s),[4,this.httpClient.get(s,n)];case 2:return 200!==(i=a.sent()).statusCode?(this.logger.log(c.Error,"(LongPolling transport) Unexpected response code: "+i.statusCode),r=new l(i.statusText,i.statusCode),this.running=!1):this.running=!0,this.poll(this.url,n,r),[2,Promise.resolve()]}})})},t.prototype.updateHeaderToken=function(t,e){e?t.headers.Authorization="Bearer "+e:t.headers.Authorization&&delete t.headers.Authorization},t.prototype.poll=function(t,e,n){return i(this,void 0,void 0,function(){var o,r,s,i;return a(this,function(a){switch(a.label){case 0:a.trys.push([0,,8,9]),a.label=1;case 1:return this.running?[4,this.accessTokenFactory()]:[3,7];case 2:o=a.sent(),this.updateHeaderToken(e,o),a.label=3;case 3:return a.trys.push([3,5,,6]),r=t+"&_="+Date.now(),this.logger.log(c.Trace,"(LongPolling transport) polling: "+r),[4,this.httpClient.get(r,e)];case 4:return 204===(s=a.sent()).statusCode?(this.logger.log(c.Information,"(LongPolling transport) Poll terminated by server"),this.running=!1):200!==s.statusCode?(this.logger.log(c.Error,"(LongPolling transport) Unexpected response code: "+s.statusCode),n=new l(s.statusText,s.statusCode),this.running=!1):s.content?(this.logger.log(c.Trace,"(LongPolling transport) data received. "+b(s.content,this.logMessageContent)),this.onreceive&&this.onreceive(s.content)):this.logger.log(c.Trace,"(LongPolling transport) Poll timed out, reissuing."),[3,6];case 5:return i=a.sent(),this.running?i instanceof u?this.logger.log(c.Trace,"(LongPolling transport) Poll timed out, reissuing."):(n=i,this.running=!1):this.logger.log(c.Trace,"(LongPolling transport) Poll errored after shutdown: "+i.message),[3,6];case 6:return[3,1];case 7:return[3,9];case 8:return this.stopped=!0,this.shutdownTimer&&clearTimeout(this.shutdownTimer),this.onclose&&(this.logger.log(c.Trace,"(LongPolling transport) Firing onclose event. Error: "+(n||"<undefined>")),this.onclose(n)),this.logger.log(c.Trace,"(LongPolling transport) Transport finished."),[7];case 9:return[2]}})})},t.prototype.send=function(t){return i(this,void 0,void 0,function(){return a(this,function(e){return this.running?[2,y(this.logger,"LongPolling",this.httpClient,this.url,this.accessTokenFactory,t,this.logMessageContent)]:[2,Promise.reject(new Error("Cannot send until the transport is connected"))]})})},t.prototype.stop=function(){return i(this,void 0,void 0,function(){var t,e,n=this;return a(this,function(o){switch(o.label){case 0:return o.trys.push([0,,3,4]),this.running=!1,this.logger.log(c.Trace,"(LongPolling transport) sending DELETE request to "+this.url+"."),t={headers:{}},[4,this.accessTokenFactory()];case 1:return e=o.sent(),this.updateHeaderToken(t,e),[4,this.httpClient.delete(this.url,t)];case 2:return o.sent(),this.logger.log(c.Trace,"(LongPolling transport) DELETE request accepted."),[3,4];case 3:return this.stopped||(this.shutdownTimer=setTimeout(function(){n.logger.log(c.Warning,"(LongPolling transport) server did not terminate after DELETE request, canceling poll."),n.pollAbort.abort()},this.shutdownTimeout)),[7];case 4:return[2]}})})},t}(),B=function(){function t(t,e,n,o){this.httpClient=t,this.accessTokenFactory=e||function(){return null},this.logger=n,this.logMessageContent=o}return t.prototype.connect=function(t,e){return i(this,void 0,void 0,function(){var n,o=this;return a(this,function(r){switch(r.label){case 0:if(v.isRequired(t,"url"),v.isRequired(e,"transferFormat"),v.isIn(e,E,"transferFormat"),"undefined"==typeof EventSource)throw new Error("'EventSource' is not supported in your environment.");return this.logger.log(c.Trace,"(SSE transport) Connecting"),[4,this.accessTokenFactory()];case 1:return(n=r.sent())&&(t+=(t.indexOf("?")<0?"?":"&")+"access_token="+encodeURIComponent(n)),this.url=t,[2,new Promise(function(n,r){var s=!1;e!==E.Text&&r(new Error("The Server-Sent Events transport only supports the 'Text' transfer format"));var i=new EventSource(t,{withCredentials:!0});try{i.onmessage=function(t){if(o.onreceive)try{o.logger.log(c.Trace,"(SSE transport) data received. "+b(t.data,o.logMessageContent)+"."),o.onreceive(t.data)}catch(t){return void(o.onclose&&o.onclose(t))}},i.onerror=function(t){var e=new Error(t.message||"Error occurred");s?o.close(e):r(e)},i.onopen=function(){o.logger.log(c.Information,"SSE connected to "+o.url),o.eventSource=i,s=!0,n()}}catch(t){return Promise.reject(t)}})]}})})},t.prototype.send=function(t){return i(this,void 0,void 0,function(){return a(this,function(e){return this.eventSource?[2,y(this.logger,"SSE",this.httpClient,this.url,this.accessTokenFactory,t,this.logMessageContent)]:[2,Promise.reject(new Error("Cannot send until the transport is connected"))]})})},t.prototype.stop=function(){return this.close(),Promise.resolve()},t.prototype.close=function(t){this.eventSource&&(this.eventSource.close(),this.eventSource=null,this.onclose&&this.onclose(t))},t}(),M=function(){function t(t,e,n){this.logger=e,this.accessTokenFactory=t||function(){return null},this.logMessageContent=n}return t.prototype.connect=function(t,e){return i(this,void 0,void 0,function(){var n,o=this;return a(this,function(r){switch(r.label){case 0:if(v.isRequired(t,"url"),v.isRequired(e,"transferFormat"),v.isIn(e,E,"transferFormat"),"undefined"==typeof WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return this.logger.log(c.Trace,"(WebSockets transport) Connecting"),[4,this.accessTokenFactory()];case 1:return(n=r.sent())&&(t+=(t.indexOf("?")<0?"?":"&")+"access_token="+encodeURIComponent(n)),[2,new Promise(function(n,r){t=t.replace(/^http/,"ws");var s=new WebSocket(t);e===E.Binary&&(s.binaryType="arraybuffer"),s.onopen=function(e){o.logger.log(c.Information,"WebSocket connected to "+t),o.webSocket=s,n()},s.onerror=function(t){r(t.error)},s.onmessage=function(t){o.logger.log(c.Trace,"(WebSockets transport) data received. "+b(t.data,o.logMessageContent)+"."),o.onreceive&&o.onreceive(t.data)},s.onclose=function(t){o.logger.log(c.Trace,"(WebSockets transport) socket closed."),o.onclose&&(!1===t.wasClean||1e3!==t.code?o.onclose(new Error("Websocket closed with status code: "+t.code+" ("+t.reason+")")):o.onclose())}})]}})})},t.prototype.send=function(t){return this.webSocket&&this.webSocket.readyState===WebSocket.OPEN?(this.logger.log(c.Trace,"(WebSockets transport) sending data. "+b(t,this.logMessageContent)+"."),this.webSocket.send(t),Promise.resolve()):Promise.reject("WebSocket is not in the OPEN state")},t.prototype.stop=function(){return this.webSocket&&(this.webSocket.close(),this.webSocket=null),Promise.resolve()},t}(),N=function(){function t(t,e){void 0===e&&(e={}),this.features={},v.isRequired(t,"url"),this.logger=function(t){return void 0===t?new k(c.Information):null===t?m.instance:t.log?t:new k(t)}(e.logger),this.baseUrl=this.resolveUrl(t),(e=e||{}).accessTokenFactory=e.accessTokenFactory||function(){return null},e.logMessageContent=e.logMessageContent||!1,this.httpClient=e.httpClient||new p(this.logger),this.connectionState=2,this.options=e}return t.prototype.start=function(t){return t=t||E.Binary,v.isIn(t,E,"transferFormat"),this.logger.log(c.Debug,"Starting connection with transfer format '"+E[t]+"'."),2!==this.connectionState?Promise.reject(new Error("Cannot start a connection that is not in the 'Disconnected' state.")):(this.connectionState=0,this.startPromise=this.startInternal(t),this.startPromise)},t.prototype.send=function(t){if(1!==this.connectionState)throw new Error("Cannot send data if the connection is not in the 'Connected' State.");return this.transport.send(t)},t.prototype.stop=function(t){return i(this,void 0,void 0,function(){return a(this,function(e){switch(e.label){case 0:this.connectionState=2,e.label=1;case 1:return e.trys.push([1,3,,4]),[4,this.startPromise];case 2:return e.sent(),[3,4];case 3:return e.sent(),[3,4];case 4:return this.transport?(this.stopError=t,[4,this.transport.stop()]):[3,6];case 5:e.sent(),this.transport=null,e.label=6;case 6:return[2]}})})},t.prototype.startInternal=function(t){return i(this,void 0,void 0,function(){var e,n,o,r,s,i,l,u=this;return a(this,function(h){switch(h.label){case 0:e=this.baseUrl,this.accessTokenFactory=this.options.accessTokenFactory,h.label=1;case 1:return h.trys.push([1,12,,13]),this.options.skipNegotiation?this.options.transport!==w.WebSockets?[3,3]:(this.transport=this.constructTransport(w.WebSockets),[4,this.transport.connect(e,t)]):[3,5];case 2:return h.sent(),[3,4];case 3:throw Error("Negotiation can only be skipped when using the WebSocket transport directly.");case 4:return[3,11];case 5:n=null,o=0,r=function(){var t;return a(this,function(r){switch(r.label){case 0:return[4,s.getNegotiationResponse(e)];case 1:return n=r.sent(),2===s.connectionState?[2,{value:void 0}]:(n.url&&(e=n.url),n.accessToken&&(t=n.accessToken,s.accessTokenFactory=function(){return t}),o++,[2])}})},s=this,h.label=6;case 6:return[5,r()];case 7:if("object"==typeof(i=h.sent()))return[2,i.value];h.label=8;case 8:if(n.url&&o<100)return[3,6];h.label=9;case 9:if(100===o&&n.url)throw Error("Negotiate redirection limit exceeded.");return[4,this.createTransport(e,this.options.transport,n,t)];case 10:h.sent(),h.label=11;case 11:return this.transport instanceof x&&(this.features.inherentKeepAlive=!0),this.transport.onreceive=this.onreceive,this.transport.onclose=function(t){return u.stopConnection(t)},this.changeState(0,1),[3,13];case 12:throw l=h.sent(),this.logger.log(c.Error,"Failed to start the connection: "+l),this.connectionState=2,this.transport=null,l;case 13:return[2]}})})},t.prototype.getNegotiationResponse=function(t){return i(this,void 0,void 0,function(){var e,n,o,r,s,i;return a(this,function(a){switch(a.label){case 0:return[4,this.accessTokenFactory()];case 1:(e=a.sent())&&((i={}).Authorization="Bearer "+e,n=i),o=this.resolveNegotiateUrl(t),this.logger.log(c.Debug,"Sending negotiation request: "+o),a.label=2;case 2:return a.trys.push([2,4,,5]),[4,this.httpClient.post(o,{content:"",headers:n})];case 3:if(200!==(r=a.sent()).statusCode)throw Error("Unexpected status code returned from negotiate "+r.statusCode);return[2,JSON.parse(r.content)];case 4:throw s=a.sent(),this.logger.log(c.Error,"Failed to complete negotiation with the server: "+s),s;case 5:return[2]}})})},t.prototype.createConnectUrl=function(t,e){return t+(-1===t.indexOf("?")?"?":"&")+"id="+e},t.prototype.createTransport=function(t,e,n,o){return i(this,void 0,void 0,function(){var r,s,i,l,u,h,d;return a(this,function(a){switch(a.label){case 0:return r=this.createConnectUrl(t,n.connectionId),this.isITransport(e)?(this.logger.log(c.Debug,"Connection was provided an instance of ITransport, using that directly."),this.transport=e,[4,this.transport.connect(r,o)]):[3,2];case 1:return a.sent(),this.changeState(0,1),[2];case 2:s=n.availableTransports,i=0,l=s,a.label=3;case 3:return i<l.length?(u=l[i],this.connectionState=0,"number"!=typeof(h=this.resolveTransport(u,e,o))?[3,8]:(this.transport=this.constructTransport(h),null!==n.connectionId?[3,5]:[4,this.getNegotiationResponse(t)])):[3,9];case 4:n=a.sent(),r=this.createConnectUrl(t,n.connectionId),a.label=5;case 5:return a.trys.push([5,7,,8]),[4,this.transport.connect(r,o)];case 6:return a.sent(),this.changeState(0,1),[2];case 7:return d=a.sent(),this.logger.log(c.Error,"Failed to start the transport '"+w[h]+"': "+d),this.connectionState=2,n.connectionId=null,[3,8];case 8:return i++,[3,3];case 9:throw new Error("Unable to initialize any of the available transports.")}})})},t.prototype.constructTransport=function(t){switch(t){case w.WebSockets:return new M(this.accessTokenFactory,this.logger,this.options.logMessageContent);case w.ServerSentEvents:return new B(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent);case w.LongPolling:return new x(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent);default:throw new Error("Unknown transport: "+t+".")}},t.prototype.resolveTransport=function(t,e,n){var o=w[t.transport];if(null===o||void 0===o)this.logger.log(c.Debug,"Skipping transport '"+t.transport+"' because it is not supported by this client.");else{var r=t.transferFormats.map(function(t){return E[t]});if(function(t,e){return!t||0!=(e&t)}(e,o))if(r.indexOf(n)>=0){if(!(o===w.WebSockets&&"undefined"==typeof WebSocket||o===w.ServerSentEvents&&"undefined"==typeof EventSource))return this.logger.log(c.Debug,"Selecting transport '"+w[o]+"'"),o;this.logger.log(c.Debug,"Skipping transport '"+w[o]+"' because it is not supported in your environment.'")}else this.logger.log(c.Debug,"Skipping transport '"+w[o]+"' because it does not support the requested transfer format '"+E[n]+"'.");else this.logger.log(c.Debug,"Skipping transport '"+w[o]+"' because it was disabled by the client.")}return null},t.prototype.isITransport=function(t){return t&&"object"==typeof t&&"connect"in t},t.prototype.changeState=function(t,e){return this.connectionState===t&&(this.connectionState=e,!0)},t.prototype.stopConnection=function(t){return i(this,void 0,void 0,function(){return a(this,function(e){return this.transport=null,(t=this.stopError||t)?this.logger.log(c.Error,"Connection disconnected with error '"+t+"'."):this.logger.log(c.Information,"Connection disconnected."),this.connectionState=2,this.onclose&&this.onclose(t),[2]})})},t.prototype.resolveUrl=function(t){if(0===t.lastIndexOf("https://",0)||0===t.lastIndexOf("http://",0))return t;if("undefined"==typeof window||!window||!window.document)throw new Error("Cannot resolve '"+t+"'.");var e=window.document.createElement("a");return e.href=t,this.logger.log(c.Information,"Normalizing '"+t+"' to '"+e.href+"'."),e.href},t.prototype.resolveNegotiateUrl=function(t){var e=t.indexOf("?"),n=t.substring(0,-1===e?t.length:e);return"/"!==n[n.length-1]&&(n+="/"),n+="negotiate",n+=-1===e?"":t.substring(e)},t}();var R="json",O=function(){function t(){this.name=R,this.version=1,this.transferFormat=E.Text}return t.prototype.parseMessages=function(t,e){if("string"!=typeof t)throw new Error("Invalid input for JSON hub protocol. Expected a string.");if(!t)return[];null===e&&(e=m.instance);for(var n=[],o=0,r=g.parse(t);o<r.length;o++){var s=r[o],i=JSON.parse(s);if("number"!=typeof i.type)throw new Error("Invalid payload.");switch(i.type){case h.Invocation:this.isInvocationMessage(i);break;case h.StreamItem:this.isStreamItemMessage(i);break;case h.Completion:this.isCompletionMessage(i);break;case h.Ping:case h.Close:break;default:e.log(c.Information,"Unknown message type '"+i.type+"' ignored.");continue}n.push(i)}return n},t.prototype.writeMessage=function(t){return g.write(JSON.stringify(t))},t.prototype.isInvocationMessage=function(t){this.assertNotEmptyString(t.target,"Invalid payload for Invocation message."),void 0!==t.invocationId&&this.assertNotEmptyString(t.invocationId,"Invalid payload for Invocation message.")},t.prototype.isStreamItemMessage=function(t){if(this.assertNotEmptyString(t.invocationId,"Invalid payload for StreamItem message."),void 0===t.item)throw new Error("Invalid payload for StreamItem message.")},t.prototype.isCompletionMessage=function(t){if(t.result&&t.error)throw new Error("Invalid payload for Completion message.");!t.result&&t.error&&this.assertNotEmptyString(t.error,"Invalid payload for Completion message."),this.assertNotEmptyString(t.invocationId,"Invalid payload for Completion message.")},t.prototype.assertNotEmptyString=function(t,e){if("string"!=typeof t||""===t)throw new Error(e)},t}(),F=function(){function t(){}return t.prototype.configureLogging=function(t){return v.isRequired(t,"logging"),!function(t){return void 0!==t.log}(t)?this.logger=new k(t):this.logger=t,this},t.prototype.withUrl=function(t,e){return v.isRequired(t,"url"),this.url=t,this.httpConnectionOptions="object"==typeof e?e:{transport:e},this},t.prototype.withHubProtocol=function(t){return v.isRequired(t,"protocol"),this.protocol=t,this},t.prototype.build=function(){var t=this.httpConnectionOptions||{};if(void 0===t.logger&&(t.logger=this.logger),!this.url)throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection.");var e=new N(this.url,t);return I.create(e,this.logger||m.instance,this.protocol||new O)},t}();class H{constructor(t){let e=document.createElement("div"),n=document.createElement("div"),o=document.createElement("h1"),r=document.createElement("div");e.className="modal-container",n.className="modal",o.className="modal-title",r.className="modal-body",o.textContent=t,n.appendChild(o),n.appendChild(r),e.appendChild(n),document.body.appendChild(e),this.bg=e,this.modal=n,this.title=o,this.body=r,this.registerEvents()}registerEvents(){this.modal.addEventListener("click",t=>{t.stopPropagation()}),this.bg.addEventListener("click",()=>{this.close()})}close(){this.bg.classList.add("hidden"),this.bg.addEventListener("transitionend",()=>{document.body.removeChild(this.bg)})}setBodyText(t){this.body.textContent=t}}n(0);class q extends H{constructor(t,e,n,o){super(t),this.serverName=t,this.serverClient=e,this.notificationManager=n,this.pageUI=o;let r=document.createElement("div"),s=document.createElement("div"),i=document.createElement("div"),a=document.createElement("label"),c=document.createElement("input"),l=document.createElement("span");a.setAttribute("for","password-input"),a.textContent="Passwort:",a.title="Das Passwort des Spiels",c.id="password-input",c.type="password",c.placeholder="Passwort",c.title="Das Passwort des Spiels",l.className="invalid hidden",l.textContent="Das eingegebene Passwort ist falsch.";let u=document.createElement("label"),h=document.createElement("input"),d=document.createElement("span");u.setAttribute("for","name-input"),u.textContent="Benutzername:",u.title="Dein Anzeigename",h.id="name-input",h.type="text",h.autocomplete="on",h.placeholder="Name",h.title="Dein Anzeigename",d.className="invalid hidden",d.textContent="Der eingegebene Nutzername ist bereits vergeben.";let p=document.createElement("button");p.className="btn",p.textContent="Login",p.id="login-button",r.appendChild(a),r.appendChild(c),r.appendChild(l),s.appendChild(u),s.appendChild(h),s.appendChild(d),i.appendChild(p),this.body.appendChild(r),this.body.appendChild(s),this.body.appendChild(i),this.nameInput=h,this.passwordInput=c,this.loginButton=p,this.passwordInvalid=l,this.nameInvalid=d,this.registerLoginBtn()}registerLoginBtn(){let t,e=(e,n)=>{console.log(e),0==e?(this.redirectToPlay(n),this.close()):1==e?(this.invalid("Name"),this.loginButton.addEventListener("click",t)):2==e?(this.invalid("Pass"),this.loginButton.addEventListener("click",t)):(this.notificationManager.show("unknownLoginErr","Ein unbekannter Fehler ist aufgetreten"),this.close())};t=(()=>{this.invalid(),this.loginButton.removeEventListener("click",t),this.userName=this.nameInput.value,this.passwordInput.value.getHash().then(t=>{this.serverClient.sendLogin(this.serverName,t,this.userName,e)})}),this.loginButton.addEventListener("click",t)}invalid(t){this.body.classList.remove("frst-warning"),this.body.classList.remove("scnd-warning"),this.passwordInvalid.classList.add("hidden"),this.nameInvalid.classList.add("hidden"),this.passwordInput.style.borderColor="none",this.nameInput.style.borderColor="none","Pass"==t?(this.body.classList.add("frst-warning"),this.passwordInvalid.classList.remove("hidden"),this.passwordInput.style.borderColor="#ef5350"):"Name"==t&&(this.body.classList.add("scnd-warning"),this.nameInvalid.classList.remove("hidden"),this.nameInput.style.borderColor="#ef5350")}redirectToPlay(t){window.history.pushState("object or string","Game Page","play#game="+this.serverName),fetch("play").then(e=>{e.text().then(e=>{e=e.replace(/\.\.\//g,"./"),e=(e=(e=/<body>((.)|(\n))*<\/body>/g.exec(e)[0]).replace(/<script src=".*"><\/script>/,"")).replace(/<remove_if_redirected>((.)|\n)*?<\/remove_if_redirected>/g,""),document.body.innerHTML=e;let o=document.createElement("link");o.rel="stylesheet",o.type="text/css",o.href="./style/play.css",document.head.appendChild(o);for(let t of this.pageUI)t.refresh();n.e(1).then(n.bind(null,2)).then(({default:e})=>{new e(this.userName,t).registerChat("chat")})})})}}class j{constructor(t,e){this.serverListing=document.getElementById(t),this.notifications=e}flushElements(){this.serverListing.innerHTML=""}addElements(t,e,n){for(let o of t){const t=o.name,r=o.userCount;let s=document.createElement("div"),i=document.createElement("span"),a=document.createElement("div"),c=document.createElement("div"),l=document.createElement("span"),u=document.createElement("span"),h=document.createElement("button");s.className="server",i.className="server-name",a.className="right-aligned-items",c.className="player-count-dot",l.className="player-count",u.className="player-count-static",h.className="btn join-btn",h.id="join",i.textContent=t,l.textContent=r,u.textContent="Spieler online",h.textContent="Beitreten",h.addEventListener("click",()=>{new q(t,e,this.notifications,n)}),a.appendChild(c),a.appendChild(l),a.appendChild(u),a.appendChild(h),s.appendChild(i),s.appendChild(a),this.serverListing.appendChild(s)}}}let A=new class{constructor(t,e,n){this.ids={backdropMenu:t,frontLayer:e,menuButton:n},this.backdrop=document.getElementById(t),this.frontLayer=document.getElementById(e),this.menuButton=document.getElementById(n)}register(){this.registerButtonEvent(),this.registerFrontLayerEvent()}refresh(){this.backdrop=document.getElementById(this.ids.backdropMenu),this.frontLayer=document.getElementById(this.ids.frontLayer),this.menuButton=document.getElementById(this.ids.menuButton),this.register()}registerButtonEvent(){this.menuButton.addEventListener("click",()=>{this.backdrop.classList.contains("hidden")?this.backdrop.classList.remove("hidden"):this.backdrop.classList.add("hidden"),this.menuButton.classList.contains("open")?this.menuButton.classList.remove("open"):this.menuButton.classList.add("open")})}registerFrontLayerEvent(){this.frontLayer.addEventListener("click",()=>{this.backdrop.classList.add("hidden"),this.menuButton.classList.remove("open")})}}("menu","front-layer","show-menu");A.register();let D=new class{constructor(t,e,n,o){this.ids={bannerId:t,textP:e,dismissBtn:n,notificationBadge:o},this.banner=document.getElementById(t),this.bannerText=document.getElementById(e),this.dismissBtn=document.getElementById(n),this.notificationBadge=document.getElementById(o),this.bannerMsgs=[],this.banner.classList.add("hidden")}register(){this.dismissBtn.addEventListener("click",()=>{this.dismissCurrent()})}refresh(){this.banner=document.getElementById(this.ids.bannerId),this.bannerText=document.getElementById(this.ids.textP),this.dismissBtn=document.getElementById(this.ids.dismissBtn),this.notificationBadge=document.getElementById(this.ids.notificationBadge),this.bannerMsgs=[],this.banner.classList.add("hidden")}show(t,e){let n={name:t,text:e,html:!1};this.bannerMsgs.push(n),this.update()}hide(t){if(t){for(let e=0;e<this.bannerMsgs.length;e++)this.bannerMsgs[e].name==t&&this.bannerMsgs.splice(e,1);this.update()}else this.bannerMsgs=[],this.banner.classList.add("hidden")}dismissCurrent(){this.hide(this.current)}update(){if(0===this.bannerMsgs.length)return void this.banner.classList.add("hidden");const t=this.bannerMsgs[this.bannerMsgs.length-1],e=t.name,n=t.text,o=t.html;this.banner.classList.remove("hidden"),o?this.bannerText.innerHTML=n:this.bannerText.innerText=n,this.current=e,this.bannerMsgs.length<2?this.notificationBadge.classList.add("hidden"):this.bannerMsgs.length>9?(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent="∞"):(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent=this.bannerMsgs.length.toString())}}("notifications","banner-info","dismiss-banner","notification-amount");D.register();let U=new class{constructor(t,e,n,o,r=!1){this.ui=o;const s=(new F).withUrl(t);r?s.configureLogging(c.Debug):s.configureLogging(c.Error),this.connection=s.build(),this.connection.start().then(()=>this.loadServers()).catch(t=>console.error(t.toString())),this.refreshing=!1,this.serverListing=new j(e,n)}loadServers(){this.refreshing||(this.connection.on("ListGroups",t=>{this.serverListing.flushElements(),this.serverListing.addElements(t,this,this.ui),this.connection.off("ListGroups"),this.refreshing=!1}),this.connection.invoke("GetGroups").catch(t=>{this.refreshing=!1,console.error(t.toString())}),this.refreshing=!0)}createServer(t,e){}sendLogin(t,e,n,o){this.connection.on("LoginResponse",t=>{o(t,this.connection),this.connection.off("LoginResponse")}),this.connection.invoke("Login",t,n,e)}}("http://127.0.0.1:5000/chatHub","server-list",D,[A,D],!0);document.getElementById("refresh-button").addEventListener("click",U.loadServers.bind(U)),window.client=U}]); \ No newline at end of file
+var u=function(t,e){return(u=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(t,e)};function d(t,e){function n(){this.constructor=t}u(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)}var p=function(){return(p=Object.assign||function(t){for(var e,n=1,o=arguments.length;n<o;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t}).apply(this,arguments)};function g(t,e,n,o){return new(n||(n=Promise))(function(r,s){function i(t){try{c(o.next(t))}catch(t){s(t)}}function a(t){try{c(o.throw(t))}catch(t){s(t)}}function c(t){t.done?r(t.value):new n(function(e){e(t.value)}).then(i,a)}c((o=o.apply(t,e||[])).next())})}function f(t,e){var n,o,r,s,i={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,o&&(r=2&s[0]?o.return:s[0]?o.throw||((r=o.return)&&r.call(o),0):o.next)&&!(r=r.call(o,s[1])).done)return r;switch(o=0,r&&(s=[2&s[0],r.value]),s[0]){case 0:case 1:r=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,o=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!(r=(r=i.trys).length>0&&r[r.length-1])&&(6===s[0]||2===s[0])){i=0;continue}if(3===s[0]&&(!r||s[1]>r[0]&&s[1]<r[3])){i.label=s[1];break}if(6===s[0]&&i.label<r[1]){i.label=r[1],r=s;break}if(r&&i.label<r[2]){i.label=r[2],i.ops.push(s);break}r[2]&&i.ops.pop(),i.trys.pop();continue}s=e.call(t,i)}catch(t){s=[6,t],o=0}finally{n=r=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}([s,a])}}}var m,v=function(t){function e(e,n){var o=this,r=this.constructor.prototype;return(o=t.call(this,e)||this).statusCode=n,o.__proto__=r,o}return d(e,t),e}(Error),b=function(t){function e(e){var n=this.constructor;void 0===e&&(e="A timeout occurred.");var o=this,r=n.prototype;return(o=t.call(this,e)||this).__proto__=r,o}return d(e,t),e}(Error);!function(t){t[t.Trace=0]="Trace",t[t.Debug=1]="Debug",t[t.Information=2]="Information",t[t.Warning=3]="Warning",t[t.Error=4]="Error",t[t.Critical=5]="Critical",t[t.None=6]="None"}(m||(m={}));var y,w=function(){return function(t,e,n){this.statusCode=t,this.statusText=e,this.content=n}}(),C=function(t){function e(e){var n=t.call(this)||this;return n.logger=e,n}return d(e,t),e.prototype.send=function(t){var e=this;return new Promise(function(n,o){var r=new XMLHttpRequest;r.open(t.method,t.url,!0),r.withCredentials=!0,r.setRequestHeader("X-Requested-With","XMLHttpRequest"),r.setRequestHeader("Content-Type","text/plain;charset=UTF-8"),t.headers&&Object.keys(t.headers).forEach(function(e){return r.setRequestHeader(e,t.headers[e])}),t.responseType&&(r.responseType=t.responseType),t.abortSignal&&(t.abortSignal.onabort=function(){r.abort()}),t.timeout&&(r.timeout=t.timeout),r.onload=function(){t.abortSignal&&(t.abortSignal.onabort=null),r.status>=200&&r.status<300?n(new w(r.status,r.statusText,r.response||r.responseText)):o(new v(r.statusText,r.status))},r.onerror=function(){e.logger.log(m.Warning,"Error from HTTP request. "+r.status+": "+r.statusText),o(new v(r.statusText,r.status))},r.ontimeout=function(){e.logger.log(m.Warning,"Timeout from HTTP request."),o(new b)},r.send(t.content||"")})},e}(function(){function t(){}return t.prototype.get=function(t,e){return this.send(p({},e,{method:"GET",url:t}))},t.prototype.post=function(t,e){return this.send(p({},e,{method:"POST",url:t}))},t.prototype.delete=function(t,e){return this.send(p({},e,{method:"DELETE",url:t}))},t}()),E=function(){function t(){}return t.write=function(e){return""+e+t.RecordSeparator},t.parse=function(e){if(e[e.length-1]!==t.RecordSeparator)throw new Error("Message is incomplete.");var n=e.split(t.RecordSeparator);return n.pop(),n},t.RecordSeparatorCode=30,t.RecordSeparator=String.fromCharCode(t.RecordSeparatorCode),t}(),S=function(){function t(){}return t.prototype.writeHandshakeRequest=function(t){return E.write(JSON.stringify(t))},t.prototype.parseHandshakeResponse=function(t){var e,n;if(t instanceof ArrayBuffer){var o=new Uint8Array(t);if(-1===(s=o.indexOf(E.RecordSeparatorCode)))throw new Error("Message is incomplete.");var r=s+1;e=String.fromCharCode.apply(null,o.slice(0,r)),n=o.byteLength>r?o.slice(r).buffer:null}else{var s,i=t;if(-1===(s=i.indexOf(E.RecordSeparator)))throw new Error("Message is incomplete.");r=s+1;e=i.substring(0,r),n=i.length>r?i.substring(r):null}var a=E.parse(e);return[n,JSON.parse(a[0])]},t}();!function(t){t[t.Invocation=1]="Invocation",t[t.StreamItem=2]="StreamItem",t[t.Completion=3]="Completion",t[t.StreamInvocation=4]="StreamInvocation",t[t.CancelInvocation=5]="CancelInvocation",t[t.Ping=6]="Ping",t[t.Close=7]="Close"}(y||(y={}));var k=function(){function t(){}return t.prototype.log=function(t,e){},t.instance=new t,t}(),I=function(){function t(){}return t.isRequired=function(t,e){if(null===t||void 0===t)throw new Error("The '"+e+"' argument is required.")},t.isIn=function(t,e,n){if(!(t in e))throw new Error("Unknown "+n+" value: "+t+".")},t}();function T(t,e){var n=null;return t instanceof ArrayBuffer?(n="Binary data of length "+t.byteLength,e&&(n+=". Content: '"+function(t){var e="";return new Uint8Array(t).forEach(function(t){e+="0x"+(t<16?"0":"")+t.toString(16)+" "}),e.substr(0,e.length-1)}(t)+"'")):"string"==typeof t&&(n="String data of length "+t.length,e&&(n+=". Content: '"+t+"'.")),n}function L(t,e,n,o,r,s,i){return g(this,void 0,void 0,function(){var a,c,l,h;return f(this,function(u){switch(u.label){case 0:return[4,r()];case 1:return(c=u.sent())&&((h={}).Authorization="Bearer "+c,a=h),t.log(m.Trace,"("+e+" transport) sending data. "+T(s,i)+"."),[4,n.post(o,{content:s,headers:a})];case 2:return l=u.sent(),t.log(m.Trace,"("+e+" transport) request complete. Response status: "+l.statusCode+"."),[2]}})})}var P,M,x=function(){function t(t){this.observers=[],this.cancelCallback=t}return t.prototype.next=function(t){for(var e=0,n=this.observers;e<n.length;e++){n[e].next(t)}},t.prototype.error=function(t){for(var e=0,n=this.observers;e<n.length;e++){var o=n[e];o.error&&o.error(t)}},t.prototype.complete=function(){for(var t=0,e=this.observers;t<e.length;t++){var n=e[t];n.complete&&n.complete()}},t.prototype.subscribe=function(t){return this.observers.push(t),new B(this,t)},t}(),B=function(){function t(t,e){this.subject=t,this.observer=e}return t.prototype.dispose=function(){var t=this.subject.observers.indexOf(this.observer);t>-1&&this.subject.observers.splice(t,1),0===this.subject.observers.length&&this.subject.cancelCallback().catch(function(t){})},t}(),j=function(){function t(t){this.minimumLogLevel=t}return t.prototype.log=function(t,e){if(t>=this.minimumLogLevel)switch(t){case m.Critical:case m.Error:console.error(m[t]+": "+e);break;case m.Warning:console.warn(m[t]+": "+e);break;case m.Information:console.info(m[t]+": "+e);break;default:console.log(m[t]+": "+e)}},t}(),R=3e4,N=function(){function t(t,e,n){var o=this;I.isRequired(t,"connection"),I.isRequired(e,"logger"),I.isRequired(n,"protocol"),this.serverTimeoutInMilliseconds=R,this.logger=e,this.protocol=n,this.connection=t,this.handshakeProtocol=new S,this.connection.onreceive=function(t){return o.processIncomingData(t)},this.connection.onclose=function(t){return o.connectionClosed(t)},this.callbacks={},this.methods={},this.closedCallbacks=[],this.id=0}return t.create=function(e,n,o){return new t(e,n,o)},t.prototype.start=function(){return g(this,void 0,void 0,function(){var t;return f(this,function(e){switch(e.label){case 0:return t={protocol:this.protocol.name,version:this.protocol.version},this.logger.log(m.Debug,"Starting HubConnection."),this.receivedHandshakeResponse=!1,[4,this.connection.start(this.protocol.transferFormat)];case 1:return e.sent(),this.logger.log(m.Debug,"Sending handshake request."),[4,this.connection.send(this.handshakeProtocol.writeHandshakeRequest(t))];case 2:return e.sent(),this.logger.log(m.Information,"Using HubProtocol '"+this.protocol.name+"'."),this.cleanupTimeout(),this.configureTimeout(),[2]}})})},t.prototype.stop=function(){return this.logger.log(m.Debug,"Stopping HubConnection."),this.cleanupTimeout(),this.connection.stop()},t.prototype.stream=function(t){for(var e=this,n=[],o=1;o<arguments.length;o++)n[o-1]=arguments[o];var r=this.createStreamInvocation(t,n),s=new x(function(){var t=e.createCancelInvocation(r.invocationId),n=e.protocol.writeMessage(t);return delete e.callbacks[r.invocationId],e.connection.send(n)});this.callbacks[r.invocationId]=function(t,e){e?s.error(e):t.type===y.Completion?t.error?s.error(new Error(t.error)):s.complete():s.next(t.item)};var i=this.protocol.writeMessage(r);return this.connection.send(i).catch(function(t){s.error(t),delete e.callbacks[r.invocationId]}),s},t.prototype.send=function(t){for(var e=[],n=1;n<arguments.length;n++)e[n-1]=arguments[n];var o=this.createInvocation(t,e,!0),r=this.protocol.writeMessage(o);return this.connection.send(r)},t.prototype.invoke=function(t){for(var e=this,n=[],o=1;o<arguments.length;o++)n[o-1]=arguments[o];var r=this.createInvocation(t,n,!1);return new Promise(function(t,n){e.callbacks[r.invocationId]=function(e,o){if(o)n(o);else if(e.type===y.Completion){var r=e;r.error?n(new Error(r.error)):t(r.result)}else n(new Error("Unexpected message type: "+e.type))};var o=e.protocol.writeMessage(r);e.connection.send(o).catch(function(t){n(t),delete e.callbacks[r.invocationId]})})},t.prototype.on=function(t,e){t&&e&&(t=t.toLowerCase(),this.methods[t]||(this.methods[t]=[]),-1===this.methods[t].indexOf(e)&&this.methods[t].push(e))},t.prototype.off=function(t,e){if(t){t=t.toLowerCase();var n=this.methods[t];if(n)if(e){var o=n.indexOf(e);-1!==o&&(n.splice(o,1),0===n.length&&delete this.methods[t])}else delete this.methods[t]}},t.prototype.onclose=function(t){t&&this.closedCallbacks.push(t)},t.prototype.processIncomingData=function(t){if(this.cleanupTimeout(),this.receivedHandshakeResponse||(t=this.processHandshakeResponse(t),this.receivedHandshakeResponse=!0),t)for(var e=0,n=this.protocol.parseMessages(t,this.logger);e<n.length;e++){var o=n[e];switch(o.type){case y.Invocation:this.invokeClientMethod(o);break;case y.StreamItem:case y.Completion:var r=this.callbacks[o.invocationId];null!=r&&(o.type===y.Completion&&delete this.callbacks[o.invocationId],r(o));break;case y.Ping:break;case y.Close:this.logger.log(m.Information,"Close message received from server."),this.connection.stop(o.error?new Error("Server returned an error on close: "+o.error):null);break;default:this.logger.log(m.Warning,"Invalid message type: "+o.type)}}this.configureTimeout()},t.prototype.processHandshakeResponse=function(t){var e,n,o;try{n=(o=this.handshakeProtocol.parseHandshakeResponse(t))[0],e=o[1]}catch(t){var r="Error parsing handshake response: "+t;this.logger.log(m.Error,r);var s=new Error(r);throw this.connection.stop(s),s}if(e.error){r="Server returned handshake error: "+e.error;this.logger.log(m.Error,r),this.connection.stop(new Error(r))}else this.logger.log(m.Debug,"Server handshake complete.");return n},t.prototype.configureTimeout=function(){var t=this;this.connection.features&&this.connection.features.inherentKeepAlive||(this.timeoutHandle=setTimeout(function(){return t.serverTimeout()},this.serverTimeoutInMilliseconds))},t.prototype.serverTimeout=function(){this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server."))},t.prototype.invokeClientMethod=function(t){var e=this,n=this.methods[t.target.toLowerCase()];if(n){if(n.forEach(function(n){return n.apply(e,t.arguments)}),t.invocationId){var o="Server requested a response, which is not supported in this version of the client.";this.logger.log(m.Error,o),this.connection.stop(new Error(o))}}else this.logger.log(m.Warning,"No client method with the name '"+t.target+"' found.")},t.prototype.connectionClosed=function(t){var e=this,n=this.callbacks;this.callbacks={},Object.keys(n).forEach(function(e){(0,n[e])(void 0,t||new Error("Invocation canceled due to connection being closed."))}),this.cleanupTimeout(),this.closedCallbacks.forEach(function(n){return n.apply(e,[t])})},t.prototype.cleanupTimeout=function(){this.timeoutHandle&&clearTimeout(this.timeoutHandle)},t.prototype.createInvocation=function(t,e,n){if(n)return{arguments:e,target:t,type:y.Invocation};var o=this.id;return this.id++,{arguments:e,invocationId:o.toString(),target:t,type:y.Invocation}},t.prototype.createStreamInvocation=function(t,e){var n=this.id;return this.id++,{arguments:e,invocationId:n.toString(),target:t,type:y.StreamInvocation}},t.prototype.createCancelInvocation=function(t){return{invocationId:t,type:y.CancelInvocation}},t}();!function(t){t[t.None=0]="None",t[t.WebSockets=1]="WebSockets",t[t.ServerSentEvents=2]="ServerSentEvents",t[t.LongPolling=4]="LongPolling"}(P||(P={})),function(t){t[t.Text=1]="Text",t[t.Binary=2]="Binary"}(M||(M={}));var O=function(){function t(){this.isAborted=!1}return t.prototype.abort=function(){this.isAborted||(this.isAborted=!0,this.onabort&&this.onabort())},Object.defineProperty(t.prototype,"signal",{get:function(){return this},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"aborted",{get:function(){return this.isAborted},enumerable:!0,configurable:!0}),t}(),H=5e3,F=function(){function t(t,e,n,o,r){this.httpClient=t,this.accessTokenFactory=e||function(){return null},this.logger=n,this.pollAbort=new O,this.logMessageContent=o,this.shutdownTimeout=r||H}return Object.defineProperty(t.prototype,"pollAborted",{get:function(){return this.pollAbort.aborted},enumerable:!0,configurable:!0}),t.prototype.connect=function(t,e){return g(this,void 0,void 0,function(){var n,o,r,s,i;return f(this,function(a){switch(a.label){case 0:if(I.isRequired(t,"url"),I.isRequired(e,"transferFormat"),I.isIn(e,M,"transferFormat"),this.url=t,this.logger.log(m.Trace,"(LongPolling transport) Connecting"),e===M.Binary&&"string"!=typeof(new XMLHttpRequest).responseType)throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");return n={abortSignal:this.pollAbort.signal,headers:{},timeout:9e4},e===M.Binary&&(n.responseType="arraybuffer"),[4,this.accessTokenFactory()];case 1:return o=a.sent(),this.updateHeaderToken(n,o),s=t+"&_="+Date.now(),this.logger.log(m.Trace,"(LongPolling transport) polling: "+s),[4,this.httpClient.get(s,n)];case 2:return 200!==(i=a.sent()).statusCode?(this.logger.log(m.Error,"(LongPolling transport) Unexpected response code: "+i.statusCode),r=new v(i.statusText,i.statusCode),this.running=!1):this.running=!0,this.poll(this.url,n,r),[2,Promise.resolve()]}})})},t.prototype.updateHeaderToken=function(t,e){e?t.headers.Authorization="Bearer "+e:t.headers.Authorization&&delete t.headers.Authorization},t.prototype.poll=function(t,e,n){return g(this,void 0,void 0,function(){var o,r,s,i;return f(this,function(a){switch(a.label){case 0:a.trys.push([0,,8,9]),a.label=1;case 1:return this.running?[4,this.accessTokenFactory()]:[3,7];case 2:o=a.sent(),this.updateHeaderToken(e,o),a.label=3;case 3:return a.trys.push([3,5,,6]),r=t+"&_="+Date.now(),this.logger.log(m.Trace,"(LongPolling transport) polling: "+r),[4,this.httpClient.get(r,e)];case 4:return 204===(s=a.sent()).statusCode?(this.logger.log(m.Information,"(LongPolling transport) Poll terminated by server"),this.running=!1):200!==s.statusCode?(this.logger.log(m.Error,"(LongPolling transport) Unexpected response code: "+s.statusCode),n=new v(s.statusText,s.statusCode),this.running=!1):s.content?(this.logger.log(m.Trace,"(LongPolling transport) data received. "+T(s.content,this.logMessageContent)),this.onreceive&&this.onreceive(s.content)):this.logger.log(m.Trace,"(LongPolling transport) Poll timed out, reissuing."),[3,6];case 5:return i=a.sent(),this.running?i instanceof b?this.logger.log(m.Trace,"(LongPolling transport) Poll timed out, reissuing."):(n=i,this.running=!1):this.logger.log(m.Trace,"(LongPolling transport) Poll errored after shutdown: "+i.message),[3,6];case 6:return[3,1];case 7:return[3,9];case 8:return this.stopped=!0,this.shutdownTimer&&clearTimeout(this.shutdownTimer),this.onclose&&(this.logger.log(m.Trace,"(LongPolling transport) Firing onclose event. Error: "+(n||"<undefined>")),this.onclose(n)),this.logger.log(m.Trace,"(LongPolling transport) Transport finished."),[7];case 9:return[2]}})})},t.prototype.send=function(t){return g(this,void 0,void 0,function(){return f(this,function(e){return this.running?[2,L(this.logger,"LongPolling",this.httpClient,this.url,this.accessTokenFactory,t,this.logMessageContent)]:[2,Promise.reject(new Error("Cannot send until the transport is connected"))]})})},t.prototype.stop=function(){return g(this,void 0,void 0,function(){var t,e,n=this;return f(this,function(o){switch(o.label){case 0:return o.trys.push([0,,3,4]),this.running=!1,this.logger.log(m.Trace,"(LongPolling transport) sending DELETE request to "+this.url+"."),t={headers:{}},[4,this.accessTokenFactory()];case 1:return e=o.sent(),this.updateHeaderToken(t,e),[4,this.httpClient.delete(this.url,t)];case 2:return o.sent(),this.logger.log(m.Trace,"(LongPolling transport) DELETE request accepted."),[3,4];case 3:return this.stopped||(this.shutdownTimer=setTimeout(function(){n.logger.log(m.Warning,"(LongPolling transport) server did not terminate after DELETE request, canceling poll."),n.pollAbort.abort()},this.shutdownTimeout)),[7];case 4:return[2]}})})},t}(),q=function(){function t(t,e,n,o){this.httpClient=t,this.accessTokenFactory=e||function(){return null},this.logger=n,this.logMessageContent=o}return t.prototype.connect=function(t,e){return g(this,void 0,void 0,function(){var n,o=this;return f(this,function(r){switch(r.label){case 0:if(I.isRequired(t,"url"),I.isRequired(e,"transferFormat"),I.isIn(e,M,"transferFormat"),"undefined"==typeof EventSource)throw new Error("'EventSource' is not supported in your environment.");return this.logger.log(m.Trace,"(SSE transport) Connecting"),[4,this.accessTokenFactory()];case 1:return(n=r.sent())&&(t+=(t.indexOf("?")<0?"?":"&")+"access_token="+encodeURIComponent(n)),this.url=t,[2,new Promise(function(n,r){var s=!1;e!==M.Text&&r(new Error("The Server-Sent Events transport only supports the 'Text' transfer format"));var i=new EventSource(t,{withCredentials:!0});try{i.onmessage=function(t){if(o.onreceive)try{o.logger.log(m.Trace,"(SSE transport) data received. "+T(t.data,o.logMessageContent)+"."),o.onreceive(t.data)}catch(t){return void(o.onclose&&o.onclose(t))}},i.onerror=function(t){var e=new Error(t.message||"Error occurred");s?o.close(e):r(e)},i.onopen=function(){o.logger.log(m.Information,"SSE connected to "+o.url),o.eventSource=i,s=!0,n()}}catch(t){return Promise.reject(t)}})]}})})},t.prototype.send=function(t){return g(this,void 0,void 0,function(){return f(this,function(e){return this.eventSource?[2,L(this.logger,"SSE",this.httpClient,this.url,this.accessTokenFactory,t,this.logMessageContent)]:[2,Promise.reject(new Error("Cannot send until the transport is connected"))]})})},t.prototype.stop=function(){return this.close(),Promise.resolve()},t.prototype.close=function(t){this.eventSource&&(this.eventSource.close(),this.eventSource=null,this.onclose&&this.onclose(t))},t}(),U=function(){function t(t,e,n){this.logger=e,this.accessTokenFactory=t||function(){return null},this.logMessageContent=n}return t.prototype.connect=function(t,e){return g(this,void 0,void 0,function(){var n,o=this;return f(this,function(r){switch(r.label){case 0:if(I.isRequired(t,"url"),I.isRequired(e,"transferFormat"),I.isIn(e,M,"transferFormat"),"undefined"==typeof WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return this.logger.log(m.Trace,"(WebSockets transport) Connecting"),[4,this.accessTokenFactory()];case 1:return(n=r.sent())&&(t+=(t.indexOf("?")<0?"?":"&")+"access_token="+encodeURIComponent(n)),[2,new Promise(function(n,r){t=t.replace(/^http/,"ws");var s=new WebSocket(t);e===M.Binary&&(s.binaryType="arraybuffer"),s.onopen=function(e){o.logger.log(m.Information,"WebSocket connected to "+t),o.webSocket=s,n()},s.onerror=function(t){r(t.error)},s.onmessage=function(t){o.logger.log(m.Trace,"(WebSockets transport) data received. "+T(t.data,o.logMessageContent)+"."),o.onreceive&&o.onreceive(t.data)},s.onclose=function(t){o.logger.log(m.Trace,"(WebSockets transport) socket closed."),o.onclose&&(!1===t.wasClean||1e3!==t.code?o.onclose(new Error("Websocket closed with status code: "+t.code+" ("+t.reason+")")):o.onclose())}})]}})})},t.prototype.send=function(t){return this.webSocket&&this.webSocket.readyState===WebSocket.OPEN?(this.logger.log(m.Trace,"(WebSockets transport) sending data. "+T(t,this.logMessageContent)+"."),this.webSocket.send(t),Promise.resolve()):Promise.reject("WebSocket is not in the OPEN state")},t.prototype.stop=function(){return this.webSocket&&(this.webSocket.close(),this.webSocket=null),Promise.resolve()},t}(),A=function(){function t(t,e){void 0===e&&(e={}),this.features={},I.isRequired(t,"url"),this.logger=function(t){return void 0===t?new j(m.Information):null===t?k.instance:t.log?t:new j(t)}(e.logger),this.baseUrl=this.resolveUrl(t),(e=e||{}).accessTokenFactory=e.accessTokenFactory||function(){return null},e.logMessageContent=e.logMessageContent||!1,this.httpClient=e.httpClient||new C(this.logger),this.connectionState=2,this.options=e}return t.prototype.start=function(t){return t=t||M.Binary,I.isIn(t,M,"transferFormat"),this.logger.log(m.Debug,"Starting connection with transfer format '"+M[t]+"'."),2!==this.connectionState?Promise.reject(new Error("Cannot start a connection that is not in the 'Disconnected' state.")):(this.connectionState=0,this.startPromise=this.startInternal(t),this.startPromise)},t.prototype.send=function(t){if(1!==this.connectionState)throw new Error("Cannot send data if the connection is not in the 'Connected' State.");return this.transport.send(t)},t.prototype.stop=function(t){return g(this,void 0,void 0,function(){return f(this,function(e){switch(e.label){case 0:this.connectionState=2,e.label=1;case 1:return e.trys.push([1,3,,4]),[4,this.startPromise];case 2:return e.sent(),[3,4];case 3:return e.sent(),[3,4];case 4:return this.transport?(this.stopError=t,[4,this.transport.stop()]):[3,6];case 5:e.sent(),this.transport=null,e.label=6;case 6:return[2]}})})},t.prototype.startInternal=function(t){return g(this,void 0,void 0,function(){var e,n,o,r,s,i,a,c=this;return f(this,function(l){switch(l.label){case 0:e=this.baseUrl,this.accessTokenFactory=this.options.accessTokenFactory,l.label=1;case 1:return l.trys.push([1,12,,13]),this.options.skipNegotiation?this.options.transport!==P.WebSockets?[3,3]:(this.transport=this.constructTransport(P.WebSockets),[4,this.transport.connect(e,t)]):[3,5];case 2:return l.sent(),[3,4];case 3:throw Error("Negotiation can only be skipped when using the WebSocket transport directly.");case 4:return[3,11];case 5:n=null,o=0,r=function(){var t;return f(this,function(r){switch(r.label){case 0:return[4,s.getNegotiationResponse(e)];case 1:return n=r.sent(),2===s.connectionState?[2,{value:void 0}]:(n.url&&(e=n.url),n.accessToken&&(t=n.accessToken,s.accessTokenFactory=function(){return t}),o++,[2])}})},s=this,l.label=6;case 6:return[5,r()];case 7:if("object"==typeof(i=l.sent()))return[2,i.value];l.label=8;case 8:if(n.url&&o<100)return[3,6];l.label=9;case 9:if(100===o&&n.url)throw Error("Negotiate redirection limit exceeded.");return[4,this.createTransport(e,this.options.transport,n,t)];case 10:l.sent(),l.label=11;case 11:return this.transport instanceof F&&(this.features.inherentKeepAlive=!0),this.transport.onreceive=this.onreceive,this.transport.onclose=function(t){return c.stopConnection(t)},this.changeState(0,1),[3,13];case 12:throw a=l.sent(),this.logger.log(m.Error,"Failed to start the connection: "+a),this.connectionState=2,this.transport=null,a;case 13:return[2]}})})},t.prototype.getNegotiationResponse=function(t){return g(this,void 0,void 0,function(){var e,n,o,r,s,i;return f(this,function(a){switch(a.label){case 0:return[4,this.accessTokenFactory()];case 1:(e=a.sent())&&((i={}).Authorization="Bearer "+e,n=i),o=this.resolveNegotiateUrl(t),this.logger.log(m.Debug,"Sending negotiation request: "+o),a.label=2;case 2:return a.trys.push([2,4,,5]),[4,this.httpClient.post(o,{content:"",headers:n})];case 3:if(200!==(r=a.sent()).statusCode)throw Error("Unexpected status code returned from negotiate "+r.statusCode);return[2,JSON.parse(r.content)];case 4:throw s=a.sent(),this.logger.log(m.Error,"Failed to complete negotiation with the server: "+s),s;case 5:return[2]}})})},t.prototype.createConnectUrl=function(t,e){return t+(-1===t.indexOf("?")?"?":"&")+"id="+e},t.prototype.createTransport=function(t,e,n,o){return g(this,void 0,void 0,function(){var r,s,i,a,c,l,h;return f(this,function(u){switch(u.label){case 0:return r=this.createConnectUrl(t,n.connectionId),this.isITransport(e)?(this.logger.log(m.Debug,"Connection was provided an instance of ITransport, using that directly."),this.transport=e,[4,this.transport.connect(r,o)]):[3,2];case 1:return u.sent(),this.changeState(0,1),[2];case 2:s=n.availableTransports,i=0,a=s,u.label=3;case 3:return i<a.length?(c=a[i],this.connectionState=0,"number"!=typeof(l=this.resolveTransport(c,e,o))?[3,8]:(this.transport=this.constructTransport(l),null!==n.connectionId?[3,5]:[4,this.getNegotiationResponse(t)])):[3,9];case 4:n=u.sent(),r=this.createConnectUrl(t,n.connectionId),u.label=5;case 5:return u.trys.push([5,7,,8]),[4,this.transport.connect(r,o)];case 6:return u.sent(),this.changeState(0,1),[2];case 7:return h=u.sent(),this.logger.log(m.Error,"Failed to start the transport '"+P[l]+"': "+h),this.connectionState=2,n.connectionId=null,[3,8];case 8:return i++,[3,3];case 9:throw new Error("Unable to initialize any of the available transports.")}})})},t.prototype.constructTransport=function(t){switch(t){case P.WebSockets:return new U(this.accessTokenFactory,this.logger,this.options.logMessageContent);case P.ServerSentEvents:return new q(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent);case P.LongPolling:return new F(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent);default:throw new Error("Unknown transport: "+t+".")}},t.prototype.resolveTransport=function(t,e,n){var o=P[t.transport];if(null===o||void 0===o)this.logger.log(m.Debug,"Skipping transport '"+t.transport+"' because it is not supported by this client.");else{var r=t.transferFormats.map(function(t){return M[t]});if(function(t,e){return!t||0!=(e&t)}(e,o))if(r.indexOf(n)>=0){if(!(o===P.WebSockets&&"undefined"==typeof WebSocket||o===P.ServerSentEvents&&"undefined"==typeof EventSource))return this.logger.log(m.Debug,"Selecting transport '"+P[o]+"'"),o;this.logger.log(m.Debug,"Skipping transport '"+P[o]+"' because it is not supported in your environment.'")}else this.logger.log(m.Debug,"Skipping transport '"+P[o]+"' because it does not support the requested transfer format '"+M[n]+"'.");else this.logger.log(m.Debug,"Skipping transport '"+P[o]+"' because it was disabled by the client.")}return null},t.prototype.isITransport=function(t){return t&&"object"==typeof t&&"connect"in t},t.prototype.changeState=function(t,e){return this.connectionState===t&&(this.connectionState=e,!0)},t.prototype.stopConnection=function(t){return g(this,void 0,void 0,function(){return f(this,function(e){return this.transport=null,(t=this.stopError||t)?this.logger.log(m.Error,"Connection disconnected with error '"+t+"'."):this.logger.log(m.Information,"Connection disconnected."),this.connectionState=2,this.onclose&&this.onclose(t),[2]})})},t.prototype.resolveUrl=function(t){if(0===t.lastIndexOf("https://",0)||0===t.lastIndexOf("http://",0))return t;if("undefined"==typeof window||!window||!window.document)throw new Error("Cannot resolve '"+t+"'.");var e=window.document.createElement("a");return e.href=t,this.logger.log(m.Information,"Normalizing '"+t+"' to '"+e.href+"'."),e.href},t.prototype.resolveNegotiateUrl=function(t){var e=t.indexOf("?"),n=t.substring(0,-1===e?t.length:e);return"/"!==n[n.length-1]&&(n+="/"),n+="negotiate",n+=-1===e?"":t.substring(e)},t}();var D="json",W=function(){function t(){this.name=D,this.version=1,this.transferFormat=M.Text}return t.prototype.parseMessages=function(t,e){if("string"!=typeof t)throw new Error("Invalid input for JSON hub protocol. Expected a string.");if(!t)return[];null===e&&(e=k.instance);for(var n=[],o=0,r=E.parse(t);o<r.length;o++){var s=r[o],i=JSON.parse(s);if("number"!=typeof i.type)throw new Error("Invalid payload.");switch(i.type){case y.Invocation:this.isInvocationMessage(i);break;case y.StreamItem:this.isStreamItemMessage(i);break;case y.Completion:this.isCompletionMessage(i);break;case y.Ping:case y.Close:break;default:e.log(m.Information,"Unknown message type '"+i.type+"' ignored.");continue}n.push(i)}return n},t.prototype.writeMessage=function(t){return E.write(JSON.stringify(t))},t.prototype.isInvocationMessage=function(t){this.assertNotEmptyString(t.target,"Invalid payload for Invocation message."),void 0!==t.invocationId&&this.assertNotEmptyString(t.invocationId,"Invalid payload for Invocation message.")},t.prototype.isStreamItemMessage=function(t){if(this.assertNotEmptyString(t.invocationId,"Invalid payload for StreamItem message."),void 0===t.item)throw new Error("Invalid payload for StreamItem message.")},t.prototype.isCompletionMessage=function(t){if(t.result&&t.error)throw new Error("Invalid payload for Completion message.");!t.result&&t.error&&this.assertNotEmptyString(t.error,"Invalid payload for Completion message."),this.assertNotEmptyString(t.invocationId,"Invalid payload for Completion message.")},t.prototype.assertNotEmptyString=function(t,e){if("string"!=typeof t||""===t)throw new Error(e)},t}(),_=function(){function t(){}return t.prototype.configureLogging=function(t){return I.isRequired(t,"logging"),!function(t){return void 0!==t.log}(t)?this.logger=new j(t):this.logger=t,this},t.prototype.withUrl=function(t,e){return I.isRequired(t,"url"),this.url=t,this.httpConnectionOptions="object"==typeof e?e:{transport:e},this},t.prototype.withHubProtocol=function(t){return I.isRequired(t,"protocol"),this.protocol=t,this},t.prototype.build=function(){var t=this.httpConnectionOptions||{};if(void 0===t.logger&&(t.logger=this.logger),!this.url)throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection.");var e=new A(this.url,t);return N.create(e,this.logger||k.instance,this.protocol||new W)},t}();class z{constructor(t){this.iface=t}registerPublic(t,...e){this.iface.addObject(this,t,["destroy"].concat(e))}destroy(){this.iface.removeObject(this)}}class G extends z{constructor(t){super(t),this.registerPublic("listServers","listServers"),this.refreshing=!1}listServers(){if(this.refreshing)return;this.iface.callMethod("networker","registerHandler","ListGroups",t=>{this.iface.callMethod("serverListing","flushElements"),this.iface.callMethod("serverListing","addElements",t,this.iface),this.iface.callMethod("networker","removeHandler","ListGroups"),this.refreshing=!1}),this.iface.callMethod("networker","sendRequest","GetGroups",t=>{this.refreshing=!1,console.error(t.toString())}),this.refreshing=!0}}class J extends z{constructor(t){super(t),this.registerPublic("createServer","createServer"),this.refreshing=!1}createServer(){}}class X{constructor(t){let e=document.createElement("div"),n=document.createElement("div"),o=document.createElement("h1"),r=document.createElement("div");e.className="modal-container",n.className="modal",o.className="modal-title",r.className="modal-body",o.textContent=t,n.appendChild(o),n.appendChild(r),e.appendChild(n),document.body.appendChild(e),this.bg=e,this.modal=n,this.title=o,this.body=r,this.registerEvents()}registerEvents(){this.modal.addEventListener("click",t=>{t.stopPropagation()}),this.bg.addEventListener("click",()=>{this.close()})}close(){this.bg.classList.add("hidden"),this.bg.addEventListener("transitionend",()=>{document.body.removeChild(this.bg)})}setBodyText(t){this.body.textContent=t}}n(0);class K extends X{constructor(t,e){super(e),this.serverName=e,t.addObject(this,"modal",["loginFailed","close"]),this.iface=t;let n=document.createElement("div"),o=document.createElement("div"),r=document.createElement("div"),s=document.createElement("label"),i=document.createElement("input"),a=document.createElement("span");s.setAttribute("for","password-input"),s.textContent="Passwort:",s.title="Das Passwort des Spiels",i.id="password-input",i.type="password",i.placeholder="Passwort",i.title="Das Passwort des Spiels",a.className="invalid hidden",a.textContent="Das eingegebene Passwort ist falsch.";let c=document.createElement("label"),l=document.createElement("input"),h=document.createElement("span");c.setAttribute("for","name-input"),c.textContent="Benutzername:",c.title="Dein Anzeigename",l.id="name-input",l.type="text",l.autocomplete="on",l.placeholder="Name",l.title="Dein Anzeigename",h.className="invalid hidden",h.textContent="Der eingegebene Nutzername ist bereits vergeben.";let u=document.createElement("button");u.className="btn",u.textContent="Login",u.id="login-button",n.appendChild(s),n.appendChild(i),n.appendChild(a),o.appendChild(c),o.appendChild(l),o.appendChild(h),r.appendChild(u),this.body.appendChild(n),this.body.appendChild(o),this.body.appendChild(r),this.nameInput=l,this.passwordInput=i,this.loginButton=u,this.passwordInvalid=a,this.nameInvalid=h,this.registerLoginBtnEvent()}loginFailed(t){1==t?(this.invalid("Name"),this.loginButton.addEventListener("click",this.event)):2==t?(this.invalid("Pass"),this.loginButton.addEventListener("click",this.event)):(this.iface.callMethod("notifications","show","failed","Ein unbekannter Fehler ist aufgetreten"),this.close())}registerLoginBtnEvent(){this.event=(()=>{this.invalid(),this.loginButton.removeEventListener("click",this.event),this.userName=this.nameInput.value,this.passwordInput.value.getHash().then(t=>{this.iface.callMethod("login","sendLogin",this.serverName,t,this.userName)})}),this.loginButton.addEventListener("click",this.event)}invalid(t){this.body.classList.remove("frst-warning"),this.body.classList.remove("scnd-warning"),this.passwordInvalid.classList.add("hidden"),this.nameInvalid.classList.add("hidden"),this.passwordInput.style.borderColor="none",this.nameInput.style.borderColor="none","Pass"==t?(this.body.classList.add("frst-warning"),this.passwordInvalid.classList.remove("hidden"),this.passwordInput.style.borderColor="#ef5350"):"Name"==t&&(this.body.classList.add("scnd-warning"),this.nameInvalid.classList.remove("hidden"),this.nameInput.style.borderColor="#ef5350")}}class Q extends z{constructor(t){super(t),this.registerPublic("login","sendLogin","showLogin"),this.refreshing=!1}showLogin(t){new K(this.iface,t)}registerLoginResponse(){this.iface.callMethod("networker","registerHandler","LoginResponse",t=>{0==t?(this.iface.callMethod("modal","close"),this.iface.callMethod("router","routePlay"),this.iface.callMethod("networker","removeHandler","LoginResponse")):this.iface.callMethod("modal","loginFailed",t)})}sendLogin(t,e,n){this.registerLoginResponse(),this.iface.callMethod("networker","sendRequest","Login",t=>console.error(t),t,n,e)}}class V{constructor(t){this.iface=t,this.cmds=[],this.registerCommands()}registerCommands(){this.cmds.push(new G(this.iface)),this.cmds.push(new J(this.iface)),this.cmds.push(new Q(this.iface))}destroy(){for(let t of this.cmds)t.destroy()}}class Y{constructor(t){this.iface=t,this.cmds=[]}registerCommands(){}destroy(){for(let t of this.cmds)t.destroy()}}let Z=new class{constructor(){this.objects={}}addObject(t,e,n){this.objects[e]||(this.objects[e]=[]),this.objects[e].push(new o(t,n))}removeObject(t,e){this.objects[e]&&(objects[e]=objects[e].filter(e=>e.object!=t),0==objects[e].length&&(objects[e]=void 0))}callMethod(t,e,...n){if(!this.objects[t])return 1;let o=0;for(let r of this.objects[t])0!=r.execute(e,...n)&&(o=2);return o}};new class{constructor(t){this.currentUI=null,t.addObject(this,"uiMananger",["initAbout","initLogin","initPlay"]),this.iface=t}initAbout(){this.clearComponents(),this.about=new s(this.iface),this.currentUI="about"}initLogin(){this.clearComponents(),this.login=new l(this.iface),this.currentUI="login"}initPlay(){this.clearComponents(),this.play=new h(this.iface),this.currentUI="play"}clearComponents(){switch(this.currentUI){case null:return;case"about":this.about=null;break;case"login":this.login=null;break;case"play":this.play=null}this.currentUI=null}}(Z).initLogin(),new class{constructor(t,e,n=!1){this.url=e,t.addObject(this,"networker",["sendRequest","registerHandler","removeHandler"]),this.iface=t;const o=(new _).withUrl(e);n?o.configureLogging(m.Debug):o.configureLogging(m.Error),this.connection=o.build(),this.connection.start().then(()=>this.iface.callMethod("listServers","listServers")).catch(t=>console.error(t.toString())),this.refreshing=!1}sendRequest(t,e,...n){this.connection.invoke(t,...n).catch(e)}registerHandler(t,e){this.connection.on(t,e)}removeHandler(t){this.connection.off(t)}initLogin(){this.loginCmd=new V(this.iface)}initPlay(){this.playCmd=new Y(this.iface)}clearCommands(){this.loginCmd&&this.loginCmd.destroy(),this.playCmd&&this.playCmd.destroy()}}(Z,"http://127.0.0.1:5000/chatHub",!0).initLogin()}]); \ No newline at end of file
diff --git a/WebInterface/NodeJSServer/dist/script/play.js b/WebInterface/NodeJSServer/dist/script/play.js
index cebfb62..7e78fb6 100644
--- a/WebInterface/NodeJSServer/dist/script/play.js
+++ b/WebInterface/NodeJSServer/dist/script/play.js
@@ -1 +1,16 @@
-!function(t){var e={};function n(s){if(e[s])return e[s].exports;var i=e[s]={i:s,l:!1,exports:{}};return t[s].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=t,n.c=e,n.d=function(t,e,s){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:s})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(s,i,function(e){return t[e]}.bind(null,i));return s},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="../script/",n(n.s=0)}([function(t,e,n){"use strict";n.r(e);new class{constructor(t,e,n){this.ids={backdropMenu:t,frontLayer:e,menuButton:n},this.backdrop=document.getElementById(t),this.frontLayer=document.getElementById(e),this.menuButton=document.getElementById(n)}register(){this.registerButtonEvent(),this.registerFrontLayerEvent()}refresh(){this.backdrop=document.getElementById(this.ids.backdropMenu),this.frontLayer=document.getElementById(this.ids.frontLayer),this.menuButton=document.getElementById(this.ids.menuButton),this.register()}registerButtonEvent(){this.menuButton.addEventListener("click",()=>{this.backdrop.classList.contains("hidden")?this.backdrop.classList.remove("hidden"):this.backdrop.classList.add("hidden"),this.menuButton.classList.contains("open")?this.menuButton.classList.remove("open"):this.menuButton.classList.add("open")})}registerFrontLayerEvent(){this.frontLayer.addEventListener("click",()=>{this.backdrop.classList.add("hidden"),this.menuButton.classList.remove("open")})}}("menu","front-layer","show-menu").register(),new class{constructor(t,e,n,s){this.ids={bannerId:t,textP:e,dismissBtn:n,notificationBadge:s},this.banner=document.getElementById(t),this.bannerText=document.getElementById(e),this.dismissBtn=document.getElementById(n),this.notificationBadge=document.getElementById(s),this.bannerMsgs=[],this.banner.classList.add("hidden")}register(){this.dismissBtn.addEventListener("click",()=>{this.dismissCurrent()})}refresh(){this.banner=document.getElementById(this.ids.bannerId),this.bannerText=document.getElementById(this.ids.textP),this.dismissBtn=document.getElementById(this.ids.dismissBtn),this.notificationBadge=document.getElementById(this.ids.notificationBadge),this.bannerMsgs=[],this.banner.classList.add("hidden")}show(t,e){let n={name:t,text:e,html:!1};this.bannerMsgs.push(n),this.update()}hide(t){if(t){for(let e=0;e<this.bannerMsgs.length;e++)this.bannerMsgs[e].name==t&&this.bannerMsgs.splice(e,1);this.update()}else this.bannerMsgs=[],this.banner.classList.add("hidden")}dismissCurrent(){this.hide(this.current)}update(){if(0===this.bannerMsgs.length)return void this.banner.classList.add("hidden");const t=this.bannerMsgs[this.bannerMsgs.length-1],e=t.name,n=t.text,s=t.html;this.banner.classList.remove("hidden"),s?this.bannerText.innerHTML=n:this.bannerText.innerText=n,this.current=e,this.bannerMsgs.length<2?this.notificationBadge.classList.add("hidden"):this.bannerMsgs.length>9?(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent="∞"):(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent=this.bannerMsgs.length.toString())}}("notifications","banner-info","dismiss-banner","notification-amount").register()}]); \ No newline at end of file
+!function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="../script/",n(n.s=1)}([function(t,e){String.prototype.getHash=async function(){let t=new ArrayBuffer(2*this.length),e=new Uint16Array(t);for(let t=0;t<this.length;t++)e[t]=this.charCodeAt(t);let n=await crypto.subtle.digest("SHA-256",e),o=new Uint8Array(n),r="";for(let t of o)r+=String.fromCharCode(t);return btoa(r)}},function(t,e,n){"use strict";n.r(e);class o{constructor(t,e){this.object=t,this.publicMethods=e}execute(t,...e){return this.publicMethods.includes(t)?"function"!=typeof this.object[t]?2:(this.object[t](...e),0):1}}class r{constructor(t,e,n){this.ids={backdropMenu:t,frontLayer:e,menuButton:n}}initialize(){this.open=!1,this.backdrop=document.getElementById(this.ids.backdropMenu),this.frontLayer=document.getElementById(this.ids.frontLayer),this.menuButton=document.getElementById(this.ids.menuButton),this.registerEvents()}registerEvents(){this.registerButtonEvent(),this.registerFrontLayerEvent()}registerButtonEvent(){this.menuButton.addEventListener("click",()=>{this.open=!this.open,this.open?this.backdrop.classList.remove("hidden"):this.backdrop.classList.add("hidden"),this.open?this.menuButton.classList.add("open"):this.menuButton.classList.remove("open")})}registerFrontLayerEvent(){this.frontLayer.addEventListener("click",()=>{this.open&&(this.open=!1,this.backdrop.classList.add("hidden"),this.menuButton.classList.remove("open"))})}}class i{constructor(){this.backdrop=new r("menu","front-layer","show-menu"),this.backdrop.initialize()}}class s{constructor(t,e,n){this.name=t,this.content=e,this.html=n}}class a{constructor(t,e,n,o,r){t.addObject(this,"notifications",["show","hide"]),this.iface=t,this.ids={bannerId:e,textP:n,dismissBtn:o,badge:r}}initialize(){this.banner=document.getElementById(this.ids.bannerId),this.bannerText=document.getElementById(this.ids.textP),this.dismissBtn=document.getElementById(this.ids.dismissBtn),this.notificationBadge=document.getElementById(this.ids.badge),this.bannerMsgs=[],this.banner.classList.add("hidden"),this.registerEvents()}registerEvents(){this.registerDismissEvent()}registerDismissEvent(){this.dismissBtn.addEventListener("click",()=>{this.dismissCurrent()})}show(t,e){let n=new s(t,e,!1);this.bannerMsgs.push(n),this.update()}hide(t){this.bannerMsgs=t?this.bannerMsgs.filter(e=>e.name!=t):[],this.update()}dismissCurrent(){this.hide(this.current)}update(){if(0===this.bannerMsgs.length)return void this.banner.classList.add("hidden");const t=this.bannerMsgs[this.bannerMsgs.length-1],e=t.name,n=t.content,o=t.html;this.banner.classList.remove("hidden"),o?this.bannerText.innerHTML=n:this.bannerText.innerText=n,this.current=e,this.updateNotificationBadge()}updateNotificationBadge(){this.bannerMsgs.length<2?this.notificationBadge.classList.add("hidden"):this.bannerMsgs.length>9?(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent="∞"):(this.notificationBadge.classList.remove("hidden"),this.notificationBadge.textContent=this.bannerMsgs.length.toString())}}class c{constructor(t,e,n){this.ids={serverListId:e,refreshBtnId:n},t.addObject(this,"serverListing",["flushElements","addElements"]),this.iface=t}initialize(){this.serverListing=document.getElementById(this.ids.serverListId),this.refreshBtn=document.getElementById(this.ids.refreshBtnId),this.registerEvents()}registerEvents(){this.registerRefreshEvent()}registerRefreshEvent(){this.refreshBtn.addEventListener("click",()=>{this.iface.callMethod("listServers","listServers")})}flushElements(){this.serverListing.innerHTML=""}addElements(t){for(let e of t){const t=e.name,n=e.userCount;let o=document.createElement("div"),r=document.createElement("span"),i=document.createElement("div"),s=document.createElement("div"),a=document.createElement("span"),c=document.createElement("span"),u=document.createElement("button");o.className="server",r.className="server-name",i.className="right-aligned-items",s.className="player-count-dot",a.className="player-count",c.className="player-count-static",u.className="btn join-btn",u.id="join",r.textContent=t,a.textContent=n,c.textContent="Spieler online",u.textContent="Beitreten",u.addEventListener("click",()=>{this.iface.callMethod("login","showLogin",t)}),i.appendChild(s),i.appendChild(a),i.appendChild(c),i.appendChild(u),o.appendChild(r),o.appendChild(i),this.serverListing.appendChild(o)}}}class u{constructor(t){this.backdrop=new r("menu","front-layer","show-menu"),this.bannerController=new a(t,"notifications","banner-info","dismiss-banner","notification-amount"),this.serverListing=new c(t,"server-list","refresh-button"),this.backdrop.initialize(),this.bannerController.initialize(),this.serverListing.initialize()}}class l{constructor(){this.backdrop=new r("menu","front-layer","show-menu"),this.bannerController=new a(iface,"notifications","banner-info","dismiss-banner","notification-amount"),this.backdrop.initialize(),this.bannerController.initialize()}}
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+var h=function(t,e){return(h=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(t,e)};function p(t,e){function n(){this.constructor=t}h(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)}var d=function(){return(d=Object.assign||function(t){for(var e,n=1,o=arguments.length;n<o;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t}).apply(this,arguments)};function g(t,e,n,o){return new(n||(n=Promise))(function(r,i){function s(t){try{c(o.next(t))}catch(t){i(t)}}function a(t){try{c(o.throw(t))}catch(t){i(t)}}function c(t){t.done?r(t.value):new n(function(e){e(t.value)}).then(s,a)}c((o=o.apply(t,e||[])).next())})}function f(t,e){var n,o,r,i,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,o&&(r=2&i[0]?o.return:i[0]?o.throw||((r=o.return)&&r.call(o),0):o.next)&&!(r=r.call(o,i[1])).done)return r;switch(o=0,r&&(i=[2&i[0],r.value]),i[0]){case 0:case 1:r=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,o=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===i[0]||2===i[0])){s=0;continue}if(3===i[0]&&(!r||i[1]>r[0]&&i[1]<r[3])){s.label=i[1];break}if(6===i[0]&&s.label<r[1]){s.label=r[1],r=i;break}if(r&&s.label<r[2]){s.label=r[2],s.ops.push(i);break}r[2]&&s.ops.pop(),s.trys.pop();continue}i=e.call(t,s)}catch(t){i=[6,t],o=0}finally{n=r=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,a])}}}var v,m=function(t){function e(e,n){var o=this,r=this.constructor.prototype;return(o=t.call(this,e)||this).statusCode=n,o.__proto__=r,o}return p(e,t),e}(Error),b=function(t){function e(e){var n=this.constructor;void 0===e&&(e="A timeout occurred.");var o=this,r=n.prototype;return(o=t.call(this,e)||this).__proto__=r,o}return p(e,t),e}(Error);!function(t){t[t.Trace=0]="Trace",t[t.Debug=1]="Debug",t[t.Information=2]="Information",t[t.Warning=3]="Warning",t[t.Error=4]="Error",t[t.Critical=5]="Critical",t[t.None=6]="None"}(v||(v={}));var y,w=function(){return function(t,e,n){this.statusCode=t,this.statusText=e,this.content=n}}(),S=function(t){function e(e){var n=t.call(this)||this;return n.logger=e,n}return p(e,t),e.prototype.send=function(t){var e=this;return new Promise(function(n,o){var r=new XMLHttpRequest;r.open(t.method,t.url,!0),r.withCredentials=!0,r.setRequestHeader("X-Requested-With","XMLHttpRequest"),r.setRequestHeader("Content-Type","text/plain;charset=UTF-8"),t.headers&&Object.keys(t.headers).forEach(function(e){return r.setRequestHeader(e,t.headers[e])}),t.responseType&&(r.responseType=t.responseType),t.abortSignal&&(t.abortSignal.onabort=function(){r.abort()}),t.timeout&&(r.timeout=t.timeout),r.onload=function(){t.abortSignal&&(t.abortSignal.onabort=null),r.status>=200&&r.status<300?n(new w(r.status,r.statusText,r.response||r.responseText)):o(new m(r.statusText,r.status))},r.onerror=function(){e.logger.log(v.Warning,"Error from HTTP request. "+r.status+": "+r.statusText),o(new m(r.statusText,r.status))},r.ontimeout=function(){e.logger.log(v.Warning,"Timeout from HTTP request."),o(new b)},r.send(t.content||"")})},e}(function(){function t(){}return t.prototype.get=function(t,e){return this.send(d({},e,{method:"GET",url:t}))},t.prototype.post=function(t,e){return this.send(d({},e,{method:"POST",url:t}))},t.prototype.delete=function(t,e){return this.send(d({},e,{method:"DELETE",url:t}))},t}()),C=function(){function t(){}return t.write=function(e){return""+e+t.RecordSeparator},t.parse=function(e){if(e[e.length-1]!==t.RecordSeparator)throw new Error("Message is incomplete.");var n=e.split(t.RecordSeparator);return n.pop(),n},t.RecordSeparatorCode=30,t.RecordSeparator=String.fromCharCode(t.RecordSeparatorCode),t}(),E=function(){function t(){}return t.prototype.writeHandshakeRequest=function(t){return C.write(JSON.stringify(t))},t.prototype.parseHandshakeResponse=function(t){var e,n;if(t instanceof ArrayBuffer){var o=new Uint8Array(t);if(-1===(i=o.indexOf(C.RecordSeparatorCode)))throw new Error("Message is incomplete.");var r=i+1;e=String.fromCharCode.apply(null,o.slice(0,r)),n=o.byteLength>r?o.slice(r).buffer:null}else{var i,s=t;if(-1===(i=s.indexOf(C.RecordSeparator)))throw new Error("Message is incomplete.");r=i+1;e=s.substring(0,r),n=s.length>r?s.substring(r):null}var a=C.parse(e);return[n,JSON.parse(a[0])]},t}();!function(t){t[t.Invocation=1]="Invocation",t[t.StreamItem=2]="StreamItem",t[t.Completion=3]="Completion",t[t.StreamInvocation=4]="StreamInvocation",t[t.CancelInvocation=5]="CancelInvocation",t[t.Ping=6]="Ping",t[t.Close=7]="Close"}(y||(y={}));var k=function(){function t(){}return t.prototype.log=function(t,e){},t.instance=new t,t}(),T=function(){function t(){}return t.isRequired=function(t,e){if(null===t||void 0===t)throw new Error("The '"+e+"' argument is required.")},t.isIn=function(t,e,n){if(!(t in e))throw new Error("Unknown "+n+" value: "+t+".")},t}();function I(t,e){var n=null;return t instanceof ArrayBuffer?(n="Binary data of length "+t.byteLength,e&&(n+=". Content: '"+function(t){var e="";return new Uint8Array(t).forEach(function(t){e+="0x"+(t<16?"0":"")+t.toString(16)+" "}),e.substr(0,e.length-1)}(t)+"'")):"string"==typeof t&&(n="String data of length "+t.length,e&&(n+=". Content: '"+t+"'.")),n}function L(t,e,n,o,r,i,s){return g(this,void 0,void 0,function(){var a,c,u,l;return f(this,function(h){switch(h.label){case 0:return[4,r()];case 1:return(c=h.sent())&&((l={}).Authorization="Bearer "+c,a=l),t.log(v.Trace,"("+e+" transport) sending data. "+I(i,s)+"."),[4,n.post(o,{content:i,headers:a})];case 2:return u=h.sent(),t.log(v.Trace,"("+e+" transport) request complete. Response status: "+u.statusCode+"."),[2]}})})}var P,M,x=function(){function t(t){this.observers=[],this.cancelCallback=t}return t.prototype.next=function(t){for(var e=0,n=this.observers;e<n.length;e++){n[e].next(t)}},t.prototype.error=function(t){for(var e=0,n=this.observers;e<n.length;e++){var o=n[e];o.error&&o.error(t)}},t.prototype.complete=function(){for(var t=0,e=this.observers;t<e.length;t++){var n=e[t];n.complete&&n.complete()}},t.prototype.subscribe=function(t){return this.observers.push(t),new j(this,t)},t}(),j=function(){function t(t,e){this.subject=t,this.observer=e}return t.prototype.dispose=function(){var t=this.subject.observers.indexOf(this.observer);t>-1&&this.subject.observers.splice(t,1),0===this.subject.observers.length&&this.subject.cancelCallback().catch(function(t){})},t}(),B=function(){function t(t){this.minimumLogLevel=t}return t.prototype.log=function(t,e){if(t>=this.minimumLogLevel)switch(t){case v.Critical:case v.Error:console.error(v[t]+": "+e);break;case v.Warning:console.warn(v[t]+": "+e);break;case v.Information:console.info(v[t]+": "+e);break;default:console.log(v[t]+": "+e)}},t}(),O=3e4,R=function(){function t(t,e,n){var o=this;T.isRequired(t,"connection"),T.isRequired(e,"logger"),T.isRequired(n,"protocol"),this.serverTimeoutInMilliseconds=O,this.logger=e,this.protocol=n,this.connection=t,this.handshakeProtocol=new E,this.connection.onreceive=function(t){return o.processIncomingData(t)},this.connection.onclose=function(t){return o.connectionClosed(t)},this.callbacks={},this.methods={},this.closedCallbacks=[],this.id=0}return t.create=function(e,n,o){return new t(e,n,o)},t.prototype.start=function(){return g(this,void 0,void 0,function(){var t;return f(this,function(e){switch(e.label){case 0:return t={protocol:this.protocol.name,version:this.protocol.version},this.logger.log(v.Debug,"Starting HubConnection."),this.receivedHandshakeResponse=!1,[4,this.connection.start(this.protocol.transferFormat)];case 1:return e.sent(),this.logger.log(v.Debug,"Sending handshake request."),[4,this.connection.send(this.handshakeProtocol.writeHandshakeRequest(t))];case 2:return e.sent(),this.logger.log(v.Information,"Using HubProtocol '"+this.protocol.name+"'."),this.cleanupTimeout(),this.configureTimeout(),[2]}})})},t.prototype.stop=function(){return this.logger.log(v.Debug,"Stopping HubConnection."),this.cleanupTimeout(),this.connection.stop()},t.prototype.stream=function(t){for(var e=this,n=[],o=1;o<arguments.length;o++)n[o-1]=arguments[o];var r=this.createStreamInvocation(t,n),i=new x(function(){var t=e.createCancelInvocation(r.invocationId),n=e.protocol.writeMessage(t);return delete e.callbacks[r.invocationId],e.connection.send(n)});this.callbacks[r.invocationId]=function(t,e){e?i.error(e):t.type===y.Completion?t.error?i.error(new Error(t.error)):i.complete():i.next(t.item)};var s=this.protocol.writeMessage(r);return this.connection.send(s).catch(function(t){i.error(t),delete e.callbacks[r.invocationId]}),i},t.prototype.send=function(t){for(var e=[],n=1;n<arguments.length;n++)e[n-1]=arguments[n];var o=this.createInvocation(t,e,!0),r=this.protocol.writeMessage(o);return this.connection.send(r)},t.prototype.invoke=function(t){for(var e=this,n=[],o=1;o<arguments.length;o++)n[o-1]=arguments[o];var r=this.createInvocation(t,n,!1);return new Promise(function(t,n){e.callbacks[r.invocationId]=function(e,o){if(o)n(o);else if(e.type===y.Completion){var r=e;r.error?n(new Error(r.error)):t(r.result)}else n(new Error("Unexpected message type: "+e.type))};var o=e.protocol.writeMessage(r);e.connection.send(o).catch(function(t){n(t),delete e.callbacks[r.invocationId]})})},t.prototype.on=function(t,e){t&&e&&(t=t.toLowerCase(),this.methods[t]||(this.methods[t]=[]),-1===this.methods[t].indexOf(e)&&this.methods[t].push(e))},t.prototype.off=function(t,e){if(t){t=t.toLowerCase();var n=this.methods[t];if(n)if(e){var o=n.indexOf(e);-1!==o&&(n.splice(o,1),0===n.length&&delete this.methods[t])}else delete this.methods[t]}},t.prototype.onclose=function(t){t&&this.closedCallbacks.push(t)},t.prototype.processIncomingData=function(t){if(this.cleanupTimeout(),this.receivedHandshakeResponse||(t=this.processHandshakeResponse(t),this.receivedHandshakeResponse=!0),t)for(var e=0,n=this.protocol.parseMessages(t,this.logger);e<n.length;e++){var o=n[e];switch(o.type){case y.Invocation:this.invokeClientMethod(o);break;case y.StreamItem:case y.Completion:var r=this.callbacks[o.invocationId];null!=r&&(o.type===y.Completion&&delete this.callbacks[o.invocationId],r(o));break;case y.Ping:break;case y.Close:this.logger.log(v.Information,"Close message received from server."),this.connection.stop(o.error?new Error("Server returned an error on close: "+o.error):null);break;default:this.logger.log(v.Warning,"Invalid message type: "+o.type)}}this.configureTimeout()},t.prototype.processHandshakeResponse=function(t){var e,n,o;try{n=(o=this.handshakeProtocol.parseHandshakeResponse(t))[0],e=o[1]}catch(t){var r="Error parsing handshake response: "+t;this.logger.log(v.Error,r);var i=new Error(r);throw this.connection.stop(i),i}if(e.error){r="Server returned handshake error: "+e.error;this.logger.log(v.Error,r),this.connection.stop(new Error(r))}else this.logger.log(v.Debug,"Server handshake complete.");return n},t.prototype.configureTimeout=function(){var t=this;this.connection.features&&this.connection.features.inherentKeepAlive||(this.timeoutHandle=setTimeout(function(){return t.serverTimeout()},this.serverTimeoutInMilliseconds))},t.prototype.serverTimeout=function(){this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server."))},t.prototype.invokeClientMethod=function(t){var e=this,n=this.methods[t.target.toLowerCase()];if(n){if(n.forEach(function(n){return n.apply(e,t.arguments)}),t.invocationId){var o="Server requested a response, which is not supported in this version of the client.";this.logger.log(v.Error,o),this.connection.stop(new Error(o))}}else this.logger.log(v.Warning,"No client method with the name '"+t.target+"' found.")},t.prototype.connectionClosed=function(t){var e=this,n=this.callbacks;this.callbacks={},Object.keys(n).forEach(function(e){(0,n[e])(void 0,t||new Error("Invocation canceled due to connection being closed."))}),this.cleanupTimeout(),this.closedCallbacks.forEach(function(n){return n.apply(e,[t])})},t.prototype.cleanupTimeout=function(){this.timeoutHandle&&clearTimeout(this.timeoutHandle)},t.prototype.createInvocation=function(t,e,n){if(n)return{arguments:e,target:t,type:y.Invocation};var o=this.id;return this.id++,{arguments:e,invocationId:o.toString(),target:t,type:y.Invocation}},t.prototype.createStreamInvocation=function(t,e){var n=this.id;return this.id++,{arguments:e,invocationId:n.toString(),target:t,type:y.StreamInvocation}},t.prototype.createCancelInvocation=function(t){return{invocationId:t,type:y.CancelInvocation}},t}();!function(t){t[t.None=0]="None",t[t.WebSockets=1]="WebSockets",t[t.ServerSentEvents=2]="ServerSentEvents",t[t.LongPolling=4]="LongPolling"}(P||(P={})),function(t){t[t.Text=1]="Text",t[t.Binary=2]="Binary"}(M||(M={}));var N=function(){function t(){this.isAborted=!1}return t.prototype.abort=function(){this.isAborted||(this.isAborted=!0,this.onabort&&this.onabort())},Object.defineProperty(t.prototype,"signal",{get:function(){return this},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"aborted",{get:function(){return this.isAborted},enumerable:!0,configurable:!0}),t}(),F=5e3,q=function(){function t(t,e,n,o,r){this.httpClient=t,this.accessTokenFactory=e||function(){return null},this.logger=n,this.pollAbort=new N,this.logMessageContent=o,this.shutdownTimeout=r||F}return Object.defineProperty(t.prototype,"pollAborted",{get:function(){return this.pollAbort.aborted},enumerable:!0,configurable:!0}),t.prototype.connect=function(t,e){return g(this,void 0,void 0,function(){var n,o,r,i,s;return f(this,function(a){switch(a.label){case 0:if(T.isRequired(t,"url"),T.isRequired(e,"transferFormat"),T.isIn(e,M,"transferFormat"),this.url=t,this.logger.log(v.Trace,"(LongPolling transport) Connecting"),e===M.Binary&&"string"!=typeof(new XMLHttpRequest).responseType)throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");return n={abortSignal:this.pollAbort.signal,headers:{},timeout:9e4},e===M.Binary&&(n.responseType="arraybuffer"),[4,this.accessTokenFactory()];case 1:return o=a.sent(),this.updateHeaderToken(n,o),i=t+"&_="+Date.now(),this.logger.log(v.Trace,"(LongPolling transport) polling: "+i),[4,this.httpClient.get(i,n)];case 2:return 200!==(s=a.sent()).statusCode?(this.logger.log(v.Error,"(LongPolling transport) Unexpected response code: "+s.statusCode),r=new m(s.statusText,s.statusCode),this.running=!1):this.running=!0,this.poll(this.url,n,r),[2,Promise.resolve()]}})})},t.prototype.updateHeaderToken=function(t,e){e?t.headers.Authorization="Bearer "+e:t.headers.Authorization&&delete t.headers.Authorization},t.prototype.poll=function(t,e,n){return g(this,void 0,void 0,function(){var o,r,i,s;return f(this,function(a){switch(a.label){case 0:a.trys.push([0,,8,9]),a.label=1;case 1:return this.running?[4,this.accessTokenFactory()]:[3,7];case 2:o=a.sent(),this.updateHeaderToken(e,o),a.label=3;case 3:return a.trys.push([3,5,,6]),r=t+"&_="+Date.now(),this.logger.log(v.Trace,"(LongPolling transport) polling: "+r),[4,this.httpClient.get(r,e)];case 4:return 204===(i=a.sent()).statusCode?(this.logger.log(v.Information,"(LongPolling transport) Poll terminated by server"),this.running=!1):200!==i.statusCode?(this.logger.log(v.Error,"(LongPolling transport) Unexpected response code: "+i.statusCode),n=new m(i.statusText,i.statusCode),this.running=!1):i.content?(this.logger.log(v.Trace,"(LongPolling transport) data received. "+I(i.content,this.logMessageContent)),this.onreceive&&this.onreceive(i.content)):this.logger.log(v.Trace,"(LongPolling transport) Poll timed out, reissuing."),[3,6];case 5:return s=a.sent(),this.running?s instanceof b?this.logger.log(v.Trace,"(LongPolling transport) Poll timed out, reissuing."):(n=s,this.running=!1):this.logger.log(v.Trace,"(LongPolling transport) Poll errored after shutdown: "+s.message),[3,6];case 6:return[3,1];case 7:return[3,9];case 8:return this.stopped=!0,this.shutdownTimer&&clearTimeout(this.shutdownTimer),this.onclose&&(this.logger.log(v.Trace,"(LongPolling transport) Firing onclose event. Error: "+(n||"<undefined>")),this.onclose(n)),this.logger.log(v.Trace,"(LongPolling transport) Transport finished."),[7];case 9:return[2]}})})},t.prototype.send=function(t){return g(this,void 0,void 0,function(){return f(this,function(e){return this.running?[2,L(this.logger,"LongPolling",this.httpClient,this.url,this.accessTokenFactory,t,this.logMessageContent)]:[2,Promise.reject(new Error("Cannot send until the transport is connected"))]})})},t.prototype.stop=function(){return g(this,void 0,void 0,function(){var t,e,n=this;return f(this,function(o){switch(o.label){case 0:return o.trys.push([0,,3,4]),this.running=!1,this.logger.log(v.Trace,"(LongPolling transport) sending DELETE request to "+this.url+"."),t={headers:{}},[4,this.accessTokenFactory()];case 1:return e=o.sent(),this.updateHeaderToken(t,e),[4,this.httpClient.delete(this.url,t)];case 2:return o.sent(),this.logger.log(v.Trace,"(LongPolling transport) DELETE request accepted."),[3,4];case 3:return this.stopped||(this.shutdownTimer=setTimeout(function(){n.logger.log(v.Warning,"(LongPolling transport) server did not terminate after DELETE request, canceling poll."),n.pollAbort.abort()},this.shutdownTimeout)),[7];case 4:return[2]}})})},t}(),H=function(){function t(t,e,n,o){this.httpClient=t,this.accessTokenFactory=e||function(){return null},this.logger=n,this.logMessageContent=o}return t.prototype.connect=function(t,e){return g(this,void 0,void 0,function(){var n,o=this;return f(this,function(r){switch(r.label){case 0:if(T.isRequired(t,"url"),T.isRequired(e,"transferFormat"),T.isIn(e,M,"transferFormat"),"undefined"==typeof EventSource)throw new Error("'EventSource' is not supported in your environment.");return this.logger.log(v.Trace,"(SSE transport) Connecting"),[4,this.accessTokenFactory()];case 1:return(n=r.sent())&&(t+=(t.indexOf("?")<0?"?":"&")+"access_token="+encodeURIComponent(n)),this.url=t,[2,new Promise(function(n,r){var i=!1;e!==M.Text&&r(new Error("The Server-Sent Events transport only supports the 'Text' transfer format"));var s=new EventSource(t,{withCredentials:!0});try{s.onmessage=function(t){if(o.onreceive)try{o.logger.log(v.Trace,"(SSE transport) data received. "+I(t.data,o.logMessageContent)+"."),o.onreceive(t.data)}catch(t){return void(o.onclose&&o.onclose(t))}},s.onerror=function(t){var e=new Error(t.message||"Error occurred");i?o.close(e):r(e)},s.onopen=function(){o.logger.log(v.Information,"SSE connected to "+o.url),o.eventSource=s,i=!0,n()}}catch(t){return Promise.reject(t)}})]}})})},t.prototype.send=function(t){return g(this,void 0,void 0,function(){return f(this,function(e){return this.eventSource?[2,L(this.logger,"SSE",this.httpClient,this.url,this.accessTokenFactory,t,this.logMessageContent)]:[2,Promise.reject(new Error("Cannot send until the transport is connected"))]})})},t.prototype.stop=function(){return this.close(),Promise.resolve()},t.prototype.close=function(t){this.eventSource&&(this.eventSource.close(),this.eventSource=null,this.onclose&&this.onclose(t))},t}(),U=function(){function t(t,e,n){this.logger=e,this.accessTokenFactory=t||function(){return null},this.logMessageContent=n}return t.prototype.connect=function(t,e){return g(this,void 0,void 0,function(){var n,o=this;return f(this,function(r){switch(r.label){case 0:if(T.isRequired(t,"url"),T.isRequired(e,"transferFormat"),T.isIn(e,M,"transferFormat"),"undefined"==typeof WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return this.logger.log(v.Trace,"(WebSockets transport) Connecting"),[4,this.accessTokenFactory()];case 1:return(n=r.sent())&&(t+=(t.indexOf("?")<0?"?":"&")+"access_token="+encodeURIComponent(n)),[2,new Promise(function(n,r){t=t.replace(/^http/,"ws");var i=new WebSocket(t);e===M.Binary&&(i.binaryType="arraybuffer"),i.onopen=function(e){o.logger.log(v.Information,"WebSocket connected to "+t),o.webSocket=i,n()},i.onerror=function(t){r(t.error)},i.onmessage=function(t){o.logger.log(v.Trace,"(WebSockets transport) data received. "+I(t.data,o.logMessageContent)+"."),o.onreceive&&o.onreceive(t.data)},i.onclose=function(t){o.logger.log(v.Trace,"(WebSockets transport) socket closed."),o.onclose&&(!1===t.wasClean||1e3!==t.code?o.onclose(new Error("Websocket closed with status code: "+t.code+" ("+t.reason+")")):o.onclose())}})]}})})},t.prototype.send=function(t){return this.webSocket&&this.webSocket.readyState===WebSocket.OPEN?(this.logger.log(v.Trace,"(WebSockets transport) sending data. "+I(t,this.logMessageContent)+"."),this.webSocket.send(t),Promise.resolve()):Promise.reject("WebSocket is not in the OPEN state")},t.prototype.stop=function(){return this.webSocket&&(this.webSocket.close(),this.webSocket=null),Promise.resolve()},t}(),A=function(){function t(t,e){void 0===e&&(e={}),this.features={},T.isRequired(t,"url"),this.logger=function(t){return void 0===t?new B(v.Information):null===t?k.instance:t.log?t:new B(t)}(e.logger),this.baseUrl=this.resolveUrl(t),(e=e||{}).accessTokenFactory=e.accessTokenFactory||function(){return null},e.logMessageContent=e.logMessageContent||!1,this.httpClient=e.httpClient||new S(this.logger),this.connectionState=2,this.options=e}return t.prototype.start=function(t){return t=t||M.Binary,T.isIn(t,M,"transferFormat"),this.logger.log(v.Debug,"Starting connection with transfer format '"+M[t]+"'."),2!==this.connectionState?Promise.reject(new Error("Cannot start a connection that is not in the 'Disconnected' state.")):(this.connectionState=0,this.startPromise=this.startInternal(t),this.startPromise)},t.prototype.send=function(t){if(1!==this.connectionState)throw new Error("Cannot send data if the connection is not in the 'Connected' State.");return this.transport.send(t)},t.prototype.stop=function(t){return g(this,void 0,void 0,function(){return f(this,function(e){switch(e.label){case 0:this.connectionState=2,e.label=1;case 1:return e.trys.push([1,3,,4]),[4,this.startPromise];case 2:return e.sent(),[3,4];case 3:return e.sent(),[3,4];case 4:return this.transport?(this.stopError=t,[4,this.transport.stop()]):[3,6];case 5:e.sent(),this.transport=null,e.label=6;case 6:return[2]}})})},t.prototype.startInternal=function(t){return g(this,void 0,void 0,function(){var e,n,o,r,i,s,a,c=this;return f(this,function(u){switch(u.label){case 0:e=this.baseUrl,this.accessTokenFactory=this.options.accessTokenFactory,u.label=1;case 1:return u.trys.push([1,12,,13]),this.options.skipNegotiation?this.options.transport!==P.WebSockets?[3,3]:(this.transport=this.constructTransport(P.WebSockets),[4,this.transport.connect(e,t)]):[3,5];case 2:return u.sent(),[3,4];case 3:throw Error("Negotiation can only be skipped when using the WebSocket transport directly.");case 4:return[3,11];case 5:n=null,o=0,r=function(){var t;return f(this,function(r){switch(r.label){case 0:return[4,i.getNegotiationResponse(e)];case 1:return n=r.sent(),2===i.connectionState?[2,{value:void 0}]:(n.url&&(e=n.url),n.accessToken&&(t=n.accessToken,i.accessTokenFactory=function(){return t}),o++,[2])}})},i=this,u.label=6;case 6:return[5,r()];case 7:if("object"==typeof(s=u.sent()))return[2,s.value];u.label=8;case 8:if(n.url&&o<100)return[3,6];u.label=9;case 9:if(100===o&&n.url)throw Error("Negotiate redirection limit exceeded.");return[4,this.createTransport(e,this.options.transport,n,t)];case 10:u.sent(),u.label=11;case 11:return this.transport instanceof q&&(this.features.inherentKeepAlive=!0),this.transport.onreceive=this.onreceive,this.transport.onclose=function(t){return c.stopConnection(t)},this.changeState(0,1),[3,13];case 12:throw a=u.sent(),this.logger.log(v.Error,"Failed to start the connection: "+a),this.connectionState=2,this.transport=null,a;case 13:return[2]}})})},t.prototype.getNegotiationResponse=function(t){return g(this,void 0,void 0,function(){var e,n,o,r,i,s;return f(this,function(a){switch(a.label){case 0:return[4,this.accessTokenFactory()];case 1:(e=a.sent())&&((s={}).Authorization="Bearer "+e,n=s),o=this.resolveNegotiateUrl(t),this.logger.log(v.Debug,"Sending negotiation request: "+o),a.label=2;case 2:return a.trys.push([2,4,,5]),[4,this.httpClient.post(o,{content:"",headers:n})];case 3:if(200!==(r=a.sent()).statusCode)throw Error("Unexpected status code returned from negotiate "+r.statusCode);return[2,JSON.parse(r.content)];case 4:throw i=a.sent(),this.logger.log(v.Error,"Failed to complete negotiation with the server: "+i),i;case 5:return[2]}})})},t.prototype.createConnectUrl=function(t,e){return t+(-1===t.indexOf("?")?"?":"&")+"id="+e},t.prototype.createTransport=function(t,e,n,o){return g(this,void 0,void 0,function(){var r,i,s,a,c,u,l;return f(this,function(h){switch(h.label){case 0:return r=this.createConnectUrl(t,n.connectionId),this.isITransport(e)?(this.logger.log(v.Debug,"Connection was provided an instance of ITransport, using that directly."),this.transport=e,[4,this.transport.connect(r,o)]):[3,2];case 1:return h.sent(),this.changeState(0,1),[2];case 2:i=n.availableTransports,s=0,a=i,h.label=3;case 3:return s<a.length?(c=a[s],this.connectionState=0,"number"!=typeof(u=this.resolveTransport(c,e,o))?[3,8]:(this.transport=this.constructTransport(u),null!==n.connectionId?[3,5]:[4,this.getNegotiationResponse(t)])):[3,9];case 4:n=h.sent(),r=this.createConnectUrl(t,n.connectionId),h.label=5;case 5:return h.trys.push([5,7,,8]),[4,this.transport.connect(r,o)];case 6:return h.sent(),this.changeState(0,1),[2];case 7:return l=h.sent(),this.logger.log(v.Error,"Failed to start the transport '"+P[u]+"': "+l),this.connectionState=2,n.connectionId=null,[3,8];case 8:return s++,[3,3];case 9:throw new Error("Unable to initialize any of the available transports.")}})})},t.prototype.constructTransport=function(t){switch(t){case P.WebSockets:return new U(this.accessTokenFactory,this.logger,this.options.logMessageContent);case P.ServerSentEvents:return new H(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent);case P.LongPolling:return new q(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent);default:throw new Error("Unknown transport: "+t+".")}},t.prototype.resolveTransport=function(t,e,n){var o=P[t.transport];if(null===o||void 0===o)this.logger.log(v.Debug,"Skipping transport '"+t.transport+"' because it is not supported by this client.");else{var r=t.transferFormats.map(function(t){return M[t]});if(function(t,e){return!t||0!=(e&t)}(e,o))if(r.indexOf(n)>=0){if(!(o===P.WebSockets&&"undefined"==typeof WebSocket||o===P.ServerSentEvents&&"undefined"==typeof EventSource))return this.logger.log(v.Debug,"Selecting transport '"+P[o]+"'"),o;this.logger.log(v.Debug,"Skipping transport '"+P[o]+"' because it is not supported in your environment.'")}else this.logger.log(v.Debug,"Skipping transport '"+P[o]+"' because it does not support the requested transfer format '"+M[n]+"'.");else this.logger.log(v.Debug,"Skipping transport '"+P[o]+"' because it was disabled by the client.")}return null},t.prototype.isITransport=function(t){return t&&"object"==typeof t&&"connect"in t},t.prototype.changeState=function(t,e){return this.connectionState===t&&(this.connectionState=e,!0)},t.prototype.stopConnection=function(t){return g(this,void 0,void 0,function(){return f(this,function(e){return this.transport=null,(t=this.stopError||t)?this.logger.log(v.Error,"Connection disconnected with error '"+t+"'."):this.logger.log(v.Information,"Connection disconnected."),this.connectionState=2,this.onclose&&this.onclose(t),[2]})})},t.prototype.resolveUrl=function(t){if(0===t.lastIndexOf("https://",0)||0===t.lastIndexOf("http://",0))return t;if("undefined"==typeof window||!window||!window.document)throw new Error("Cannot resolve '"+t+"'.");var e=window.document.createElement("a");return e.href=t,this.logger.log(v.Information,"Normalizing '"+t+"' to '"+e.href+"'."),e.href},t.prototype.resolveNegotiateUrl=function(t){var e=t.indexOf("?"),n=t.substring(0,-1===e?t.length:e);return"/"!==n[n.length-1]&&(n+="/"),n+="negotiate",n+=-1===e?"":t.substring(e)},t}();var W="json",_=function(){function t(){this.name=W,this.version=1,this.transferFormat=M.Text}return t.prototype.parseMessages=function(t,e){if("string"!=typeof t)throw new Error("Invalid input for JSON hub protocol. Expected a string.");if(!t)return[];null===e&&(e=k.instance);for(var n=[],o=0,r=C.parse(t);o<r.length;o++){var i=r[o],s=JSON.parse(i);if("number"!=typeof s.type)throw new Error("Invalid payload.");switch(s.type){case y.Invocation:this.isInvocationMessage(s);break;case y.StreamItem:this.isStreamItemMessage(s);break;case y.Completion:this.isCompletionMessage(s);break;case y.Ping:case y.Close:break;default:e.log(v.Information,"Unknown message type '"+s.type+"' ignored.");continue}n.push(s)}return n},t.prototype.writeMessage=function(t){return C.write(JSON.stringify(t))},t.prototype.isInvocationMessage=function(t){this.assertNotEmptyString(t.target,"Invalid payload for Invocation message."),void 0!==t.invocationId&&this.assertNotEmptyString(t.invocationId,"Invalid payload for Invocation message.")},t.prototype.isStreamItemMessage=function(t){if(this.assertNotEmptyString(t.invocationId,"Invalid payload for StreamItem message."),void 0===t.item)throw new Error("Invalid payload for StreamItem message.")},t.prototype.isCompletionMessage=function(t){if(t.result&&t.error)throw new Error("Invalid payload for Completion message.");!t.result&&t.error&&this.assertNotEmptyString(t.error,"Invalid payload for Completion message."),this.assertNotEmptyString(t.invocationId,"Invalid payload for Completion message.")},t.prototype.assertNotEmptyString=function(t,e){if("string"!=typeof t||""===t)throw new Error(e)},t}();!function(){function t(){}t.prototype.configureLogging=function(t){return T.isRequired(t,"logging"),!function(t){return void 0!==t.log}(t)?this.logger=new B(t):this.logger=t,this},t.prototype.withUrl=function(t,e){return T.isRequired(t,"url"),this.url=t,this.httpConnectionOptions="object"==typeof e?e:{transport:e},this},t.prototype.withHubProtocol=function(t){return T.isRequired(t,"protocol"),this.protocol=t,this},t.prototype.build=function(){var t=this.httpConnectionOptions||{};if(void 0===t.logger&&(t.logger=this.logger),!this.url)throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection.");var e=new A(this.url,t);return R.create(e,this.logger||k.instance,this.protocol||new _)}}();n(0);new class{constructor(t){this.currentUI=null,t.addObject(this,"uiMananger",["initAbout","initLogin","initPlay"]),this.iface=t}initAbout(){this.clearComponents(),this.about=new i(this.iface),this.currentUI="about"}initLogin(){this.clearComponents(),this.login=new u(this.iface),this.currentUI="login"}initPlay(){this.clearComponents(),this.play=new l(this.iface),this.currentUI="play"}clearComponents(){switch(this.currentUI){case null:return;case"about":this.about=null;break;case"login":this.login=null;break;case"play":this.play=null}this.currentUI=null}}(new class{constructor(){this.objects={}}addObject(t,e,n){this.objects[e]||(this.objects[e]=[]),this.objects[e].push(new o(t,n))}removeObject(t,e){this.objects[e]&&(objects[e]=objects[e].filter(e=>e.object!=t),0==objects[e].length&&(objects[e]=void 0))}callMethod(t,e,...n){if(!this.objects[t])return 1;let o=0;for(let r of this.objects[t])0!=r.execute(e,...n)&&(o=2);return o}}).initPlay()}]); \ No newline at end of file
diff --git a/WebInterface/NodeJSServer/src/about.js b/WebInterface/NodeJSServer/src/about.js
deleted file mode 100644
index 1561a2f..0000000
--- a/WebInterface/NodeJSServer/src/about.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Backdrop from './modules/ui/backdrop.js';
-
-let backdrop = new Backdrop('menu', 'front-layer', 'show-menu');
-backdrop.register();
diff --git a/WebInterface/NodeJSServer/src/index.js b/WebInterface/NodeJSServer/src/index.js
deleted file mode 100644
index 5b2e622..0000000
--- a/WebInterface/NodeJSServer/src/index.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import Backdrop from './modules/ui/backdrop.js';
-import BannerController from './modules/ui/notification-banner.js';
-import ServerClient from './modules/server-client.js';
-
-let backdrop = new Backdrop('menu', 'front-layer', 'show-menu');
-backdrop.register();
-
-let notifications = new BannerController('notifications',
- 'banner-info', 'dismiss-banner', 'notification-amount');
-notifications.register();
-
-let client = new ServerClient('http://127.0.0.1:5000/chatHub',
- 'server-list', notifications, [backdrop, notifications], true);
-document.getElementById('refresh-button')
- .addEventListener('click',
- client.loadServers.bind(client));
-
-window.client = client; // TODO: REMOVE, JUST FOR DEBUGGING
diff --git a/WebInterface/NodeJSServer/src/js/about.js b/WebInterface/NodeJSServer/src/js/about.js
new file mode 100644
index 0000000..23351cf
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/about.js
@@ -0,0 +1,6 @@
+import Interface from './modules/interface';
+import UIManager from './modules/ui/uiManager';
+
+let iface = new Interface();
+let uiMan = new UIManager(iface);
+uiMan.initAbout();
diff --git a/WebInterface/NodeJSServer/src/js/index.js b/WebInterface/NodeJSServer/src/js/index.js
new file mode 100644
index 0000000..6d84e7d
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/index.js
@@ -0,0 +1,12 @@
+import Interface from './modules/interface';
+import UIManager from './modules/ui/uiManager';
+import Networker from './modules/networking/networker';
+
+const SERVERURL = 'http://127.0.0.1:5000/chatHub';
+
+let iface = new Interface();
+let uiMan = new UIManager(iface);
+uiMan.initLogin();
+
+let netMan = new Networker(iface, SERVERURL, true); // TODO: Remove debug flag
+netMan.initLogin();
diff --git a/WebInterface/NodeJSServer/src/js/modules/interface.js b/WebInterface/NodeJSServer/src/js/modules/interface.js
new file mode 100644
index 0000000..ac5ea93
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/interface.js
@@ -0,0 +1,82 @@
+/**
+ * Stores an object and it's public methods
+ */
+class InterfaceAccessor {
+ /**
+ * Creates new accessor for object with publicMethods being exposed
+ * @param {object} object
+ * @param {array<String>} publicMethods
+ */
+ constructor(object, publicMethods) {
+ this.object = object;
+ this.publicMethods = publicMethods;
+ }
+
+ /**
+ * Executes method if it is a public method
+ * @param {string} method Name of method to call
+ * @return {number} 0 success, 1 method not public, 2 method not found
+ */
+ execute(method, ...args) {
+ if (!this.publicMethods.includes(method)) return 1;
+ if (typeof this.object[method] != 'function') return 2;
+
+ this.object[method](...args);
+ return 0;
+ }
+}
+
+/**
+ * Implements communication between objects
+ */
+export default class Interface {
+ /**
+ * Initializes interface
+ */
+ constructor() {
+ this.objects = {};
+ }
+
+ /**
+ * Adds a new object to array at objKey and assigns public methods
+ * @param {object} object Object to reference in Interface
+ * @param {String} objKey Key to reference the object under
+ * @param {Array} publicMethods Names of public methods
+ */
+ addObject(object, objKey, publicMethods) {
+ if (!this.objects[objKey]) this.objects[objKey] = [];
+ this.objects[objKey].push(new InterfaceAccessor(object, publicMethods));
+ }
+
+ /**
+ * Unregisters object
+ * @param {Object} object
+ * @param {String} objKey
+ */
+ removeObject(object, objKey) {
+ if (!this.objects[objKey]) return;
+
+ // Remove all instances of object from objKey
+ objects[objKey] = objects[objKey].filter(elt => elt.object != object);
+
+ // Remove reference, if none remain
+ if (objects[objKey].length == 0) objects[objKey] = undefined;
+ }
+
+ /**
+ * Calls a method on all objects with the key objKey
+ * @param {String} objKey Object Key of objects to call method on
+ * @param {String} method Method name to call on the objects
+ * @param {...*} args Arguments to pass
+ * @return {number} 0 Success, 1 no objects with objKey, 2 method not public
+ */
+ callMethod(objKey, method, ...args) {
+ if (!this.objects[objKey]) return 1;
+
+ let returnCode = 0;
+ for (let obj of this.objects[objKey]) {
+ if (obj.execute(method, ...args) != 0) returnCode = 2;
+ }
+ return returnCode;
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/networking/commands/_command.js b/WebInterface/NodeJSServer/src/js/modules/networking/commands/_command.js
new file mode 100644
index 0000000..46a1a14
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/networking/commands/_command.js
@@ -0,0 +1,28 @@
+/**
+ * Parent Command class which all commands inherit from
+ */
+export default class Command {
+ /**
+ * Constructs basic command object
+ * @param {Interface} iface Interface to communicate over
+ */
+ constructor(iface) {
+ this.iface = iface;
+ }
+
+ /**
+ * Registers public command names to interface
+ * @param {String} name Name to register under
+ * @param {...String} commandNames Names of public commands
+ */
+ registerPublic(name, ...commandNames) {
+ this.iface.addObject(this, name, ['destroy'].concat(commandNames));
+ }
+
+ /**
+ * Removes from iface
+ */
+ destroy() {
+ this.iface.removeObject(this);
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/createServer.js b/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/createServer.js
new file mode 100644
index 0000000..78b2a1b
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/createServer.js
@@ -0,0 +1,23 @@
+import Command from '../_command';
+
+/**
+ * Handles creation of Servers
+ */
+export default class CreateServer extends Command {
+ /**
+ * Registers interface for communication with other objects
+ * @param {Interface} iface
+ */
+ constructor(iface) {
+ super(iface);
+ this.registerPublic('createServer', 'createServer');
+ this.refreshing = false;
+ }
+
+ /**
+ * TODO:
+ */
+ createServer() {
+
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/listServers.js b/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/listServers.js
new file mode 100644
index 0000000..2c2bc11
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/listServers.js
@@ -0,0 +1,43 @@
+import Command from '../_command';
+
+/**
+ * Handles serverList commands
+ */
+export default class ListServers extends Command {
+ /**
+ * Registers interface for communication with other objects
+ * @param {Interface} iface
+ */
+ constructor(iface) {
+ super(iface);
+ this.registerPublic('listServers', 'listServers');
+ this.refreshing = false;
+ }
+
+ /**
+ * Requests server list from the server
+ */
+ listServers() {
+ if (this.refreshing) return; // If already refreshing, no new request
+
+ let listFn = (groups) => {
+ // Populate server listing
+ this.iface.callMethod('serverListing', 'flushElements');
+ this.iface.callMethod('serverListing', 'addElements', groups, this.iface);
+ // Unbind network function
+ this.iface.callMethod('networker', 'removeHandler', 'ListGroups');
+ this.refreshing = false;
+ };
+ let errorHandler = (err) => {
+ this.refreshing = false;
+ console.error(err.toString());
+ };
+
+ this.iface.callMethod('networker', 'registerHandler',
+ 'ListGroups', listFn);
+ this.iface.callMethod('networker', 'sendRequest',
+ 'GetGroups', errorHandler);
+
+ this.refreshing = true;
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/login.js b/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/login.js
new file mode 100644
index 0000000..44a6c94
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/networking/commands/login/login.js
@@ -0,0 +1,54 @@
+import Command from '../_command';
+import LoginModal from '../../../ui/components/modal/login-modal';
+
+/**
+ * Handles login to server
+ */
+export default class Login extends Command {
+ /**
+ * Registers interface for communication with other objects
+ * @param {Interface} iface
+ */
+ constructor(iface) {
+ super(iface);
+ this.registerPublic('login', 'sendLogin', 'showLogin');
+ this.refreshing = false;
+ }
+
+ /**
+ * Shows a login modal
+ * @param {String} name
+ */
+ showLogin(name) {
+ new LoginModal(this.iface, name);
+ }
+
+ /**
+ * Registers login response method
+ */
+ registerLoginResponse() {
+ this.iface.callMethod('networker', 'registerHandler', 'LoginResponse',
+ (result) => {
+ if (result == 0) {
+ this.iface.callMethod('modal', 'close');
+ this.iface.callMethod('router', 'routePlay');
+ this.iface.callMethod('networker', 'removeHandler',
+ 'LoginResponse');
+ } else {
+ this.iface.callMethod('modal', 'loginFailed', result);
+ }
+ });
+ }
+
+ /**
+ * Sends a login request
+ * @param {string} group Group name to join
+ * @param {string} password Password to send as SHA-256 Base64 String
+ * @param {string} username Display name to use
+ */
+ sendLogin(group, password, username) {
+ this.registerLoginResponse();
+ this.iface.callMethod('networker', 'sendRequest', 'Login',
+ (err) => console.error(err), group, username, password);
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/networking/commands/loginCmds.js b/WebInterface/NodeJSServer/src/js/modules/networking/commands/loginCmds.js
new file mode 100644
index 0000000..bc5d8a7
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/networking/commands/loginCmds.js
@@ -0,0 +1,36 @@
+import ListServers from './login/listServers';
+import CreateServer from './login/createServer';
+import Login from './login/login';
+
+/**
+ * Manages commands related to the login page
+ */
+export default class LoginCommands {
+ /**
+ * Initializes the login commands
+ * @param {Interface} iface Interface for inter-object communication
+ */
+ constructor(iface) {
+ this.iface = iface;
+ this.cmds = [];
+ this.registerCommands();
+ }
+
+ /**
+ * Registers all the available commands
+ */
+ registerCommands() {
+ this.cmds.push(new ListServers(this.iface));
+ this.cmds.push(new CreateServer(this.iface));
+ this.cmds.push(new Login(this.iface));
+ }
+
+ /**
+ * Destroys all attached commands
+ */
+ destroy() {
+ for (let cmd of this.cmds) {
+ cmd.destroy();
+ }
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/networking/commands/playCmds.js b/WebInterface/NodeJSServer/src/js/modules/networking/commands/playCmds.js
new file mode 100644
index 0000000..94cd6ba
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/networking/commands/playCmds.js
@@ -0,0 +1,31 @@
+// import ListServers from './login/listServers';
+
+/**
+ * Manages commands related to the login page
+ */
+export default class LoginCommands {
+ /**
+ * Initializes the login commands
+ * @param {Interface} iface Interface for inter-object communication
+ */
+ constructor(iface) {
+ this.iface = iface;
+ this.cmds = [];
+ }
+
+ /**
+ * Registers all the available commands
+ */
+ registerCommands() {
+ // this.cmds.push(new ListServers(iface));
+ }
+
+ /**
+ * Destroys all attached commands
+ */
+ destroy() {
+ for (let cmd of this.cmds) {
+ cmd.destroy();
+ }
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/modules/hash.js b/WebInterface/NodeJSServer/src/js/modules/networking/hash.js
index 3abcc21..3abcc21 100644
--- a/WebInterface/NodeJSServer/src/modules/hash.js
+++ b/WebInterface/NodeJSServer/src/js/modules/networking/hash.js
diff --git a/WebInterface/NodeJSServer/src/js/modules/networking/networker.js b/WebInterface/NodeJSServer/src/js/modules/networking/networker.js
new file mode 100644
index 0000000..7667895
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/networking/networker.js
@@ -0,0 +1,89 @@
+import * as signalR from '@aspnet/signalr';
+import LoginCommands from './commands/loginCmds';
+import PlayCommands from './commands/playCmds';
+
+/**
+ * Class for communication to server
+ */
+export default class Networker {
+ /**
+ * Creates new Networker and connects it to the Interface
+ * @param {Interface} iface Interface for communication between objects
+ * @param {String} url URL of the server backend
+ * @param {Boolean} [debug=false] Should there be debug output
+ */
+ constructor(iface, url, debug = false) {
+ this.url = url;
+
+ // Register in Interface
+ iface.addObject(this, 'networker',
+ ['sendRequest', 'registerHandler', 'removeHandler']);
+ this.iface = iface;
+
+ const connectionBuilder = new signalR.HubConnectionBuilder()
+ .withUrl(url);
+
+ if (debug) {
+ connectionBuilder.configureLogging(signalR.LogLevel.Debug);
+ } else {
+ connectionBuilder.configureLogging(signalR.LogLevel.Error);
+ }
+
+ this.connection = connectionBuilder.build();
+ this.connection.start()
+ .then(() => this.iface.callMethod('listServers', 'listServers'))
+ .catch((err) => console.error(err.toString()));
+
+ // Initialize refreshing (blocks new refreshes if true)
+ this.refreshing = false;
+ }
+
+ /**
+ * Sending a network request to the server
+ * @param {String} methodName Method to call on server
+ * @param {function} errorHandler Function to call on error
+ * @param {...*} args Arguments to pass to server
+ */
+ sendRequest(methodName, errorHandler, ...args) {
+ this.connection.invoke(methodName, ...args).catch(errorHandler);
+ }
+
+ /**
+ * Register a new function to be called upon receival of message from server
+ * @param {String} name Name of invoked method
+ * @param {function} fn function to call with received data
+ */
+ registerHandler(name, fn) {
+ this.connection.on(name, fn);
+ }
+
+ /**
+ * Removes handler for receiving messages from the server
+ * @param {String} name Name of the invoked method
+ */
+ removeHandler(name) {
+ this.connection.off(name);
+ }
+
+ /**
+ * Initializes Login Commands
+ */
+ initLogin() {
+ this.loginCmd = new LoginCommands(this.iface);
+ }
+
+ /**
+ * Initializes play commands
+ */
+ initPlay() {
+ this.playCmd = new PlayCommands(this.iface);
+ }
+
+ /**
+ * Clears all currently registered commands
+ */
+ clearCommands() {
+ if (this.loginCmd) this.loginCmd.destroy();
+ if (this.playCmd) this.playCmd.destroy();
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/ui/collections/about.js b/WebInterface/NodeJSServer/src/js/modules/ui/collections/about.js
new file mode 100644
index 0000000..dac8f01
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/collections/about.js
@@ -0,0 +1,14 @@
+import Backdrop from '../components/backdrop';
+
+/**
+ * UI Loader for about page
+ */
+export default class About {
+ /**
+ * Registers components for about page
+ */
+ constructor() {
+ this.backdrop = new Backdrop('menu', 'front-layer', 'show-menu');
+ this.backdrop.initialize();
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/ui/collections/login.js b/WebInterface/NodeJSServer/src/js/modules/ui/collections/login.js
new file mode 100644
index 0000000..98a6b30
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/collections/login.js
@@ -0,0 +1,24 @@
+import Backdrop from '../components/backdrop';
+import BannerController from '../components/notification-banner';
+import ServerListing from '../components/server-listing';
+
+/**
+ * UI Loader for login page
+ */
+export default class Login {
+ /**
+ * Registers components for login page
+ * @param {Interface} iface Interface to enable comm. with notifications
+ */
+ constructor(iface) {
+ this.backdrop = new Backdrop('menu', 'front-layer', 'show-menu');
+ this.bannerController = new BannerController(iface, 'notifications',
+ 'banner-info', 'dismiss-banner', 'notification-amount');
+ this.serverListing = new ServerListing(iface, 'server-list',
+ 'refresh-button');
+
+ this.backdrop.initialize();
+ this.bannerController.initialize();
+ this.serverListing.initialize();
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/ui/collections/play.js b/WebInterface/NodeJSServer/src/js/modules/ui/collections/play.js
new file mode 100644
index 0000000..cdea777
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/collections/play.js
@@ -0,0 +1,19 @@
+import Backdrop from '../components/backdrop';
+import BannerController from '../components/notification-banner';
+
+/**
+ * UI Loader for play page
+ */
+export default class Play {
+ /**
+ * Registers components for play page
+ */
+ constructor() {
+ this.backdrop = new Backdrop('menu', 'front-layer', 'show-menu');
+ this.bannerController = new BannerController(iface, 'notifications',
+ 'banner-info', 'dismiss-banner', 'notification-amount');
+
+ this.backdrop.initialize();
+ this.bannerController.initialize();
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/modules/ui/backdrop.js b/WebInterface/NodeJSServer/src/js/modules/ui/components/backdrop.js
index f89a3c9..82ca64f 100644
--- a/WebInterface/NodeJSServer/src/modules/ui/backdrop.js
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/components/backdrop.js
@@ -1,5 +1,3 @@
-// Showing / Hiding the backdrop menu
-
/**
* Class for adding functionality to backdrop elements
*/
@@ -12,27 +10,26 @@ export default class Backdrop {
*/
constructor(backdropMenu, frontLayer, menuButton) {
this.ids = {backdropMenu, frontLayer, menuButton};
- this.backdrop = document.getElementById(backdropMenu);
- this.frontLayer = document.getElementById(frontLayer);
- this.menuButton = document.getElementById(menuButton);
}
/**
- * Registers all neccessary events
+ * Initializes the components from the ids defined in the constructor
*/
- register() {
- this.registerButtonEvent();
- this.registerFrontLayerEvent();
+ initialize() {
+ this.open = false;
+ this.backdrop = document.getElementById(this.ids.backdropMenu);
+ this.frontLayer = document.getElementById(this.ids.frontLayer);
+ this.menuButton = document.getElementById(this.ids.menuButton);
+
+ this.registerEvents();
}
/**
- * Reloads the object
+ * Registers all neccessary events
*/
- refresh() {
- this.backdrop = document.getElementById(this.ids.backdropMenu);
- this.frontLayer = document.getElementById(this.ids.frontLayer);
- this.menuButton = document.getElementById(this.ids.menuButton);
- this.register();
+ registerEvents() {
+ this.registerButtonEvent();
+ this.registerFrontLayerEvent();
}
/**
@@ -40,28 +37,27 @@ export default class Backdrop {
*/
registerButtonEvent() {
this.menuButton.addEventListener('click', () => {
+ // Change open state
+ this.open = !this.open;
+
// Hide / Unhide Backdrop Menu
- if (this.backdrop.classList.contains('hidden')) {
- this.backdrop.classList.remove('hidden');
- } else {
- this.backdrop.classList.add('hidden');
- }
+ this.open ? this.backdrop.classList.remove('hidden') :
+ this.backdrop.classList.add('hidden');
// Set open state for menu button
- if (this.menuButton.classList.contains('open')) {
- this.menuButton.classList.remove('open');
- } else {
- this.menuButton.classList.add('open');
- }
+ this.open ? this.menuButton.classList.add('open') :
+ this.menuButton.classList.remove('open');
});
}
/**
- * Registers hiding, when clicking on the front layer
+ * Registers hiding upon front layer interaction
*/
registerFrontLayerEvent() {
- // Hide menu when interacting with front layer
this.frontLayer.addEventListener('click', () => {
+ if (!this.open) return; // It's already closed
+
+ this.open = false;
this.backdrop.classList.add('hidden');
this.menuButton.classList.remove('open');
});
diff --git a/WebInterface/NodeJSServer/src/js/modules/ui/components/modal/login-modal.js b/WebInterface/NodeJSServer/src/js/modules/ui/components/modal/login-modal.js
new file mode 100644
index 0000000..941fd84
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/components/modal/login-modal.js
@@ -0,0 +1,139 @@
+import Modal from './modal';
+import '../../../networking/hash';
+
+/**
+ * Class to implement a login modal from the parent modal class
+ */
+export default class LoginModal extends Modal {
+ /**
+ * Creates necessary elements for login modal
+ * @param {Interface} iface Interface for Interactions with other Objects
+ * @param {string} serverName Name of the server to connect to
+ */
+ constructor(iface, serverName) {
+ super(serverName);
+ this.serverName = serverName;
+
+ iface.addObject(this, 'modal', ['loginFailed', 'close']);
+ this.iface = iface;
+
+ let passBox = document.createElement('div');
+ let nameBox = document.createElement('div');
+ let sendBox = document.createElement('div');
+
+ let passwordLabel = document.createElement('label');
+ let passwordInput = document.createElement('input');
+ let passwordInvalid = document.createElement('span');
+ passwordLabel.setAttribute('for', 'password-input');
+ passwordLabel.textContent = 'Passwort:';
+ passwordLabel.title = 'Das Passwort des Spiels';
+ passwordInput.id = 'password-input';
+ passwordInput.type = 'password';
+ passwordInput.placeholder = 'Passwort';
+ passwordInput.title = 'Das Passwort des Spiels';
+ passwordInvalid.className = 'invalid hidden';
+ passwordInvalid.textContent = 'Das eingegebene Passwort ist falsch.';
+
+ let nameLabel = document.createElement('label');
+ let nameInput = document.createElement('input');
+ let nameInvalid = document.createElement('span');
+ nameLabel.setAttribute('for', 'name-input');
+ nameLabel.textContent = 'Benutzername:';
+ nameLabel.title = 'Dein Anzeigename';
+ nameInput.id = 'name-input';
+ nameInput.type = 'text';
+ nameInput.autocomplete = 'on';
+ nameInput.placeholder = 'Name';
+ nameInput.title = 'Dein Anzeigename';
+ nameInvalid.className = 'invalid hidden';
+ nameInvalid.textContent =
+ 'Der eingegebene Nutzername ist bereits vergeben.';
+
+ let sendButton = document.createElement('button');
+ sendButton.className = 'btn';
+ sendButton.textContent = 'Login';
+ sendButton.id = 'login-button';
+
+ passBox.appendChild(passwordLabel);
+ passBox.appendChild(passwordInput);
+ passBox.appendChild(passwordInvalid);
+ nameBox.appendChild(nameLabel);
+ nameBox.appendChild(nameInput);
+ nameBox.appendChild(nameInvalid);
+ sendBox.appendChild(sendButton);
+
+ this.body.appendChild(passBox);
+ this.body.appendChild(nameBox);
+ this.body.appendChild(sendBox);
+
+ this.nameInput = nameInput;
+ this.passwordInput = passwordInput;
+ this.loginButton = sendButton;
+
+ this.passwordInvalid = passwordInvalid;
+ this.nameInvalid = nameInvalid;
+
+ this.registerLoginBtnEvent();
+ }
+
+ /**
+ * Method that gets called, if login fails
+ * @param {number} result Error Code
+ */
+ loginFailed(result) {
+ if (result == 1) {
+ this.invalid('Name');
+ this.loginButton.addEventListener('click', this.event);
+ } else if (result == 2) {
+ this.invalid('Pass');
+ this.loginButton.addEventListener('click', this.event);
+ } else {
+ this.iface.callMethod('notifications', 'show', 'failed',
+ 'Ein unbekannter Fehler ist aufgetreten');
+ this.close();
+ }
+ }
+
+ /**
+ * Registers event to send login, on button press
+ */
+ registerLoginBtnEvent() {
+ this.event = () => {
+ this.invalid(); // Remove 'invalid' messages
+ this.loginButton.removeEventListener('click', this.event);
+ this.userName = this.nameInput.value;
+ this.passwordInput.value.getHash()
+ .then((result) => {
+ this.iface.callMethod('login', 'sendLogin', this.serverName,
+ result, this.userName);
+ });
+ };
+ this.loginButton.addEventListener('click', this.event);
+ }
+
+ /**
+ * Displays text under invalid password / username
+ * @param {string} inv Which field to display under (Pass / Name)
+ * Blank inv will hide both
+ */
+ invalid(inv) {
+ this.body.classList.remove('frst-warning');
+ this.body.classList.remove('scnd-warning');
+
+ this.passwordInvalid.classList.add('hidden');
+ this.nameInvalid.classList.add('hidden');
+
+ this.passwordInput.style.borderColor = 'none';
+ this.nameInput.style.borderColor = 'none';
+
+ if (inv == 'Pass') {
+ this.body.classList.add('frst-warning');
+ this.passwordInvalid.classList.remove('hidden');
+ this.passwordInput.style.borderColor = '#ef5350';
+ } else if (inv == 'Name') {
+ this.body.classList.add('scnd-warning');
+ this.nameInvalid.classList.remove('hidden');
+ this.nameInput.style.borderColor = '#ef5350';
+ }
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/modules/ui/modal.js b/WebInterface/NodeJSServer/src/js/modules/ui/components/modal/modal.js
index 10a1be5..10a1be5 100644
--- a/WebInterface/NodeJSServer/src/modules/ui/modal.js
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/components/modal/modal.js
diff --git a/WebInterface/NodeJSServer/src/modules/ui/notification-banner.js b/WebInterface/NodeJSServer/src/js/modules/ui/components/notification-banner.js
index 7e6b8cb..a527725 100644
--- a/WebInterface/NodeJSServer/src/modules/ui/notification-banner.js
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/components/notification-banner.js
@@ -1,48 +1,67 @@
/**
+ * Object containing a message for the notification banner
+ */
+class BannerItem {
+ /**
+ * Creates new Banner Message Items
+ * @param {String} name Name the message will be referenced under
+ * @param {String} content Content, either formatted as plain text or html
+ * @param {Boolean} html Is content formatted as html?
+ */
+ constructor(name, content, html) {
+ this.name = name;
+ this.content = content;
+ this.html = html;
+ }
+}
+
+/**
* Class for controlling the Notification banner
*/
export default class BannerController {
/**
* Creates references to objects and hides notification banner
+ * @param {Interface} iface Interface to receive comm. from
* @param {string} bannerId ID of Notification Banner
* @param {string} textP ID of Notification Banner text field
* @param {string} dismissBtn ID of dismiss button
- * @param {string} notificationBadge ID of badge (# of notifications)
+ * @param {string} badge ID of badge (# of notifications)
*/
- constructor(bannerId, textP, dismissBtn, notificationBadge) {
- this.ids = {bannerId, textP, dismissBtn, notificationBadge};
- this.banner = document.getElementById(bannerId);
- this.bannerText = document.getElementById(textP);
- this.dismissBtn = document.getElementById(dismissBtn);
- this.notificationBadge = document.getElementById(notificationBadge);
- this.bannerMsgs = [];
+ constructor(iface, bannerId, textP, dismissBtn, badge) {
+ iface.addObject(this, 'notifications', ['show', 'hide']);
+ this.iface = iface;
- // Hide Banner after JS loading finished
- this.banner.classList.add('hidden');
+ this.ids = {bannerId, textP, dismissBtn, badge};
}
/**
- * Registers dismissing via the dismiss button
+ * Initializes the Banner in the DOM
*/
- register() {
- this.dismissBtn.addEventListener('click', () => {
- this.dismissCurrent();
- });
- }
-
- /**
- * Reloads the object
- */
- refresh() {
+ initialize() {
this.banner = document.getElementById(this.ids.bannerId);
this.bannerText = document.getElementById(this.ids.textP);
this.dismissBtn = document.getElementById(this.ids.dismissBtn);
- this.notificationBadge = document.getElementById(
- this.ids.notificationBadge);
+ this.notificationBadge = document.getElementById(this.ids.badge);
this.bannerMsgs = [];
- // Hide Banner after JS loading finished
- this.banner.classList.add('hidden');
+ this.banner.classList.add('hidden'); // Hide banner by default
+ this.registerEvents();
+ }
+
+ /**
+ * Registers events for notification banner
+ */
+ registerEvents() {
+ this.registerDismissEvent();
+ }
+
+ /**
+ * Registers dismissing via dismiss button
+ */
+ registerDismissEvent() {
+ this.dismissBtn.addEventListener('click', () => {
+ this.dismissCurrent();
+ });
}
/**
@@ -51,7 +70,7 @@ export default class BannerController {
* @param {string} text Notification text
*/
show(name, text) {
- let bannerItem = {name, text, 'html': false};
+ let bannerItem = new BannerItem(name, text, false);
this.bannerMsgs.push(bannerItem);
this.update();
@@ -59,21 +78,13 @@ export default class BannerController {
/**
* Removes notification from banner
- * @param {string} name Name, that the notification was registered under
+ * @param {string} name The name the notification was registered under
*/
hide(name) {
- if (!name) {
- this.bannerMsgs = [];
- this.banner.classList.add('hidden');
- } else {
- for (let i = 0; i < this.bannerMsgs.length; i++) {
- if (this.bannerMsgs[i].name == name) {
- this.bannerMsgs.splice(i, 1);
- }
- }
+ if (name) this.bannerMsgs = this.bannerMsgs.filter(elt => elt.name != name);
+ else this.bannerMsgs = [];
- this.update();
- }
+ this.update();
}
/**
@@ -94,16 +105,21 @@ export default class BannerController {
const lastNotification = this.bannerMsgs[this.bannerMsgs.length - 1];
const name = lastNotification.name;
- const text = lastNotification.text;
- const html = lastNotification.html;
+ const text = lastNotification.content;
+ const isHtml = lastNotification.html;
this.banner.classList.remove('hidden');
- if (html) this.bannerText.innerHTML = text;
+ if (isHtml) this.bannerText.innerHTML = text;
else this.bannerText.innerText = text;
this.current = name;
+ this.updateNotificationBadge();
+ }
- // Update notification badge
+ /**
+ * Updates the notification badge number
+ */
+ updateNotificationBadge() {
if (this.bannerMsgs.length < 2) {
this.notificationBadge.classList.add('hidden');
} else if (this.bannerMsgs.length > 9) {
diff --git a/WebInterface/NodeJSServer/src/js/modules/ui/components/router.js b/WebInterface/NodeJSServer/src/js/modules/ui/components/router.js
new file mode 100644
index 0000000..c01c21b
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/components/router.js
@@ -0,0 +1,44 @@
+/**
+ * Class for routing between pages
+ */
+export default class Router {
+ /**
+ * @param {Interface} iface Interface for comm. with other objects
+ */
+ constructor(iface) {
+ iface.addObject(this, 'serverListing', ['routePlay']);
+ this.iface = iface;
+ }
+
+ /**
+ * Routes to the play page
+ * @param {HubConnection} connection Connection to the server
+ */
+ routePlay(connection) {
+ window.history.pushState('object or string', 'Game Page',
+ 'play#game=' + this.serverName);
+ fetch('play').then((response) => {
+ response.text().then((htmlString) => {
+ // Replace all references, since we're starting one level farther up
+ htmlString = htmlString.replace(/\.\.\//g, './');
+ htmlString = /<body>((.)|(\n))*<\/body>/g.exec(htmlString)[0];
+ htmlString = htmlString.replace(/<script src=".*"><\/script>/, '');
+ htmlString = htmlString.replace(
+ /<remove_if_redirected>((.)|\n)*?<\/remove_if_redirected>/g, '');
+ document.body.innerHTML = htmlString;
+
+ let stylesheet = document.createElement('link');
+ stylesheet.rel = 'stylesheet';
+ stylesheet.type = 'text/css';
+ stylesheet.href = './style/play.css';
+ document.head.appendChild(stylesheet);
+
+
+ this.iface.callMethod('uiMananger', 'initPlay');
+ for (let ui of this.pageUI) {
+ ui.refresh();
+ }
+ });
+ });
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/ui/components/server-listing.js b/WebInterface/NodeJSServer/src/js/modules/ui/components/server-listing.js
new file mode 100644
index 0000000..2af56ac
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/components/server-listing.js
@@ -0,0 +1,91 @@
+/**
+ * Class for handling the server list
+ */
+export default class ServerListing {
+ /**
+ * Creates reference to container
+ * @param {Interface} iface Interface for comm. with other objects
+ * @param {string} serverListId ID of the server list div
+ * @param {string} refreshBtnId ID of the refresh btn
+ */
+ constructor(iface, serverListId, refreshBtnId) {
+ this.ids = {serverListId, refreshBtnId};
+
+ iface.addObject(this, 'serverListing', ['flushElements', 'addElements']);
+ this.iface = iface;
+ }
+
+ /**
+ * Initializes Server List DOM Element
+ */
+ initialize() {
+ this.serverListing = document.getElementById(this.ids.serverListId);
+ this.refreshBtn = document.getElementById(this.ids.refreshBtnId);
+ this.registerEvents();
+ }
+
+ /**
+ * Registers events associated with server list UI
+ */
+ registerEvents() {
+ this.registerRefreshEvent();
+ }
+
+ /**
+ * Registers event for pushing the refresh button
+ */
+ registerRefreshEvent() {
+ this.refreshBtn.addEventListener('click', () => {
+ this.iface.callMethod('listServers', 'listServers');
+ });
+ }
+
+ /**
+ * Removes all elements currently in the server listing
+ */
+ flushElements() {
+ this.serverListing.innerHTML = '';
+ }
+
+ /**
+ * Populates servers from a given array of games
+ * @param {array} array Array of available games
+ */
+ addElements(array) {
+ for (let server of array) {
+ const name = server['name'];
+ const playerAmount = server['userCount'];
+
+ let serverDiv = document.createElement('div');
+ let nameSpan = document.createElement('span');
+ let rightAlignDiv = document.createElement('div');
+ let onlineDot = document.createElement('div');
+ let playerCountSpan = document.createElement('span');
+ let playerCountStaticSpan = document.createElement('span');
+ let joinButton = document.createElement('button');
+ serverDiv.className = 'server';
+ nameSpan.className = 'server-name';
+ rightAlignDiv.className = 'right-aligned-items';
+ onlineDot.className = 'player-count-dot';
+ playerCountSpan.className = 'player-count';
+ playerCountStaticSpan.className = 'player-count-static';
+ joinButton.className = 'btn join-btn';
+ joinButton.id = 'join';
+ nameSpan.textContent = name;
+ playerCountSpan.textContent = playerAmount;
+ playerCountStaticSpan.textContent = 'Spieler online';
+ joinButton.textContent = 'Beitreten';
+ joinButton.addEventListener('click', () => {
+ this.iface.callMethod('login', 'showLogin', name);
+ });
+
+ rightAlignDiv.appendChild(onlineDot);
+ rightAlignDiv.appendChild(playerCountSpan);
+ rightAlignDiv.appendChild(playerCountStaticSpan);
+ rightAlignDiv.appendChild(joinButton);
+ serverDiv.appendChild(nameSpan);
+ serverDiv.appendChild(rightAlignDiv);
+ this.serverListing.appendChild(serverDiv);
+ }
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/modules/ui/uiManager.js b/WebInterface/NodeJSServer/src/js/modules/ui/uiManager.js
new file mode 100644
index 0000000..ddbb152
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/modules/ui/uiManager.js
@@ -0,0 +1,60 @@
+import About from './collections/about';
+import Login from './collections/login';
+import Play from './collections/play';
+
+/**
+ * Controller class for Page UI
+ */
+export default class UIManager {
+ /**
+ * Initializes new UI Manager
+ * @param {Interface} iface Interface for inter-object communication
+ */
+ constructor(iface) {
+ this.currentUI = null;
+
+ iface.addObject(this, 'uiMananger', ['initAbout', 'initLogin', 'initPlay']);
+ this.iface = iface;
+ }
+
+ /**
+ * Initializes UI Components of About Page
+ */
+ initAbout() {
+ this.clearComponents();
+ this.about = new About(this.iface);
+ this.currentUI = 'about';
+ }
+
+ /**
+ * Initializes UI Components of Login page
+ */
+ initLogin() {
+ this.clearComponents();
+ this.login = new Login(this.iface);
+ this.currentUI = 'login';
+ }
+
+ /**
+ * Initializes UI Components of Play page
+ */
+ initPlay() {
+ this.clearComponents();
+ this.play = new Play(this.iface);
+ this.currentUI = 'play';
+ }
+
+ /**
+ * Clears currently loaded components
+ */
+ clearComponents() {
+ switch (this.currentUI) {
+ case null: return;
+ case 'about': this.about = null; break;
+ case 'login': this.login = null; break;
+ case 'play': this.play = null; break;
+ }
+
+ this.currentUI = null;
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/js/play.js b/WebInterface/NodeJSServer/src/js/play.js
new file mode 100644
index 0000000..f7cb255
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/play.js
@@ -0,0 +1,12 @@
+import Interface from './modules/interface';
+import UIManager from './modules/ui/uiManager';
+import Networker from './modules/networking/networker';
+
+const SERVERURL = 'http://127.0.0.1:5000/chatHub';
+
+let iface = new Interface();
+let uiMan = new UIManager(iface);
+uiMan.initPlay();
+// Create Network Manager as well
+
+// TODO: Implement login from the play page
diff --git a/WebInterface/NodeJSServer/src/modules/playModule.js b/WebInterface/NodeJSServer/src/js/src_old/modules/playModule.js
index 931f598..931f598 100644
--- a/WebInterface/NodeJSServer/src/modules/playModule.js
+++ b/WebInterface/NodeJSServer/src/js/src_old/modules/playModule.js
diff --git a/WebInterface/NodeJSServer/src/modules/server-client.js b/WebInterface/NodeJSServer/src/js/src_old/modules/server-client.js
index 2f712b5..2f712b5 100644
--- a/WebInterface/NodeJSServer/src/modules/server-client.js
+++ b/WebInterface/NodeJSServer/src/js/src_old/modules/server-client.js
diff --git a/WebInterface/NodeJSServer/src/modules/ui/login-modal.js b/WebInterface/NodeJSServer/src/js/src_old/modules/ui/login-modal.js
index 13de78e..13de78e 100644
--- a/WebInterface/NodeJSServer/src/modules/ui/login-modal.js
+++ b/WebInterface/NodeJSServer/src/js/src_old/modules/ui/login-modal.js
diff --git a/WebInterface/NodeJSServer/src/js/src_old/modules/ui/modal.js b/WebInterface/NodeJSServer/src/js/src_old/modules/ui/modal.js
new file mode 100644
index 0000000..10a1be5
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/js/src_old/modules/ui/modal.js
@@ -0,0 +1,67 @@
+/**
+ * Parent class to create Modals on the screen
+ * Contains no content, as that is implemented by child classes
+ */
+export default class Modal {
+ /**
+ * Creates a new modal with a title and empty content
+ * @param {string} titleString Title to show at the top of the modal
+ */
+ constructor(titleString) {
+ let modalBackground = document.createElement('div');
+ let modal = document.createElement('div');
+ let title = document.createElement('h1');
+ let body = document.createElement('div');
+
+ modalBackground.className = 'modal-container';
+ modal.className = 'modal';
+ title.className = 'modal-title';
+ body.className = 'modal-body';
+
+ title.textContent = titleString;
+
+ modal.appendChild(title);
+ modal.appendChild(body);
+ modalBackground.appendChild(modal);
+ document.body.appendChild(modalBackground);
+
+ this.bg = modalBackground;
+ this.modal = modal;
+ this.title = title;
+ this.body = body;
+
+ this.registerEvents();
+ }
+
+ /**
+ * Register event to close if clicked outside of modal
+ * Clicking on the modal itself should not close it though
+ */
+ registerEvents() {
+ this.modal.addEventListener('click', (e) => {
+ e.stopPropagation();
+ });
+
+ this.bg.addEventListener('click', () => {
+ this.close();
+ });
+ }
+
+ /**
+ * Fades modal out and removes it from the flow of the document
+ */
+ close() {
+ this.bg.classList.add('hidden');
+ this.bg.addEventListener('transitionend', () => {
+ document.body.removeChild(this.bg);
+ });
+ }
+
+ /**
+ * Puts text in the body
+ * @param {string} text Text to put into the body
+ */
+ setBodyText(text) {
+ this.body.textContent = text;
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/modules/ui/server-listing.js b/WebInterface/NodeJSServer/src/js/src_old/modules/ui/server-listing.js
index 78ca323..78ca323 100644
--- a/WebInterface/NodeJSServer/src/modules/ui/server-listing.js
+++ b/WebInterface/NodeJSServer/src/js/src_old/modules/ui/server-listing.js
diff --git a/WebInterface/NodeJSServer/src/play.js b/WebInterface/NodeJSServer/src/play.js
deleted file mode 100644
index 1e13355..0000000
--- a/WebInterface/NodeJSServer/src/play.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Backdrop from './modules/ui/backdrop.js';
-import BannerController from './modules/ui/notification-banner.js';
-
-// TODO: Implement login from the play page
-
-let backdrop = new Backdrop('menu', 'front-layer', 'show-menu');
-backdrop.register();
-
-let notifications = new BannerController('notifications',
- 'banner-info', 'dismiss-banner', 'notification-amount');
-notifications.register();
diff --git a/WebInterface/NodeJSServer/webpack.config.js b/WebInterface/NodeJSServer/webpack.config.js
index a3712c3..4b408a2 100644
--- a/WebInterface/NodeJSServer/webpack.config.js
+++ b/WebInterface/NodeJSServer/webpack.config.js
@@ -5,7 +5,7 @@ module.exports = [
{
mode: 'production',
entry: {
- index: './src/index.js',
+ index: './src/js/index.js',
},
output: {
filename: '[name].js',
@@ -16,7 +16,7 @@ module.exports = [
}, {
mode: 'production',
entry: {
- about: './src/about.js',
+ about: './src/js/about.js',
},
output: {
filename: '[name].js',
@@ -27,7 +27,7 @@ module.exports = [
}, {
mode: 'production',
entry: {
- play: './src/play.js',
+ play: './src/js/play.js',
},
output: {
filename: '[name].js',