From 76c28a5de1e14d452df2ca533c0762524ec6906b Mon Sep 17 00:00:00 2001 From: zumbiepig <121742281+zumbiepig@users.noreply.github.com> Date: Fri, 6 Sep 2024 18:07:55 -0700 Subject: [PATCH] step 2 --- .gitignore | 5 +- build.ts | 84 +- build_fast.ts | 49 - bun.lockb | Bin 113007 -> 113083 bytes eslint.config.js | 1 - package.json | 12 +- index.ts => server.ts | 0 src/game/web/main/1.2.5/index.html | 13305 ---------------- src/home/game/index.html | 2 +- .../scripts/eagler-launch/1.5.2/main.ts | 27 + .../eagler-launch/1.8.8/eaglerforge.ts | 45 + .../eagler-launch/1.8.8/eaglermobile-ef.ts | 688 + .../eagler-launch/1.8.8/eaglerpocketmobile.ts | 1156 ++ .../scripts/eagler-launch/1.8.8/main.ts | 26 + .../scripts/eagler-launch/1.9.4/main.ts | 21 + .../scripts/eagler-launch/b1.3/main.ts | 10 + src/resources/scripts/google-tag.ts | 12 + src/resources/scripts/main.ts | 631 + tsconfig.json | 7 +- 19 files changed, 2678 insertions(+), 13403 deletions(-) delete mode 100644 build_fast.ts rename index.ts => server.ts (100%) delete mode 100644 src/game/web/main/1.2.5/index.html create mode 100644 src/resources/scripts/eagler-launch/1.5.2/main.ts create mode 100644 src/resources/scripts/eagler-launch/1.8.8/eaglerforge.ts create mode 100644 src/resources/scripts/eagler-launch/1.8.8/eaglermobile-ef.ts create mode 100644 src/resources/scripts/eagler-launch/1.8.8/eaglerpocketmobile.ts create mode 100644 src/resources/scripts/eagler-launch/1.8.8/main.ts create mode 100644 src/resources/scripts/eagler-launch/1.9.4/main.ts create mode 100644 src/resources/scripts/eagler-launch/b1.3/main.ts create mode 100644 src/resources/scripts/google-tag.ts create mode 100644 src/resources/scripts/main.ts diff --git a/.gitignore b/.gitignore index aba28fd..6292299 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,7 @@ package-lock.json desktop.ini -public/resources/data/assets.json -public/resources/scripts/ -public/sw.js -public/sw-full.js +public/ # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore diff --git a/build.ts b/build.ts index ea3a3ef..6a73e9f 100644 --- a/build.ts +++ b/build.ts @@ -1,8 +1,11 @@ -import { $, build } from 'bun'; -import { mkdirSync, readdirSync, statSync, writeFileSync, readFileSync } from 'fs'; -import { resolve, dirname } from 'path'; -import chalk from 'chalk'; +import { build } from 'bun'; +import { mkdirSync, readdirSync, statSync, writeFileSync, readFileSync, rmSync } from 'fs'; +import { resolve, dirname, basename } from 'path'; import { minify } from 'html-minifier'; +import javascriptObfuscator from 'javascript-obfuscator'; +import chalk from 'chalk'; + +console.clear(); function getFiles(baseDir: string, dir?: string, filesArr?: string[]) { dir = dir ?? baseDir; @@ -19,32 +22,31 @@ function getFiles(baseDir: string, dir?: string, filesArr?: string[]) { return filesArr; } -console.clear(); - +const isDev = process.env.NODE_ENV === 'development' ? true : false; const srcDir = resolve(import.meta.dir, 'src'); const publicDir = resolve(import.meta.dir, 'public'); -const srcFilesArr = getFiles(resolve(import.meta.dir, 'src')); +const srcFiles = getFiles(resolve(import.meta.dir, 'src')); +const bundleFiles: string[] = []; +const minifyFiles: string[] = []; +const copyFiles: string[] = []; +srcFiles.forEach((file) => { + const strippedPath = file.replace(new RegExp(`^${srcDir}`), ''); + if (file.endsWith('.ts')) bundleFiles.push(file); + else if (isDev) copyFiles.push(file); + else if (/\.(html|css|js|json)$/.test(strippedPath)) { + if (strippedPath.startsWith('/game/offline/') || basename(strippedPath) === 'classes.js') copyFiles.push(file); + else minifyFiles.push(file); + } else copyFiles.push(file); +}); -console.log(chalk.cyan('Linting code...\n')); -//const lintOutput = await $`bunx eslint ./src/`.nothrow().text(); -//if (lintOutput) { -// console.error(lintOutput); -// process.exit(1); -//} - -console.log(chalk.cyan('Type-checking code...\n')); -//const tscOutput = await $`bunx tsc`.nothrow().text(); -//if (tscOutput) { -// console.error(tscOutput); -// process.exit(1); -//} - -console.log(chalk.cyan('Removing old build artifacts...\n')); -await $`rm -rf ./public/resources/scripts/ ./public/resources/data/assets.json ./public/sw.js ./public/sw-full.js`.quiet(); +if (!isDev) { + console.log(chalk.cyan('Removing old build artifacts...\n')); + rmSync(publicDir, { force: true, recursive: true }); +} console.log(chalk.cyan('Bundling TypeScript and modules...\n')); await build({ - entrypoints: srcFilesArr.filter((file) => file.endsWith('.ts')), + entrypoints: srcFiles.filter((file) => file.endsWith('.ts')), outdir: './public/', target: 'browser', root: './src/', @@ -55,10 +57,9 @@ await build({ }, }); -console.log(chalk.cyan('Minifying HTML and CSS...\n')); -srcFilesArr - .filter((file) => !file.endsWith('.ts')) - .forEach((file) => { +if (!isDev) { + console.log(chalk.cyan('Minifying HTML, JS, CSS, and JSON...\n')); + minifyFiles.forEach((file) => { const outputPath = file.replace(new RegExp(`^${srcDir}`), publicDir); mkdirSync(dirname(outputPath), { recursive: true }); writeFileSync( @@ -68,16 +69,37 @@ srcFilesArr removeComments: true, minifyCSS: true, minifyJS: true, + continueOnParseError: true, }) ); }); -console.log(chalk.cyan('Obfuscating JavaScript...\n')); -await $`bunx javascript-obfuscator ./public/resources/scripts/ --output ./public/resources/scripts/ --options-preset high-obfuscation`.quiet(); + console.log(chalk.cyan('Obfuscating JavaScript...\n')); + getFiles(resolve(publicDir, 'resources/scripts')).forEach((file) => { + writeFileSync( + file, + javascriptObfuscator + .obfuscate(readFileSync(file, 'utf-8'), { + optionsPreset: 'high-obfuscation', + target: 'browser', + }) + .getObfuscatedCode() + ); + }); +} + +console.log(chalk.cyan('Copying other files...\n')); +copyFiles.forEach((file) => { + const outputPath = file.replace(new RegExp(`^${srcDir}`), publicDir); + mkdirSync(dirname(outputPath), { recursive: true }); + writeFileSync(outputPath, readFileSync(file)); +}); console.log(chalk.cyan('Generating assets list...\n')); +const assetsJsonPath = resolve(publicDir, 'resources/data/assets.json'); +mkdirSync(dirname(assetsJsonPath), { recursive: true }); writeFileSync( - resolve(publicDir, 'resources/data/assets.json'), + assetsJsonPath, JSON.stringify( getFiles(publicDir).map((asset) => { return asset.replace(new RegExp(`^${publicDir}`), '').replace(/\/index\.html$/, '/'); diff --git a/build_fast.ts b/build_fast.ts deleted file mode 100644 index 275721f..0000000 --- a/build_fast.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { $, build } from 'bun'; -import { readdirSync, statSync, writeFileSync } from 'fs'; -import { join } from 'path'; -import chalk from 'chalk'; - -function getFiles(baseDir: string, dir?: string, filesArr?: string[]) { - dir = dir ?? baseDir; - filesArr = filesArr ?? []; - const files = readdirSync(dir); - for (const file of files) { - const name = join(dir, file); - if (statSync(name).isDirectory()) { - getFiles(baseDir, name, filesArr); - } else { - filesArr.push(name); - } - } - return filesArr; -} - -console.log(chalk.cyan('Removing old build artifacts...\n')); -await $`rm -rf ./public/resources/scripts/ ./public/resources/data/assets.json ./public/sw.js ./public/sw-full.js`.quiet(); - -console.log(chalk.cyan('Bundling TypeScript and modules...\n')); -const srcFilesArr = getFiles(join(import.meta.dir, 'src')); -await build({ - entrypoints: srcFilesArr, - outdir: './public/', - target: 'browser', - root: './src/', - minify: { - syntax: true, - whitespace: true, - identifiers: false, - }, -}); - -console.log(chalk.cyan('Generating assets list...\n')); -const publicDir = join(import.meta.dir, 'public'); -writeFileSync( - join(publicDir, 'resources/data/assets.json'), - JSON.stringify( - getFiles(publicDir).map((asset) => { - return asset.replace(new RegExp(`^${publicDir}`), '').replace(/\/index\.html$/, '/'); - }) - ) -); - -console.log(chalk.green('Build complete!\n')); diff --git a/bun.lockb b/bun.lockb index f031ae4b51d088ce9cc9042022023a75728f17ff..c3ae02ada466f968d674d2e9c76e8ff62b38632a 100755 GIT binary patch delta 12200 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 N%x8b{9@fqAe*k3jUEBZw delta 11903 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 z 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 V#vBiRwv1Ij$3hNH-OCaj{|ATUBG~`{ diff --git a/eslint.config.js b/eslint.config.js index 1eb2c7c..3258652 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -5,7 +5,6 @@ export default tseslint.config( eslint.configs.recommended, tseslint.configs.eslintRecommended, ...tseslint.configs.strict, - // ...tseslint.configs.strictTypeChecked, ...tseslint.configs.stylisticTypeChecked, { languageOptions: { diff --git a/package.json b/package.json index bc0a8a8..cdeda9b 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "name": "minexlauncher", "type": "module", "scripts": { - "start": "NODE_ENV=production bun run ./index.ts", - "dev": "NODE_ENV=development DEBUG=app:* bun --hot run ./index.ts", - "lint": "eslint ./src/", - "lint:fix": "eslint --fix ./src/", - "build": "bun run ./build.ts", - "build:fast": "bun run ./build_fast.ts" + "start": "NODE_ENV=production bun run ./server.ts", + "dev": "NODE_ENV=development DEBUG=minexlauncher:* bun --hot run ./server.ts", + "lint": "eslint ./src/ && tsc", + "lint:fix": "eslint --fix ./src/ && tsc", + "build": "NODE_ENV=production bun run ./build.ts", + "build:fast": "NODE_ENV=development bun run ./build.ts" }, "dependencies": { "chalk": "^5.3.0", diff --git a/index.ts b/server.ts similarity index 100% rename from index.ts rename to server.ts diff --git a/src/game/web/main/1.2.5/index.html b/src/game/web/main/1.2.5/index.html deleted file mode 100644 index e099dfc..0000000 --- a/src/game/web/main/1.2.5/index.html +++ /dev/null @@ -1,13305 +0,0 @@ - - - - Eaglercraft 1.2.5 - - - - - - - - - - - \ No newline at end of file diff --git a/src/home/game/index.html b/src/home/game/index.html index ce86de3..59cd360 100644 --- a/src/home/game/index.html +++ b/src/home/game/index.html @@ -82,7 +82,7 @@ 1.5.2 -
+
1.2.5
diff --git a/src/resources/scripts/eagler-launch/1.5.2/main.ts b/src/resources/scripts/eagler-launch/1.5.2/main.ts new file mode 100644 index 0000000..49b42e0 --- /dev/null +++ b/src/resources/scripts/eagler-launch/1.5.2/main.ts @@ -0,0 +1,27 @@ +// @ts-nocheck +window.addEventListener('load', function () { + const relayId = Math.floor(Math.random() * 3); + window.eaglercraftOpts = { + container: 'game_frame', + assetsURI: `${window.location.pathname}/assets.epk`, + localesURI: `${window.location.pathname}/lang/`, + serverWorkerURI: `${window.location.pathname}/worker_bootstrap.js`, + worldsFolder: 'MAIN', + servers: [{ serverName: 'BrickMC', serverAddress: 'wss://play.brickmc.net', hideAddress: false }], + relays: [ + { addr: 'wss://relay.deev.is/', name: 'lax1dude relay #1', primary: relayId === 0 }, + { addr: 'wss://relay.lax1dude.net/', name: 'lax1dude relay #2', primary: relayId === 1 }, + { addr: 'wss://relay.shhnowisnottheti.me/', name: 'ayunami relay #1', primary: relayId === 2 }, + ], + mainMenu: { + splashes: ['Darviglet!', 'eaglerenophile!', 'You Eagler!', 'Yeeeeeee!', 'yeee', 'EEEEEEEEE!', 'You Darvig!', 'You Vigg!', ':>', '|>', 'You Yumpster!'], + eaglerLogo: false, + }, + }; + + const urlParams = new URLSearchParams(window.location.search); + window.eaglercraftOpts.joinServer = urlParams.get('server') ?? undefined; + + history.replaceState({}, '', '/play'); + main(); +}); diff --git a/src/resources/scripts/eagler-launch/1.8.8/eaglerforge.ts b/src/resources/scripts/eagler-launch/1.8.8/eaglerforge.ts new file mode 100644 index 0000000..6ceeb4e --- /dev/null +++ b/src/resources/scripts/eagler-launch/1.8.8/eaglerforge.ts @@ -0,0 +1,45 @@ +// @ts-nocheck +window.addEventListener('load', () => { + const relayId = Math.floor(Math.random() * 3); + window.eaglercraftXOpts = { + container: 'game_frame', + assetsURI: `${window.location.pathname}/assets.epk`, + localesURI: `${window.location.pathname}/lang/`, + servers: [ + { addr: 'wss://mc.ricenetwork.xyz', name: 'Rice Network' }, + { addr: 'wss://mc.lamplifesteal.xyz', name: 'LampLifesteal' }, + { addr: 'wss://electronmc.club', name: 'Electron Network' }, + { addr: 'wss://play.brickmc.net', name: 'BrickMC' }, + ], + relays: [ + { addr: 'wss://relay.deev.is/', comment: 'lax1dude relay #1', primary: relayId === 0 }, + { addr: 'wss://relay.lax1dude.net/', comment: 'lax1dude relay #2', primary: relayId === 1 }, + { addr: 'wss://relay.shhnowisnottheti.me/', comment: 'ayunami relay #1', primary: relayId === 2 }, + ], + }; + + const storage = { + local: { + get: function (key: string) { + const item = localStorage.getItem('minexlauncher'); + if (item !== null) { + const json = JSON.parse(item); + if (json[key] !== undefined) { + return json[key]; + } else { + return null; + } + } else { + return null; + } + }, + }, + }; + + const urlParams = new URLSearchParams(window.location.search); + window.eaglercraftXOpts.joinServer = urlParams.get('server') ?? undefined; + window.eaglercraftXOpts.Mods = storage.local.get('mods') ?? []; + + history.replaceState({}, '', '/play'); + main(); +}); diff --git a/src/resources/scripts/eagler-launch/1.8.8/eaglermobile-ef.ts b/src/resources/scripts/eagler-launch/1.8.8/eaglermobile-ef.ts new file mode 100644 index 0000000..deb8218 --- /dev/null +++ b/src/resources/scripts/eagler-launch/1.8.8/eaglermobile-ef.ts @@ -0,0 +1,688 @@ +/* eslint-disable */ +// @ts-nocheck +if (new URLSearchParams(window.location.search).get('mobile') === 'true') { + try { + unsafeWindow.console.warn("DANGER: This userscript is using unsafeWindow. Unsafe websites could potentially use this to gain access to data and other content that the browser normally wouldn't allow!"), Object.defineProperty(window, 'clientWindow', { value: unsafeWindow }); + } catch { + Object.defineProperty(window, 'clientWindow', { value: window }); + } + (function () { + try { + return document.createEvent('TouchEvent'), !0; + } catch (A) { + return !1; + } + })() || alert('WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!'), + (clientWindow.crouchLock = !1), + (clientWindow.sprintLock = !1), + (clientWindow.keyboardFix = !1), + (clientWindow.inputFix = !1), + (clientWindow.blockNextInput = !1), + (clientWindow.hiddenInputFocused = !1); + var A = null, + I = null, + g = null; + String.prototype.toKeyCode = function () { + return { + 0: 48, + 1: 49, + 2: 50, + 3: 51, + 4: 52, + 5: 53, + 6: 54, + 7: 55, + 8: 56, + 9: 57, + backspace: 8, + tab: 9, + enter: 13, + shift: 16, + ctrl: 17, + alt: 18, + pause_break: 19, + caps_lock: 20, + escape: 27, + ' ': 32, + page_up: 33, + page_down: 34, + end: 35, + home: 36, + left_arrow: 37, + up_arrow: 38, + right_arrow: 39, + down_arrow: 40, + insert: 45, + delete: 46, + a: 65, + b: 66, + c: 67, + d: 68, + e: 69, + f: 70, + g: 71, + h: 72, + i: 73, + j: 74, + k: 75, + l: 76, + m: 77, + n: 78, + o: 79, + p: 80, + q: 81, + r: 82, + s: 83, + t: 84, + u: 85, + v: 86, + w: 87, + x: 88, + y: 89, + z: 90, + left_window_key: 91, + right_window_key: 92, + select_key: 93, + numpad_0: 96, + numpad_1: 97, + numpad_2: 98, + numpad_3: 99, + numpad_4: 100, + numpad_5: 101, + numpad_6: 102, + numpad_7: 103, + numpad_8: 104, + numpad_9: 105, + '*': 106, + '+': 107, + '-': 109, + '.': 110, + '/': 111, + f1: 112, + f2: 113, + f3: 114, + f4: 115, + f5: 116, + f6: 117, + f7: 118, + f8: 119, + f9: 120, + f10: 121, + f11: 122, + f12: 123, + num_lock: 144, + scroll_lock: 145, + ';': 186, + '=': 187, + ',': 188, + '-': 189, + '.': 190, + '/': 191, + '`': 192, + '[': 219, + '\\': 220, + ']': 221, + '"': 222, + }[this]; + }; + const i = EventTarget.prototype.addEventListener; + Object.defineProperty(EventTarget.prototype, 'addEventListener', { + value: function (A, I, ...g) { + 'keydown' == A + ? i.call( + this, + A, + function (...A) { + if (A[0].isValid || !clientWindow.keyboardFix) return I.apply(this, A); + }, + ...g + ) + : i.call(this, A, I, ...g); + }, + }); + const e = Event.prototype.preventDefault; + function C(A, I) { + const g = A.toKeyCode(); + let i = new KeyboardEvent(I, { key: A, keyCode: g, which: g }); + (i.isValid = !0), clientWindow.dispatchEvent(i); + } + function t(A, I, g) { + g.dispatchEvent(new PointerEvent(I, { button: A })); + } + function G(A, I) { + A.dispatchEvent(new WheelEvent('wheel', { wheelDeltaY: I })); + } + function d(A) { + let I = document.getElementById('inGameStyle'), + g = document.getElementById('inMenuStyle'); + (I.disabled = A), (g.disabled = !A); + } + (Event.prototype.preventDefault = function (A) { + ('hiddenInput' != document.activeElement.id || A) && ((this._preventDefault = e), this._preventDefault()); + }), + (clientWindow.fakelock = null), + Object.defineProperty(Element.prototype, 'requestPointerLock', { + value: function () { + return (clientWindow.fakelock = this), document.dispatchEvent(new Event('pointerlockchange')), d(!0), !0; + }, + }), + Object.defineProperty(Document.prototype, 'pointerLockElement', { + get: function () { + return clientWindow.fakelock; + }, + }), + Object.defineProperty(Document.prototype, 'exitPointerLock', { + value: function () { + return (clientWindow.fakelock = null), document.dispatchEvent(new Event('pointerlockchange')), d(!1), !0; + }, + }), + (clientWindow.fakefull = null), + Object.defineProperty(Element.prototype, 'requestFullscreen', { + value: function () { + return (clientWindow.fakefull = this), document.dispatchEvent(new Event('fullscreenchange')), !0; + }, + }), + Object.defineProperty(document, 'fullscreenElement', { + get: function () { + return clientWindow.fakefull; + }, + }), + Object.defineProperty(Document.prototype, 'exitFullscreen', { + value: function () { + return (clientWindow.fakefull = null), document.dispatchEvent(new Event('fullscreenchange')), !0; + }, + }); + const c = document.createElement; + document.createElement = function (A, I) { + this._createElement = c; + var g = this._createElement(A); + return ( + 'input' != A || + I || + (document.querySelectorAll('#fileUpload').forEach((A) => A.parentNode.removeChild(A)), + (g.id = 'fileUpload'), + g.addEventListener( + 'change', + function (A) { + (g.hidden = !0), (g.style.display = 'none'); + }, + { passive: !1, once: !0 } + ), + clientWindow.addEventListener( + 'focus', + function (A) { + setTimeout(() => { + (g.hidden = !0), (g.style.display = 'none'); + }, 300); + }, + { once: !0 } + ), + document.body.appendChild(g)), + g + ); + }; + let n = document.createElement('style'); + (n.id = 'inGameStyle'), (n.textContent = '\n.inGame {\ndisplay: none;\n}'), document.documentElement.appendChild(n); + let o = document.createElement('style'); + function Z(A, I, g) { + var i = document.createElement(g ?? 'button', !0); + return ( + i.classList.add(A), + i.classList.add(I), + i.classList.add('mobileControl'), + i.addEventListener( + 'touchmove', + function (A) { + A.preventDefault(); + }, + !1 + ), + i.addEventListener('contextmenu', function (A) { + A.preventDefault(); + }), + i + ); + } + var l; + (o.id = 'inMenuStyle'), + (o.textContent = '\n.inMenu {\ndisplay: none;\n}'), + document.documentElement.appendChild(o), + ((l = 'canvas'), + new Promise((A) => { + if (document.querySelector(l)) return A(document.querySelector(l)); + const I = new MutationObserver((g) => { + document.querySelector(l) && (I.disconnect(), A(document.querySelector(l))); + }); + I.observe(document.documentElement, { childList: !0, subtree: !0 }); + })).then(() => { + !(function () { + var i = document.querySelector('canvas'); + i.addEventListener( + 'touchmove', + function (g) { + g.preventDefault(); + const e = g.targetTouches[0]; + A || ((A = e.pageX), (I = e.pageY)), (g.movementX = e.pageX - A), (g.movementY = e.pageY - I); + var C = clientWindow.fakelock ? new MouseEvent('mousemove', { movementX: g.movementX, movementY: g.movementY }) : new WheelEvent('wheel', { wheelDeltaY: g.movementY }); + i.dispatchEvent(C), (A = e.pageX), (I = e.pageY); + }, + !1 + ), + i.addEventListener( + 'touchend', + function (g) { + (A = null), (I = null); + }, + !1 + ), + d(null != clientWindow.fakelock); + let e = Z('strafeRightButton', 'inGame', 'div'); + (e.style.cssText = 'left:20vh;bottom:20vh;'), document.body.appendChild(e); + let c = Z('strafeLeftButton', 'inGame', 'div'); + (c.style.cssText = 'left:0vh;bottom:20vh;'), document.body.appendChild(c); + let n = Z('forwardButton', 'inGame', 'div'); + (n.style.cssText = 'left:10vh;bottom:20vh;'), + n.addEventListener( + 'touchstart', + function (A) { + C('w', 'keydown'), e.classList.remove('hide'), c.classList.remove('hide'), n.classList.add('active'); + }, + !1 + ), + n.addEventListener( + 'touchmove', + function (A) { + A.preventDefault(); + const I = A.targetTouches[0]; + g || (g = I.pageX); + let i = I.pageX - g; + 10 * i > clientWindow.innerHeight ? (e.classList.add('active'), c.classList.remove('active'), C('d', 'keydown'), C('a', 'keyup')) : 10 * i < 0 - clientWindow.innerHeight ? (c.classList.add('active'), e.classList.remove('active'), C('a', 'keydown'), C('d', 'keyup')) : (e.classList.remove('active'), c.classList.remove('active')); + }, + !1 + ), + n.addEventListener( + 'touchend', + function (A) { + C('w', 'keyup'), C('d', 'keyup'), C('a', 'keyup'), e.classList.remove('active'), c.classList.remove('active'), e.classList.add('hide'), c.classList.add('hide'), n.classList.remove('active'), (g = null); + }, + !1 + ), + e.classList.add('hide'), + c.classList.add('hide'), + document.body.appendChild(n); + let o = Z('rightButton', 'inGame'); + (o.style.cssText = 'left:20vh;bottom:10vh;'), + o.addEventListener( + 'touchstart', + function (A) { + C('d', 'keydown'); + }, + !1 + ), + o.addEventListener( + 'touchend', + function (A) { + C('d', 'keyup'); + }, + !1 + ), + document.body.appendChild(o); + let l = Z('leftButton', 'inGame'); + (l.style.cssText = 'left: 0vh; bottom:10vh;'), + l.addEventListener( + 'touchstart', + function (A) { + C('a', 'keydown'); + }, + !1 + ), + l.addEventListener( + 'touchend', + function (A) { + C('a', 'keyup'); + }, + !1 + ), + document.body.appendChild(l); + let b = Z('backButton', 'inGame'); + (b.style.cssText = 'left:10vh;bottom:0vh;'), + b.addEventListener( + 'touchstart', + function (A) { + C('s', 'keydown'); + }, + !1 + ), + b.addEventListener( + 'touchend', + function (A) { + C('s', 'keyup'); + }, + !1 + ), + document.body.appendChild(b); + let w = Z('jumpButton', 'inGame'); + (w.style.cssText = 'right:10vh;bottom:10vh;'), + w.addEventListener( + 'touchstart', + function (A) { + C(' ', 'keydown'); + }, + !1 + ), + w.addEventListener( + 'touchend', + function (A) { + C(' ', 'keyup'); + }, + !1 + ), + document.body.appendChild(w); + let M = Z('crouchButton', 'inGame'); + (M.style.cssText = 'left:10vh;bottom:10vh;'), + M.addEventListener( + 'touchstart', + function (A) { + C('shift', 'keydown'), + (clientWindow.crouchLock = !!clientWindow.crouchLock && null), + (clientWindow.crouchTimer = setTimeout(function (A) { + (clientWindow.crouchLock = null != clientWindow.crouchLock), M.classList.toggle('active'); + }, 1e3)); + }, + !1 + ), + M.addEventListener( + 'touchend', + function (A) { + clientWindow.crouchLock || (C('shift', 'keyup'), M.classList.remove('active'), (clientWindow.crouchLock = !1)), clearTimeout(clientWindow.crouchTimer); + }, + !1 + ), + document.body.appendChild(M); + let v = Z('inventoryButton', 'inGame'); + (v.style.cssText = 'right:0vh;bottom:30vh;'), + v.addEventListener( + 'touchstart', + function (A) { + C('e', 'keydown'); + }, + !1 + ), + v.addEventListener( + 'touchend', + function (A) { + C('e', 'keyup'); + }, + !1 + ), + document.body.appendChild(v); + let m = Z('exitButton', 'inMenu'); + (m.style.cssText = 'top: 0vh; margin: auto; left: 0vh; right:8vh; width: 8vh; height: 8vh;'), + m.addEventListener( + 'touchstart', + function (A) { + C('`', 'keydown'); + }, + !1 + ), + m.addEventListener( + 'touchend', + function (A) { + C('`', 'keyup'); + }, + !1 + ), + document.body.appendChild(m); + let h = document.createElement('input', !0); + (h.id = 'hiddenInput'), + h.classList.add('inMenu'), + (h.style.cssText = 'position:absolute;top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;font-size:20px;z-index: -10;color: transparent;text-shadow: 0 0 0 black;'), + h.addEventListener( + 'beforeinput', + function (A) { + A.stopImmediatePropagation(), A.preventDefault(!0); + let I = 'insertLineBreak' == A.inputType ? 'return' : null == A.data ? 'delete' : A.data.slice(-1); + if ((clientWindow.lastKey || (clientWindow.console.warn('Enabling blocking duplicate key events. Some functionality may be lost.'), (clientWindow.inputFix = !0)), clientWindow.keyboardFix)) + if ('insertLineBreak' == A.inputType) C('enter', 'keydown'), C('enter', 'keyup'); + else { + const g = A.inputType.slice(0, 1); + if ('i' == g && A.data) { + if (clientWindow.lastKey == I && clientWindow.blockNextInput && clientWindow.inputFix) clientWindow.blockNextInput = !1; + else { + I.toLowerCase() != I ? (C('shift', 'keydown'), C(I, 'keydown'), C(I, 'keyup'), C('shift', 'keyup')) : (C(I, 'keydown'), C(I, 'keyup')), (clientWindow.blockNextInput = !0); + } + } else ('d' != g && A.data) || (C('backspace', 'keydown'), C('backspace', 'keyup'), (clientWindow.blockNextInput = !1)); + } + (clientWindow.lastKey = I), (h.value = ' '); + }, + !1 + ), + h.addEventListener( + 'input', + function (A) { + ' ' != h.value && (h.value = ' '); + }, + !1 + ), + h.addEventListener( + 'keydown', + function (A) { + (229 != A.keyCode && 229 != A.which) || clientWindow.keyboardFix || (clientWindow.console.warn('Switching from keydown to input events due to invalid KeyboardEvent. Some functionality will be lost.'), (clientWindow.keyboardFix = !0), clientWindow.lastKey && (C(clientWindow.lastKey, 'keydown'), C(clientWindow.lastKey, 'keyup'))); + }, + !1 + ), + h.addEventListener('blur', function (A) { + clientWindow.hiddenInputFocused = !1; + }), + document.body.appendChild(h); + let a = Z('keyboardButton', 'inMenu'); + (a.style.cssText = 'top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;'), + a.addEventListener( + 'touchstart', + function (A) { + A.preventDefault(); + }, + !1 + ), + a.addEventListener( + 'touchend', + function (A) { + A.preventDefault(), clientWindow.hiddenInputFocused ? h.blur() : (h.select(), (clientWindow.hiddenInputFocused = !0)); + }, + !1 + ), + document.body.appendChild(a); + let p = Z('placeButton', 'inGame'); + (p.style.cssText = 'right:0vh;bottom:20vh;'), + p.addEventListener( + 'touchstart', + function (A) { + t(2, 'mousedown', i); + }, + !1 + ), + p.addEventListener( + 'touchend', + function (A) { + t(2, 'mouseup', i); + }, + !1 + ), + document.body.appendChild(p); + let R = Z('breakButton', 'inGame'); + (R.style.cssText = 'right:10vh;bottom:20vh;'), + R.addEventListener( + 'touchstart', + function (A) { + t(0, 'mousedown', i); + }, + !1 + ), + R.addEventListener( + 'touchend', + function (A) { + t(0, 'mouseup', i); + }, + !1 + ), + document.body.appendChild(R); + let W = Z('selectButton', 'inGame'); + (W.style.cssText = 'right:20vh;bottom:20vh;'), + W.addEventListener( + 'touchstart', + function (A) { + t(1, 'mousedown', i); + }, + !1 + ), + W.addEventListener( + 'touchend', + function (A) { + t(1, 'mouseup', i); + }, + !1 + ), + document.body.appendChild(W); + let u = Z('scrollUpButton', 'inGame'); + (u.style.cssText = 'right:0vh;bottom:0vh;'), + u.addEventListener( + 'touchstart', + function (A) { + G(i, -10); + }, + !1 + ), + document.body.appendChild(u); + let Y = Z('scrollDownButton', 'inGame'); + (Y.style.cssText = 'right:10vh;bottom:0vh;'), + Y.addEventListener( + 'touchstart', + function (A) { + G(i, 10); + }, + !1 + ), + document.body.appendChild(Y); + let S = Z('throwButton', 'inGame'); + (S.style.cssText = 'right:10vh;bottom:30vh;'), + S.addEventListener( + 'touchstart', + function (A) { + C('q', 'keydown'); + }, + !1 + ), + S.addEventListener( + 'touchend', + function (A) { + C('q', 'keyup'); + }, + !1 + ), + document.body.appendChild(S); + let j = Z('sprintButton', 'inGame'); + (j.style.cssText = 'right:0vh;bottom:10vh;'), + j.addEventListener( + 'touchstart', + function (A) { + C('r', 'keydown'), + (clientWindow.sprintLock = !!clientWindow.sprintLock && null), + (clientWindow.sprintTimer = setTimeout(function (A) { + (clientWindow.sprintLock = null != clientWindow.sprintLock), j.classList.toggle('active'); + }, 1e3)); + }, + !1 + ), + j.addEventListener( + 'touchend', + function (A) { + clientWindow.sprintLock || (C('r', 'keyup'), j.classList.remove('active'), (clientWindow.sprintLock = !1)), clearTimeout(clientWindow.sprintTimer); + }, + !1 + ), + document.body.appendChild(j); + let y = Z('pauseButton', 'inGame'); + (y.style.cssText = 'top: 0vh; margin: auto; left: 0vh; right: 32vh; width: 8vh; height: 8vh;'), + y.addEventListener( + 'touchstart', + function (A) { + C('`', 'keydown'); + }, + !1 + ), + y.addEventListener( + 'touchend', + function (A) { + C('`', 'keyup'); + }, + !1 + ), + document.body.appendChild(y); + let L = Z('chatButton', 'inGame'); + (L.style.cssText = 'top: 0vh; margin: auto; left: 0vh; right: 16vh; width: 8vh; height: 8vh;'), + L.addEventListener( + 'touchstart', + function (A) { + C('t', 'keydown'); + }, + !1 + ), + document.body.appendChild(L); + let N = Z('perspectiveButton', 'inGame'); + (N.style.cssText = 'top: 0vh; margin: auto; left: 0vh; right: 0vh; width: 8vh; height: 8vh;'), + N.addEventListener( + 'touchstart', + function (A) { + C('f', 'keydown'), C('5', 'keydown'); + }, + !1 + ), + N.addEventListener( + 'touchend', + function (A) { + C('f', 'keyup'), C('5', 'keyup'); + }, + !1 + ), + document.body.appendChild(N); + let U = Z('screenshotButton', 'inGame'); + (U.style.cssText = 'top: 0vh; margin: auto; left: 16vh; right: 0vh; width: 8vh; height: 8vh;'), + U.addEventListener( + 'touchstart', + function (A) { + C('f', 'keydown'), C('2', 'keydown'); + }, + !1 + ), + U.addEventListener( + 'touchend', + function (A) { + C('f', 'keyup'), C('2', 'keyup'); + }, + !1 + ), + document.body.appendChild(U); + let z = Z('coordinatesButton', 'inGame'); + (z.style.cssText = 'top: 0vh; margin: auto; left: 32vh; right: 0vh; width: 8vh; height: 8vh;'), + z.addEventListener( + 'touchstart', + function (A) { + C('f', 'keydown'), C('3', 'keydown'); + }, + !1 + ), + z.addEventListener( + 'touchend', + function (A) { + C('f', 'keyup'), C('3', 'keyup'); + }, + !1 + ), + document.body.appendChild(z); + })(); + }); + let b = document.createElement('style'); + (b.textContent = + '\n.mobileControl, .mobileControl:active, .mobileControl.active{\nposition: absolute; \nwidth: 10vh;\nheight: 10vh;\nfont-size:4vh;\n-webkit-user-select: none;\n-ms-user-select: none;\nuser-select: none;\nline-height: 0px;\npadding:0px;\nbackground-color: transparent;\nbox-sizing: content-box;\nimage-rendering: pixelated;\nbackground-size: cover;\noutline:none;\nbox-shadow: none;\nborder: none;\n}\n.mobileControl:active, .mobileControl.active {\nposition: absolute; \nwidth: 10vh;\nheight: 10vh;\nfont-size:4vh;\n-webkit-user-select: none;\n-ms-user-select: none;\nuser-select: none;\nline-height: 0px;\npadding:0px;\nbackground-color: transparent;\ncolor: #ffffff;\ntext-shadow: 0.35vh 0.35vh #000000;\nbox-sizing: content-box;\nimage-rendering: pixelated;\nbackground-size: contain, cover;\noutline:none;\nbox-shadow: none;\nborder: none;\n}\nhtml, body, canvas {\nheight: 100svh !important;\nheight: -webkit-fill-available !important;\ntouch-action: pan-x pan-y;\n-webkit-touch-callout: none;\n-webkit-user-select: none;\n-khtml-user-select: none;\n-moz-user-select: none;\n-ms-user-select: none;\nuser-select: none;\noutline: none;\n-webkit-tap-highlight-color: rgba(255, 255, 255, 0);\n}\n.hide {\ndisplay: none;\n}\n#fileUpload {\nposition: absolute;\nleft: 0;\nright: 100vw;\ntop: 0; \nbottom: 100vh;\nwidth: 100vw;\nheight: 100vh;\nbackground-color:rgba(255,255,255,0.5);\n}\n.strafeRightButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAUGVYSWZNTQAqAAAACAACARIAAwAAAAEAAQAAh2kABAAAAAEAAAAmAAAAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAAAWoAMABAAAAAEAAAAWAAAAAA78HUQAAAIwaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KhDb0tQAAANRJREFUOBHVlMENwyAMRWnVHcqIjMEYzJAemlUidQh6aFZIApIt20FgUC/JxTb8/2wRgTFX+25i4E3UvSXyMDkIGeqc64VlfQgBfJnJwAC11oJIFWOMFJ6Zd+nshSZ/yXMCy0aj9aPH6L1HOc1xkSQqcAtCeJhWwSNAIA+dsaZhFawBwIQyVsFJLOGylkCom+ASHMy1WP151KidFDynieF6gkATSx42cYzf43o+TUnYajBNLyZh4LSzLB8mGC3YUczze5Rj1vXHvPTZTBt/e+hZl0sUO9qPMTJ6UlWFAAAAAElFTkSuQmCC");\n}\n.strafeRightButton.active, .strafeRightButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAEsWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjIyIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMjIiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjIiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDg6NTQ6MzYtMDU6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzFUMDg6NTQ6MzYtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwODo1NDozNi0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+QDppmQAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAKlJREFUOI3t1E0OgyAQBeBn08NwHLtyTsBCT2PiihNwJC5Rr6AbMfzYOjO1iyZ9K0jgYzIBgH+2NMV8ucpL4QUAvPcqkYgyM4MjGkIQocaYFG8A4FYukqKv9lTwVblLFltr97Fz7nM4Bbl5C2vAGFWPz9pwCnMAFXyEcw9jtUJTOfu6SfGq4vg8JTnak/0VbfsAUSeGAaDvB8zzczerVozjpILLfO0//r2skH4sFj3RStwAAAAASUVORK5CYII=");\n}\n.strafeLeftButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAUGVYSWZNTQAqAAAACAACARIAAwAAAAEAAQAAh2kABAAAAAEAAAAmAAAAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAAAWoAMABAAAAAEAAAAWAAAAAA78HUQAAAIwaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KhDb0tQAAAVtJREFUOBHVVTtug0AQHT5CRklqOCAFB0CiAYkC7oA4glPGF7GUQ5AqVpyCAtjsW2tWGAjeOJVHGmYY3ns7DCwQPbI9y+a/HMcRcNu2lVuWJWTd1KGhzOJERuG6LkVRNCmZp3VdA/wt/QXJlXAcx6hREAQkO1X5/CAEmr8YY9q2VYWmaWgcR0W0GcRxSxQYiLEzJwxDlU4XXQgz+L/RvSVQlqWC7HY7SpLkFlxfN+646zoqioKqqtLkefKnUfR9T3AY3hqQsQB8yxYd85NmEguwOOpYwPd9hqzGhfAaSm6YRTlN00VtWjASzvOcPM9TPIwiyzKdT8WmuZEwCOga48AiJuYyaD5brnNcu/Utju6YQbw9WdAkrnF0xxBo2w8Kw0DGy943EWXMfv/KqYpamDs+Ht+vAPee6FEMw3A+HN7u1aHT6fNX7pO8YvpBF/IO9c8APwXJPUuHxoPaD5BueyY3ULuKAAAAAElFTkSuQmCC");\n}\n.strafeLeftButton.active, .strafeLeftButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAEsWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjIyIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMjIiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjIiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDg6NTY6MDgtMDU6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzFUMDg6NTY6MDgtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwODo1NjowOC0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IdwDtgAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAKtJREFUOI3t1LENwyAQBdDvKMMwjlPlJrjCmcZSKiZgJJaIV7AbYxlIBB+RIlJ+ZSR4dyAD8M+eIRmvvbwzvAKAc65JFJHIjOCAeu8p1BhzxgcAuKSTWPTTmgzulWtpgqoe39baapjqWFWjQt1gpkARZrZPwa0Fq+AUqdkF3XHt0RR/NxYMyToO15PJuzXRWzGON4jcaRgApumBZXkdZnYU8/xsgtN87T3+vWwtZC52/2ikAQAAAABJRU5ErkJggg==");\n}\n.forwardButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAACiSURBVHjazNXLCcMwDAbgX8JDeESPoTE8Q3JIVgl0CPXQrKBek1DwS4b6Zix9CCFkMjMQkcHxmBkRAAOAlJILmnMGALAnerXC8yHG2AWq6u3OHuivXMakUw2LCETEF76CtTi3oC04t6K1OPcmlmK4Fy3FThu3MFrx8Bz/J/xcJCNLKJQChipWfbu1YFnWe8XH8fLv8b5vbuB5fgAANOsz/Q4AuOQ/2az67j0AAAAASUVORK5CYII=");\n}\n.forwardButton.active, .forwardButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAADASURBVHjazNXdCYMwEMDxf9IM44MLCB3ECfJgl+gcTpBBCi5gwQmygi+W60NLsSKo8Sw9COTj7kcejsSICEeEAzDGqOoiYgwgACEEFbQsSwCsJjq23PSg67okMMuyr7XVQOdqLQfFath7j/deFx6Da3G7Bd2C263oWtymFi7l2FR0KfewdnNzm3Vd/66P/xOePiR7HiG3lJAaJ+A6DA/a9k6e57vBqrrQ9z2fH6Qozio3bZrbaxJj5I2rjRgjzwEApPZQpLqg/4wAAAAASUVORK5CYII=");\n}\n.rightButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAACpSURBVHjazJXLDQMhDETHFkVQImW4DGrYHLKtrJQivIdsC841y4VPbCm+IeBpZGYMmRmIyOBYZkYEwACglOICrbUCANgT+s1K7UbOeQmoqrc196AiAhHpgtu7PKpoBL4EnoXzbC9H4bzyUCNwXrVVD84Iqv9SHNLjEFeE+Hg2eakdJG3mR4HtEEq9Az/ZTfV08++2Pe6Kj+PlH5B9f7oBr+sNAKCoz/QzAEIyQCEEVZuQAAAAAElFTkSuQmCC");\n}\n.rightButton.active, .rightButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAADCSURBVHjatNXBCcMwDAXQHzXD6JAFAh3EE+iQLtE5MkEGKWSBFDyBV8glRb0U2qYlthVH4IOx/RC2kSpVxSERQgAALTlUFdVrgmEYiiTqnAMAUEn006rXC957E8jMX3NKQUUEIrIJr89STlYx3Azn4GS5zxScrK8fw2nP19rCCQfFLrjv+/LwFmqGY6gJTkGz4VT0pwgx8996kQKui1Ad22CNE4DrsjwwTXc0TbMb7LoL5nl+d5C2PRfJdBxvx/a85wDd42waukdj3wAAAABJRU5ErkJggg==");\n}\n.leftButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAClSURBVHja1NWxDcMgEIXh/xBDMCJj3BjM4BTxKpYyBCniFS5tjGJhCBShQ6BPJ3gcYmaIiDFwmJkIYAAxxiFoSgkANxL9tHy5EELoAnPOh7lrRVUVVaVWkGup6ht4NtwM9DLcil6Ce9Aq3Is2X95/VDzljKemYmqOe3BfNpJavzjDyybkaxt+SkXOz2H5XZbbseJte4x/IOt6Hwbu+wsAmfWZvgcArjJAIdYPiecAAAAASUVORK5CYII=");\n}\n.leftButton.active, .leftButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAADDSURBVHjatNXBDYMwDEDR7zTDcGABpA7CBDnQJToHEzBIJRagEhOwAhcq91a1UYEQEt+iJE+WLdmiquQICyAiSfVpmkQABei6Lgla1zUAJiX6bVn/YhzHKLAoip+zOYI653DO/b3z/5rQjNbAtTA50CA4Bt2FY9FN+Ax6qHnJ4LZt82V8Bt8tRSweVOMYPLh5R3HrD5KtebGF+0PI7j2IjQtwX5YXw/CkLMvTYNPcmOeZzwapqmuSTPv+AYCoapad9x4A2K1DUGTs/EYAAAAASUVORK5CYII=");\n}\n.backButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAACnSURBVHjavJXLEcMgDERXGoqgRMqgDGqwD3ErnkkR8sFuQbnGHhLzEdYNkB7LMghSVRCRwjBUlQiAAkAIwQSaUgIAsCX0m+WuC977JqCInMZsAc3VMgbFMLDLTcYYqyC5/GetqFH8K5drC0pzuLXwbmNuUVVyGq71sdR/13tJjz8Q/tdIepqQu0voUiyymVkwTfNZ8bq+7T1elpcZ8Dh2AACN+kw/AwCfbD/bsCZGlgAAAABJRU5ErkJggg==");\n}\n.backButton.active, .backButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAADESURBVHjavJXNDYMwDEaf3QzDgQWQOggTcGiX6BxMwCCVWIBKTJAVuFC5l0r9EYIQApZySGK/fHEUW8yMXcx7D2Aph/ceeU9omiaJ0LIsAdCU0G+W+9/o+z4KmGXZz1xTQKdilZ1sN7CbWqyqahWkruvjFGuogjVqZxWHwOd8NDZw6WCNURVyG12bx9D8u62PdPgH0blCsqUIuSWHWDsBt3F80nUP8jzfDLxcrgzD8OkgRXFOorRt7wCImSEiSTuqmclrANDkWlTK1XudAAAAAElFTkSuQmCC");\n}\n.jumpButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAUGVYSWZNTQAqAAAACAACARIAAwAAAAEAAQAAh2kABAAAAAEAAAAmAAAAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAAAWoAMABAAAAAEAAAAWAAAAAA78HUQAAAIwaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KhDb0tQAAASFJREFUOBHVlEGOgzAMRV1ggcQByuW4C8dgz66zKFdBmkNkFtMdUooEmZhiTxo8mWRVNRKyY38/DJIN8G7n5Dac57kpy9INRftaa1iWhXkFVSLUJqBpGgol2a7rYGdscH5DVVWGoHVdJ0GVUpu+73uYpmljZj4Boeu6+uE/76iVGjmAkZBlx3DbtoCPfyQtao4Ev9LeXaDrC1IO/QuWQFKMibsTBIcAoRyyg2C/i5R7EBzqKpSL6lgCSDH/a4Idk9gFuT7lJcsjLSXdWCyQag4d03iSIMZKNdyxMQaU+rLjebb2MfsxUNJcLh9Pq4DBtB/G8ZO0yVbrO9fwr5jnGYbhyolU53b7BmN+lxevTQTZhWKKgj8iiY2N2fPESwK8XPwDqnFjUQw2agIAAAAASUVORK5CYII=");\n}\n.jumpButton.active, .jumpButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAEsWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjIyIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMjIiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjIiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6MzM6MTYtMDU6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzFUMDk6MzM6MTYtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+k8/VfAAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAK9JREFUOI3t1M0NwyAMBeCXqsMwTnqqJ/AhnSZST0zASCzRrJBeSsVPABu1h0p9N4T9gSwB8M8rU7beP+XF8A4AzrkhkYgSM4ED6r1XocaYGJ8A4JQXadFaTwHXwsxgZvFhIjgGpXgXPoIkeBNuAT1cPGNtmrC1dmivC9eAHiqCc0iCAsBZVKUAQ4obh+epyVFP8lfM8wVEVzUMAMtyw7Y93mYxinW9D8F5vvYf/16eolUyCllQ87sAAAAASUVORK5CYII=");\n}\n.crouchButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iOTYvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iOTYvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQwOTo0NjozMy0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6NDY6MzMtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNTo0MC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTo0NjozMy0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+MJmZ/gAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAAOxAAADsQBlSsOGwAAANNJREFUOI3V1LENwyAQBdDvKDuEhuloGIMx3LCAU8SrWMoGNBekeAQ7lS1DwD5QUvhXcMATQgBwtjRRf/6Vt4VnANBaV4lt2wZmAC+oEKIIJaIt3gDAJZ6UQ40xMMYkx1JrvuAcmmrv5RBeIKUUlFJsfBfeolJKSCnZeBaO0SVcnHXGNTncsbUWzrm17pyDtTaYUwSncC56CMc4F2XBMcS9x9e4QETJl5QDp2mC934fJnpBiNv69kvSdfc8DADD8CxGUwnOuO8f1dA4voP+3z768+UDWxNf9NcgELQAAAAASUVORK5CYII=");\n}\n.crouchButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6NTE6NDMtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+m4M75AAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAM9JREFUOI3t1M8NgyAUx/GfTYdhCmawpzIBBzuNSU9MwBxM8ZaoK9hDi4En6JO0hyb9nvyDnxBFgH/vOnY+f8pL4RkAvPdNojEmMzM4okR0CFVKpXgHACc+qIZaa2GtLd4rPbOCa2jpeKtdOEIhBIQQxPgmnKJEBCIS41WYozEpLnrHLVVh5xwAQGu9LCfgtbS01tmYQ3AJl6K7MMelqAjmkAQFgDO/oJQq/klbYPoNYtle0fcXGHMVzYg3DDdM02MxVzMex3sTzPvafvx7PQF551iSZuKOLwAAAABJRU5ErkJggg==");\n}\n.crouchButton.active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQwOTo1OToxNC0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6NTk6MTQtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNjoxMC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1Njo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTo1OToxNC0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+dlsxWgAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAPFJREFUOI3V1NENgyAQBuC/hglsGlcgbmDSQRyiiSMQt/GtDtHEDZzGYF965IRDqG0f+j9hkM/zRIB/y8m7Xr/lcXgFgKa5HhKn6bExFZ8syzO67nYIrqoLxvHurjcVD8MAAJjnGUVRZKNaawBA27bODFbnotZaWGvdGj+BEEONMTDGJO+LwjFUGu/hSZigvu/dmOPZMPXNR6lCGqcqj1bsozwSng1/mmTFUlX+2/D2RWHeLwnnqARSVGyCHkC7geMpVKxYCv+AfIfs7eOgYq21+0X5Qr+fSiksywIAqOs6gINjkw6id/M6gJz5s4P+//IEHI9j+ArWcs0AAAAASUVORK5CYII=");\n}\n.inventoryButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQxMDozNjoyOC0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMTA6MzY6MjgtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNjoxMC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQxMDozNjoyOC0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IX6oygAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAM9JREFUOI3dlTEOgjAUhr82jk4mJAxcwHO4eAVHt17DW/QMOMhVSEx0NE44yMTGgAPWYJEo8Fz4t/7t+/Infa9V1KqQlVIOaowRIVprAdCS0CZr5m+EYTgImGXZ21pLQD/V6o5zozURcBlElEE0yOsEl0HEzmzZrFec8qKX9zXx+XId7DkpoHJN7VrGJVgu5q+Dv3iul6217QHxi/t6ThNpN3Gw/5D0kV/burwx8KZ0DbuJwADieA80EqfpUQwOz8RJchAD5vkdqCcP/vCZPgBXBlJB5rsPrwAAAABJRU5ErkJggg==");\n}\n.inventoryButton.active, .inventoryButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iOTYvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iOTYvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQxMDozNzoyNC0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMTA6Mzc6MjQtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNTo0MC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQxMDozNzoyNC0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+Ek/8IQAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAMBJREFUOI1jYKARYITS/6ltLiPM0BUrVlDFxIiICAYGBgYGJmoaimwWC7rEjRs3yDJQQ0MDhc9EDUOx6WXCoY5iMEwM/i0qw/BbVIYsMZwG/xaVYWjMSGSI9HBmuP7uC0liBF1848EjssVggJGBgeE/LFHDkgzMBZpCPHCFxIjB0nJERARmBkHXTKoYDAyT5EZ1g9ELElIAul6MyKPEcGQALY9XUsUwBgZEQc/CwMDAsGHDeoaXL19TzXAGBhrWeQBnYU1GknvkpwAAAABJRU5ErkJggg==");\n}\n.chatButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAEsWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjE4IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTgiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTgiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDg6NDQ6MTEtMDU6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzFUMDg6NDQ6MTEtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwODo0NDoxMS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+OsVW9wAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAN1JREFUOI2t1MERgyAQBdCvYw+hnKUD04Vd7NCFNZhDLMEWnEkRXKQFczAQQCSG5J8YYN4uKlbYsuK3VJVFuq4rEvq+3yQAq0WEEF8hWmuHNXbSIsx8ClFKQQjhsMZftIhSKoswM5g52FenKp3pJs4O8ivmxqegkiQhIgIRZcdxmngibl1KuRunjhdARIRpmoK3d/RMiCgoEkBSSte6D6aO4yM7KLUhrnwUB2mtgytiu8gh9qsGXnetba8Q4vKxairDcIMxy7ujeX4UQTY1AIzjvRgwZgGwHQ34w4/tCcPEV3iQ5pTDAAAAAElFTkSuQmCC");\n}\n.chatButton.active, .chatButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxMDoyNzo0NS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTA6Mjc6NDUtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxMDoyNzo0NS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+/Oo+0wAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAANFJREFUOI2t1MsRgyAQBuA1QzGUsXRgTqEGU8QOJThjEZZBGTQRWzAXl/CSEOJeZJD59kdFgItqOK77v87AyLquXYLW2ifaGXHO/YRIKT0meJIRImpCjDHgnPOYCG8yYoypIkQERBStu5U6taRJK4PCjrVxE9RTRQgRARGr47REOpFGV0pl49L2IggRwVobvb2zZ4KIUZMIUkr56CFY2k6IZFBpQdr5rDwkpYyOCKeoIfxVAxxnbRzvoPXja9dSTdMTtu31STTPSxcUJgK44H/0BuaQUd766ZZEAAAAAElFTkSuQmCC");\n}\n.pauseButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAUGVYSWZNTQAqAAAACAACARIAAwAAAAEAAQAAh2kABAAAAAEAAAAmAAAAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAAASoAMABAAAAAEAAAASAAAAAIh0nEsAAAIwaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4xODwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4xODwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KINZMMgAAAPhJREFUOBGdUsENwjAMTBA70HX6Bok1+kRCTIGQeHaG8qCrVGKI8KArQGxj16lcSpOPHZ/vck7iHa33N+YG7yMTRaqqyhKp6xp5KMQiRVEsEgshYD+IrZi5VAR4mrNmIR1Px4Nsz5cr5lZNmmJiCjkHE4+XXOcYwP2EELzA+CFh74066codpceQiE+ckcu0NrBMR3T20AQZOLQG5q4JRwCnY5ATqNnrh1BKoDsDV7Yvc7Ttbi8qZVlibtWkKSamEJN1o1XTuIzG312Dc7nmoKMQnvG7b5wG5kQYb5obpjJa1z0Yy4o4Wtves8hA6vsXcvktp77Hvwf4DxqOPQw2kc6BAAAAAElFTkSuQmCC");\n}\n.pauseButton.active, .pauseButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxMDoyNTozOC0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTA6MjU6MzgtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxMDoyNTozOC0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+RuelNgAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAAJZJREFUOI1jYKASYITS/yk1hxFmyIoVK8gyISIiAu6i/zBDbty4QZIhGhoacMOYYIKkGoKuhwWbgtLiQji7u7cfpxgyYMIQIROMGkRHg7BGv5ePH5zt6OiIU4ygQdgUYhNDBnCvwZI7KQBZDyMDA8P/gIBAhoiIcJINYmBgYMjKymZ49+4twmsTJ04hyyBkFzEwUKE8AgBQeiatdxIg3QAAAABJRU5ErkJggg==");\n}\n.exitButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAEsWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjE4IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTgiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTgiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzBUMTQ6MjA6NTktMDc6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzBUMTQ6MjA6NTktMDc6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMFQxNDoyMDo1OS0wNzowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+nCkcagAAAYFpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/M2MiRhTKQpqELIYYNbFRZhJKmsYovzYzb36p+fF6byZNtsp2ihIbvxb8BWyVtVJEStbWxAY9581TI5lzO/d87vfec7r3XLCH00pGrxmETDavhSb97oXFJXftM046aaOGvoiiq+PB4AxV7f0Omxlv+s1a1c/9aw2xuK6ArU54TFG1vPCU8MxaXjV5W7hVSUViwqfCHk0uKHxr6lGLn01OWvxpshYOBcDeLOxO/uLoL1ZSWkZYXk53Jl1Qfu5jvsQVz87PSewS70AnxCR+3EwzQQAfQ4zK7KMfLwOyokr+YDl/lpzkKjKrFNFYJUmKPB5RC1I9LjEhelxGmqLZ/7991RPDXqu6yw/OJ8N47YHaLfgqGcbHoWF8HYHjES6ylfzcAYy8iV6qaN370LQBZ5cVLboD55vQ/qBGtEhZcojbEwl4OYHGRWi5hvplq2c/+xzfQ3hdvuoKdvegV843rXwDWHhn38f3IFcAAAAJcEhZcwAACxMAAAsTAQCanBgAAAC8SURBVDiNrdTBDcIgFAbgH9IdZB3OmrgGxybtMMxQD9ZEB2niEM+DXaEeGggUqID+xwd8eRCAYc2C38KYQZRSVYLWepUALAYRQhQhRGQxboqlyHYNT03quxZ912aPRaEUsJcAqkEAoPmG5MLJMyqN19HxdMbjfsO2loqUMg5JKQPMnbyXYGsGK00TK7pYbkcWIiLvpuYA5olYiOgFIQ7eQG6G4eJ3NE3PYsQNB4BxvFYD8/wGsH4jwB8+tg+lWzs5jAClVwAAAABJRU5ErkJggg==");\n}\n.exitButton.active, .exitButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxMDoyOTozMC0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTA6Mjk6MzAtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxMDoyOTozMC0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8++Tr22gAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAALhJREFUOI2t1L0NgzAQBeAHYphb4+pECqniGUiDBIsgZQi3kZJdvERYgTTYGP8Qm/AapMN8PBAGOCjFfJz+dQqNSCl3CUII02jSiFIqCyEig5V6mIu415SxRX3Xou/a5HNBKAZsxYP2IABQ/UJS4eg7ys2q0el8wfv1hDuLhZnDEDN7mL14K96jaSw3VWhoY6mNDEREqy81BdBbBJj3Wl1fIcQt6c5umuaOcfwsjYbhsQuyGwEH/I++B+Q3JPhRaVIAAAAASUVORK5CYII=");\n}\n.keyboardButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQxMDozMDoxMy0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMTA6MzA6MTMtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNjoxMC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQxMDozMDoxMy0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+ZKXV9AAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAONJREFUOI3FlT8KgzAUhz9DD+EhCh28QsFV8BJOdRc6FdztlDNYpK5CLyH0EHaoV7CDWOKfQm2i/S2JyXtffsT31KJVg1lZVgcNgsAIUUoJgDAJVVmb4YZt2z8Bq6rqPQsT0Klc8SFOW4uBe3dcFDct2G63fc9Hjn3fAyCOT1+PXY4qC2i6EinLuxHHUsppx3Ncq/GrOB41iHq64zgkyZkwPPRi1LUoOuL7HlmW/8Gx6+61wGpbr9N5ww/JHA1zRy9PB65KtLCHERhAml4AxbFuRQwlAPL8agxY10+grWNY4Gf6Aux0Xojq759HAAAAAElFTkSuQmCC")\n}\n.keyboardButton.active, .keyboardButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iOTYvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iOTYvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQxMDozMjozNy0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMTA6MzI6MzctMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNTo0MC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQxMDozMjozNy0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+CffUegAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAAOxAAADsQBlSsOGwAAANBJREFUOI3Flc0NgzAMRh+IYZiCDbhHGYAFMgcLMACipyKxAVMgNkAdgl6gNT+VSmPod0kg9uMTiR04ScE0jtrcYIZWVaVCNMYAEGpCJStaL3Rd9xMwjuPFc6gB3csNP8R56zTw4h9nWeYFa9v2Nd84ds4BkKbp1+OcIxUA43xEkiRRcWyM2Xd8xLWMv8TxpkDk1/u+pyxLrLWLGPmuaRqcc+R5/gfHRVF4gWVZX1N560ZyROvczeb5wKWmfnxTgcG70UcAdX1nGB5qcDjxznsC/OhXI+h9hhMAAAAASUVORK5CYII=")\n}\n.placeButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAEsWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjIiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMjIiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIyMiIKICAgZXhpZjpQaXhlbFlEaW1lbnNpb249IjIyIgogICBleGlmOkNvbG9yU3BhY2U9IjEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMjI6MDg6NDgtMDU6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzFUMjI6MDg6NDgtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQyMjowODo0OC0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+gByU7gAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAAKxJREFUOI3VlcENwyAMRR+oQzAiY3gMZkgPzSqROoR7aFZoD0mlhIiGUKtS/s3CPJlvyzgmvbCVcx9ojNGEmFICwFtCl6xLfhBCaAKq6ir2tVARQUSK5/ldX8j7WecDb5pX0jd/q8E55GgM/7Zi79k1tlRXvDfHzeCjOh941TxVLe6LPX/zJbSZijyhVX6CPUxgAF13BRYVD8PdDA5zxX1/MwOO4xMAN8fmn+kbxKMyrUK2UxQAAAAASUVORK5CYII=");\n}\n.placeButton.active, .placeButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVEhL3ZXtCYAgEIYzGsYVggZxAodygmuPoBXcprjrLtLo+/zTA/ZK0YPUi1alMJwTpxYGxSQFAIzPOOcoa7xoSRFx0Yq34hgjz55hreXZsmpasXAm9d7TOCJ/NxFr8mNxCIHGXRrOhPwn5cKr50ixT5H0+G2HBenyrsdnXPU458d1e0qxTWgn1mBtBUBPNzSQjX49Qdq2w/jMOA6UKEaUz7zKzGMSPYSsKtaQAAAAAElFTkSuQmCC");\n}\n.breakButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADVSURBVEhLvZXRDYMgEIa5TsFYdgvHYAxmsA91m8YNIE1dgfa/SqOIFpD2e/Dw4n380QTJOSeIyImKvJxEqLhp2xblMFprridcakmBd3HiuVhKOa2+o5SaVstwSM2JPaVSEM4uxKmE0hjZ4pg01ssSp0pBVIyHw4EcKdhN7AdzpWAzsadECqp8vBib4tKknv+JMTwXhLJU+e479pJwsxQWYmPMtIqzt0E4uzrdavA53Yyx3KhB1124cuKmOQtr79w4yjDcxDg+3on7/srNGkAKfvQzdfQETtBmHYKdHW8AAAAASUVORK5CYIJFTkSuQmCCnd8aY0jyr65+cLRujrL+fJOPAaJMN46HP07L/I9c3n9ESitLhsbMMowoH/3cozJTep8My+2KiwA8gRAju39mJaqaAEeXWxfxIbJZCwbFGkMgYDWx692ANTNbFaEbegDqusY5t5Rc2yvHCLx/nlERinw1OT3GyxoDMRmWr+w0pjpOimoCGftl0CmX/eBQEYzowsBjm873/ZmqqubYiCxTv1kLyNz4Mbw+htmcKGzvMo6fcVI1mdK2LSFAvrJzL2Pgv1Pq7TgxaWEkpuHSkvyi4nw+471Py+E6UwnUYUTZrJOTY7CNKLkJ+BhQETbrq7WVZdR1fZvDfnBzHi9OXme0yIvbVXOZ5bZtbwHHOR5dve4ZQNd3vP+I028AJKKaoDKA7XbL4+Pj999t46qqcM5N6+54PHJ/f0/XdYQQZsCu6zgcDpNTY9CtTSY55xabelwmRVHw+vq6eKj87ZfU/8fHSrLxG7/VAAAAAElFTkSuQmCC");\n}\n.breakButton.active, .breakButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADZSURBVEhLtZXRDYMgEIa9TsMKJh3ECXywGzkBXYM0cQW3of2vYBEPC4rfg/9Jcp8XTICstc0VsJiIqto/TiIkXrTWiNN0Xcd5w6OWFHgXTxyK53l21X/6vndV0xhjXPWdmif2HJWCuHclziWWShSLJek4jq76USTOlQJRDEEsKZGC3Ym9rFQKRHHYdEQKin9ejhQkxZIgVwqSYmkLpLUUGzGaQ0E8Za58d4+9FFmyDWAlVkq5SmbvA3Hv5nSrwXK6af3khRoMw4NzuUHa9o44zTS9OC+68yy9Abg0YUFOFw2OAAAAAElFTkSuQmCCQmCCnd8aY0jyr65+cLRujrL+fJOPAaJMN46HP07L/I9c3n9ESitLhsbMMowoH/3cozJTep8My+2KiwA8gRAju39mJaqaAEeXWxfxIbJZCwbFGkMgYDWx692ANTNbFaEbegDqusY5t5Rc2yvHCLx/nlERinw1OT3GyxoDMRmWr+w0pjpOimoCGftl0CmX/eBQEYzowsBjm873/ZmqqubYiCxTv1kLyNz4Mbw+htmcKGzvMo6fcVI1mdK2LSFAvrJzL2Pgv1Pq7TgxaWEkpuHSkvyi4nw+471Py+E6UwnUYUTZrJOTY7CNKLkJ+BhQETbrq7WVZdR1fZvDfnBzHi9OXme0yIvbVXOZ5bZtbwHHOR5dve4ZQNd3vP+I028AJKKaoDKA7XbL4+Pj999t46qqcM5N6+54PHJ/f0/XdYQQZsCu6zgcDpNTY9CtTSY55xabelwmRVHw+vq6eKj87ZfU/8fHSrLxG7/VAAAAAElFTkSuQmCC");\n}\n.selectButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAEqGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjIyIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMjIiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjIiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzBUMTQ6MTQtMDc6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzBUMTQ6MTQtMDc6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMFQxNDoxNC0wNzowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+ndR9ZQAAAYFpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/M2MiRhTKQpqELIYYNbFRZhJKmsYovzYzb36p+fF6byZNtsp2ihIbvxb8BWyVtVJEStbWxAY9581TI5lzO/d87vfec7r3XLCH00pGrxmETDavhSb97oXFJXftM046aaOGvoiiq+PB4AxV7f0Omxlv+s1a1c/9aw2xuK6ArU54TFG1vPCU8MxaXjV5W7hVSUViwqfCHk0uKHxr6lGLn01OWvxpshYOBcDeLOxO/uLoL1ZSWkZYXk53Jl1Qfu5jvsQVz87PSewS70AnxCR+3EwzQQAfQ4zK7KMfLwOyokr+YDl/lpzkKjKrFNFYJUmKPB5RC1I9LjEhelxGmqLZ/7991RPDXqu6yw/OJ8N47YHaLfgqGcbHoWF8HYHjES6ylfzcAYy8iV6qaN370LQBZ5cVLboD55vQ/qBGtEhZcojbEwl4OYHGRWi5hvplq2c/+xzfQ3hdvuoKdvegV843rXwDWHhn38f3IFcAAAAJcEhZcwAACxMAAAsTAQCanBgAAADASURBVDiNrdXdDYMgEAfwP6RDMCJjOAYz2Ie6ikmHwIe6Qvugl+DxUTzuEhMR+XkBPAyO+EI3jCHUe68ihhAAAFYTTa0H73DOdQHTNF3uY4yXfitF6aI2H2sL45oYf1aLbphjLfQW3IJKfcNTUftgF1wDhuaYr/4/kCLbx72oePGkmTbhUbQI819VghZhgkbQDKZCIkF5Ecp2RVpC+ct3wh7AJgZ4zPMTQJLxur7VcODMeFleauC+fwAA5myrH6Y/P01lF42FgH8AAAAASUVORK5CYII=");\n}\n.selectButton.active, .selectButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAEsWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjIiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMjIiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249Ijk2LzEiCiAgIHRpZmY6WVJlc29sdXRpb249Ijk2LzEiCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIyMiIKICAgZXhpZjpQaXhlbFlEaW1lbnNpb249IjIyIgogICBleGlmOkNvbG9yU3BhY2U9IjEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6MzU6MzktMDU6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzFUMDk6MzU6MzktMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTozNTozOS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+c0BT1gAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAMtJREFUOI211dENgyAQBuBf0mFYwaSDOAEPdqObgK5BmriC29iHeoSegIB4L4rIl/MkB3BTDPt16+0OjFpru4jTNAEAVE80tB5yYl3XIsAY4++JCFrrv3nVihIRiMiP5VoVW5jCwixl1jKKYYnl0CqYP7t0rqoUEgjr3ATHUH6eilM4RBnKlYXjsI9L0TM8mXFrpln4KhqFwx/SikZhhq6gB5gbSQsqm9BhVzjnki/XxN6P382AjHl+AfgdTRsAjOOzC7wsHw8DN5x5X3p5YEMtOwViAAAAAElFTkSuQmCC");\n}\n.scrollUpButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQwOTo0MTozNS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6NDE6MzUtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNjoxMC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTo0MTozNS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+qh8mWAAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAL1JREFUOI21lcsNwyAQRB8oRVCiL3Ex+EINziFuxVKKIIe4heRgW8IY/GMztxWzj2GRQDHqi6yUmqFVVYkQnXMAaEloyLrFC8aYS0Dv/aLWEtBUr874krK2wdrmkPcUONzgL+Aj8NXlHWkKfXV9T65dTrwXohicUzE4N4rkjHPm+Ng5HxQk3oJeBu9BITOKEuCsReL4ITmjuHeVuAQeSo+wtwgMoG0fQJC4719icJgSd91TDDgMHwDUVIt/pj9lYzcah66GagAAAABJRU5ErkJggg==");\n}\n.scrollUpButton.active, .scrollUpButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iOTYvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iOTYvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQwOTo0Mjo1MS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6NDI6NTEtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNTo0MC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTo0Mjo1MS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+nGTdYQAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAK9JREFUOI21ldERhCAMRFeGYiiJClIUFXBfZzm04FwR+qMjIjmRhP1xmGyea5wJwCBN+3PV5k4HNMaoQvTeAwCMJjRn2bKQUuoCOucuZ6MBrfUaxlcVEYGImryvwPkLhoBb4Lef19KU+0II1Vp34qcQYjAnMZgbRXXGnLn8bM4HCBL/g3aDn6AAMwoJ8NAlcblI3qjsvSWWwHPt+/ijAgPORW8BYJ6/WJafGhwYeOdt2u4xmobYNrAAAAAASUVORK5CYII=");\n}\n.scrollDownButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQwOTozOTo0Ny0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6Mzk6NDctMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNjoxMC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTozOTo0Ny0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+cF/fZAAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAALFJREFUOI21lcENwyAMRR+oQzAiYzAGM6SHZpVIHSI9NCu0h6QSUKAlmH8z4OcvY4Fi1wtZKfWBWmtFiN57ALQkNGRd0g1jzCnguq5RrCWguVxdOBfJOYdzrqnQT3Ar8C/wWShkLq8GLRXKrWcd9zitgiU0zHG1x2mBloLDpmLYHBdb0QuPHKcPSYvS3C/HPfBQeoc9RGAA03QFAsfLcheDw+F4nm9iwG17AqCOWPwzfQNXBzbjhlGh5wAAAABJRU5ErkJggg==");\n}\n.scrollDownButton.active, .scrollDownButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iOTYvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iOTYvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQwOTo0MDozNS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMDk6NDA6MzUtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNTo0MC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQwOTo0MDozNS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8++KGu/QAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAAOxAAADsQBlSsOGwAAALBJREFUOI21ldENwyAMRC8owzASE3goT0C/mnFYIeoQ7U+qBjCkBud+EAGeL4dkgJu0HOPbmrt8oTFGE2IIAQDgLKFn1loupJSGgN77bO4soNJZ19iXiYhARKpCl2At8C/wKBQQLq8HbRVi5uqb6HjGaRdsIREs/ZpWzYyZuYpEU7AbxYzzy4xH4c0oZuGZ47KRaFSerRzPwM86+vHDBAb8Gv0KANv2xL6/zODAjW/eB0CEMZpx89CdAAAAAElFTkSuQmCC");\n}\n.throwButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQxMDoxODowNi0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMTA6MTg6MDYtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNjoxMC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQxMDoxODowNi0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+WtcK9wAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAQ1JREFUOI2tlcERwiAQRX8yFpEuQgXbhdaQixazXFKDHrQGKoAZi8CDtqAHJZNsANHh38Luvv1ZSGjw1hN11TQBOgxDFeI4jgCAtiZ0ztrIQNd10QJmjcNhv1iz1kGpHgDgvV/E2n+hzHqCxmoX4JRiULkmlQUzazDrbCNr3e9gACCiZAM5jrlWmzcvCs6U6mGtm+BhPTeOJFhKqX5yJxvEFB0FswYRJYtysSw4OEzJGPP7qfjmNnUKvoKDo5RK3AKRzQtFYYOIKDuWYnCsgTEGRFTsdgX23q++efkGKcmf0MqxTAja7bbZuFT7Tr4VJZfoeDwBmDl27loNDnwcXy7nasDH4w4AaD7P1S/TF7dhcsyyU4psAAAAAElFTkSuQmCC");\n}\n.throwButton.active, .throwButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyMiIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIyMiIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMiIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iOTYvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iOTYvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNS0zMVQxMDoxOTo0OS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMTA6MTk6NDktMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwODozNTo0MC0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQxMDoxOTo0OS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+7MGLFgAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAO9JREFUOI21lb0NhDAMhR+IYdJ7GSbwUEzAVccM9O5ZAd0Qdw1GwfnlFF6DlODPL3YwwEPqjue3NbdT6DzPTYjjOAIA+pZQnzXYjW3bogHMjGmaLmtEBBEBADjnLnv9v1BmPqGx2As4pRjUrlllwcwMZs4mIqL7YAAQkWQCWw5fQfP8IHUmIiCiE67ruXIkwTHn6s4miClaitwRNUlJyRrngono/q0ouU3dgiK4FFzjFog0T4O0QX7T7ih5K/wEOhNq3QZg51zwzdsTpGSHUODYvqBa1zW7b3XM41fVyzXSQT8AwLK8se+fZnDgwX/eDxNAdxmhDNuAAAAAAElFTkSuQmCC");\n}\n.sprintButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADOSURBVEhLvZXtDYMgEIa5pkMwlt3CMRyDGeyPuk3jBhBTV6C9E1r50KicfX54xyU8eYNBwVorAMAKRj5OAKy4qOsaSzFKKaoXfHBJEe+ixHOxlNJ1+9Bau25KTYk9R6VIvDcQc3J1NaFpGtf9+txsiU2Jc5LD4ngjrnOzJVYTl8hPe3n/T1wiRTYdRU6wJkWCK11y8xB/rZMrzUkgnn9I9hLvTb5uHHyPQmtDAw7a9k6VElfVTRgz0KCUvn+KcXxNibvuQUMOUIqc9DO18AZc1WQdjvTPDgAAAABJRU5ErkJggq5CYIIY9LrovOfskvxtnd8aY0jyr65+cLRujrL+fJOPAaJMN46HP07L/I9c3n9ESitLhsbMMowoH/3cozJTep8My+2KiwA8gRAju39mJaqaAEeXWxfxIbJZCwbFGkMgYDWx692ANTNbFaEbegDqusY5t5Rc2yvHCLx/nlERinw1OT3GyxoDMRmWr+w0pjpOimoCGftl0CmX/eBQEYzowsBjm873/ZmqqubYiCxTv1kLyNz4Mbw+htmcKGzvMo6fcVI1mdK2LSFAvrJzL2Pgv1Pq7TgxaWEkpuHSkvyi4nw+471Py+E6UwnUYUTZrJOTY7CNKLkJ+BhQETbrq7WVZdR1fZvDfnBzHi9OXme0yIvbVXOZ5bZtbwHHOR5dve4ZQNd3vP+I028AJKKaoDKA7XbL4+Pj999t46qqcM5N6+54PHJ/f0/XdYQQZsCu6zgcDpNTY9CtTSY55xabelwmRVHw+vq6eKj87ZfU/8fHSrLxG7/VAAAAAElFTkSuQmCC");\n}\n.sprintButton.active, .sprintButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADOSURBVEhLtZXbDcMgDEVx1WFYIVIHyQR8pBsxAd2jUlZgG1q7EPEwKClwPmJiKUdXRAZwzokZkBgAhtq/TgCs+GKMwdLNuq5Ub/gYJUWCixLHYmutX11DSulXv9SUOPCvFMm/TcQjuftaoJTyKyG01lS5Xo1TiWNhgOvFVMV5IhRxvRrNxD3yaT+vKc7TYFqux1EV90iRU1vBCVpSJBnpnslDwlgXIz2SRBwfJFfJvy1OtxEcW2HMixoj2LYn1eMGWZYHlm72/U110p3n4APUi1xBJ/2pfQAAAABJRU5ErkJggmCCAAAAAElFTkSuQmCCnd8aY0jyr65+cLRujrL+fJOPAaJMN46HP07L/I9c3n9ESitLhsbMMowoH/3cozJTep8My+2KiwA8gRAju39mJaqaAEeXWxfxIbJZCwbFGkMgYDWx692ANTNbFaEbegDqusY5t5Rc2yvHCLx/nlERinw1OT3GyxoDMRmWr+w0pjpOimoCGftl0CmX/eBQEYzowsBjm873/ZmqqubYiCxTv1kLyNz4Mbw+htmcKGzvMo6fcVI1mdK2LSFAvrJzL2Pgv1Pq7TgxaWEkpuHSkvyi4nw+471Py+E6UwnUYUTZrJOTY7CNKLkJ+BhQETbrq7WVZdR1fZvDfnBzHi9OXme0yIvbVXOZ5bZtbwHHOR5dve4ZQNd3vP+I028AJKKaoDKA7XbL4+Pj999t46qqcM5N6+54PHJ/f0/XdYQQZsCu6zgcDpNTY9CtTSY55xabelwmRVHw+vq6eKj87ZfU/8fHSrLxG7/VAAAAAElFTkSuQmCC");\n}\n.perspectiveButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAEsWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjE4IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTgiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTgiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDUtMzFUMTU6NTg6NDUtMDU6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDUtMzFUMTU6NTg6NDUtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNS0zMVQxNTo1ODo0NS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+GlXzLwAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZogwjWJhoUzCamhQk9koIw0laYwy2Mw882bU/Hi99yTZKltFiY1fC/4CtspaKSIl61kTG6bnvHlqJHNu557P/d57TveeC65YVskZtQHI5U09Ggn75uMLvvoidXTioYtQQjG00ZmZKaraxwM1drzrs2tVP/evNS2nDAVqGoRHFE03hSeEp9ZMzeZd4TYlk1gWPhf263JB4XtbTzpctDnt8JfNeiw6Bq4WYV/6Fyd/sZLRc8Lycrpz2VXl5z72S5pT+blZiV3iHRhEiRDGxyTjjBFkgJDMQfoYpF9WVMkPlPOnKUiuIrPGOjorpMlg4hd1VaqnJKqip2RkWbf7/7evhjo06FRvDkPdi2W99UD9DpS2Levz2LJKJ+B+hqt8Jb9wBMPvom9XtO5D8G7CxXVFS+7B5Ra0P2kJPVGW3OIuVYXXM/DEofUWGhednv3sc/oIsQ35qhvYP4BeOe9d+gawQmgHbIPcOQAAAAlwSFlzAAALEwAACxMBAJqcGAAAASBJREFUOI2tlLFxhDAQRR/MpcQWFdCAcqkBCqADutDQBTWcA18JKCe5GSdUgBzctXAOGGkQAo/v7B9pV7tv/koaZSx68DdlmYe0bfsSoe/7hQQ8PEQI8RTEORdgJ5/cgxhjorjruigWQgTYiR15wLbxKA+Q70GGYQjFSimUUgEwDEPiNHHkIdZaAJqmoSgKyrIEwFob9owxkbP8CAJQFAUA8zxTVVXIW2sTZwEkpURrHcYAmKZpd62UQmuNlDIdra5rpJSM45iM4eMtpK7r/Vtbw7TWdF0XwfwoHrJWcv0eBgR36/F9zVa778gXrs/gCJCAnHPJ6/6p0fdEIOe+EOIt2vitzuf32NH1+vk0ZK0c4HL5eBlwv9+A5RuBf/jYvgH4yIZeQnyS2QAAAABJRU5ErkJggg==");\n}\n.perspectiveButton.active, .perspectiveButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxMDozNDo0MC0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTA6MzQ6NDAtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxMDozNDo0MC0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+JwuWpAAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAARdJREFUOI2tlMFthDAQRR+rvVIAFeACfLcbQNqcAi1sirBcwkopgjLsOwVABZyyLZBL7AA2K2Wz/wIzzDz9b1nAi1T8PJf/cooA6fv+KULbttHREiDjOP4JIoSIsHNo5iDGmE1trd3U4zhG2JmMAmC/eNQHOOUgzrk4rJRCKRUBzrnEaeIoQLz3AHRdR1mWVFUFgPc+fjPGbJydjiAAZVkCMM8zdV3Hvvc+cRZBUkq01jEGwDRN2XelFFprpJRptKZpkFIyDEMSI9R7SNM0+TNaw7TWWGs3sBBlD0lAaxgQ3a3jh5m9svcoDK7P4AiQgIQQye1+tBh2ggpguVzeaNv3h0tHul4/uN+/fh3dbp9PgdaO4AX/o28EZ31RG7EATQAAAABJRU5ErkJggg==");\n}\n.screenshotButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxOToxNDo0MS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTk6MTQ6NDEtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQxNTo1ODo0NS0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxOToxNDo0MS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+gsym2gAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAANlJREFUOI2t1EEKgzAQBdCveIfOdbIV8RxuRTyFiFvPYBeVUnoPoYeYLuoVdCGRJEab2P6dZuZlDJIASyb8liCQSJZlp4S2bRcJwCQRIvJCmHnFIvlSRcoiR1U3KItca6zqRnsmohWLYEQ2S8y2ZkMjs9As2JvIrA13u5QG8xNtOYTkrrbz8oJ8sjlsNeokR2f3FXIBrFCcpHg+7k6NcZJCCGGHhBCIk9QJUhENYmYQ0abgKPKvXiHmN4gu2oJruu6qTzQML29ETQgAfX87DYzjB8ByjQB/uNhm16JRDHvEtZMAAAAASUVORK5CYII=");\n}\n.screenshotButton.active, .screenshotButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxOToxNTowNS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTk6MTU6MDUtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxOToxNTowNS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+ViGmhwAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAANNJREFUOI2t1EEOQ0AUBuBf4zBzjdmKiK46Z9CViFOIiKSHsG6anuVdoq6gG0+fMXRo/43gvW+eCYA/JRiPw69OwEjXdYcEY8w00cAIEe1ClFITFvJFiZRFjqpuUBb5rLGqm9k5EU1YCCvczJjrngsN7UK7YG0iu/a02iUa7Ed0ZRPiVV37tQvak8Vmy8hJtvbuK+QDOKEoTvB83L0aoziB1toNaa0RxYkXJJEZpJQCES0KtsJvNTB+a2l6hjEXb0Amy67o+9dnora9HYLkRMAf/kdvmR9KtwpEL4QAAAAASUVORK5CYII=");\n}\n.coordinatesButton {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFPmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxOTo0NjoxMS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTk6NDY6MTEtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQxNTo1ODo0NS0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxOTo0NjoxMS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+7f9PrQAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAANVJREFUOI2t1MENgjAUxvE/hB3sOlyNcQFj4pGrMc4CIyAeJDHGIyu0iUPgQRgBD6QICoWq3/G1/eW9pqlDnYrf4jgaCYLgKyGKoloCKo0IIayQPM8bzNVFW+T9jGvY15v9bkuWZR91zwYAmC+W+L5vD2kA4Hy5Du4zjial6iBhGPZ2MwoBrNYbYyc6g6O1u4njA2VZDHZjhHTKsgAwIoOQlGoyYITGxuhLc9n6udt00T7j1YU7Qsw6C1OTJMcXBKDUzRppxwVI09PXQFE8gPobgT98bE9BU0xrk+bdqAAAAABJRU5ErkJggg==");\n}\n.coordinatesButton.active, .coordinatesButton:active {\nbackground-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxOTo0Njo0NS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTk6NDY6NDUtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxOTo0Njo0NS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+3ivAWwAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAAM9JREFUOI2t1DEKwjAYhuG34mF6ja4iUicDIjjXScSDFIReIc4ijr1Cu/USJkeoi6lVm9TUfksgJA/fX0pgpATPtf7XCQwipRwkCCGaRrVBqqryQsIwbLCJ2fRFPu9MHOc6czzsyfP8a3/qAwDM5guiKPKHDABwud6s55yjFUX5hmRZ1tmmFwJYb7bOJibW0dptpDyjtbK2cUImWisAJ2KFiqL8GXBCfWN0pfnY5nf3adG+EwB1HC8RYuXVwCRJdih1f42WpqdBULsRjPAePQBKokYW6r1DQwAAAABJRU5ErkJggg==");\n}\n'), + document.documentElement.appendChild(b); +} diff --git a/src/resources/scripts/eagler-launch/1.8.8/eaglerpocketmobile.ts b/src/resources/scripts/eagler-launch/1.8.8/eaglerpocketmobile.ts new file mode 100644 index 0000000..7eed39a --- /dev/null +++ b/src/resources/scripts/eagler-launch/1.8.8/eaglerpocketmobile.ts @@ -0,0 +1,1156 @@ +/* eslint-disable */ +// @ts-nocheck +if (new URLSearchParams(window.location.search).get('mobile') === 'true') { + // Removed brainless unsafeWindow + console.log('Eagler Pocket Mobile v1.40'); + // TODO: remove the mobile check is implement the dynamic enabling and disabling of individual features + function isMobile() { + try { + document.createEvent('TouchEvent'); + return true; + } catch (e) { + return false; + } + } + if (!isMobile()) { + alert('WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!'); + } + // TODO: consolidate all of these into a single object? + window.crouchLock = false; // Used for crouch mobile control + window.sprintLock = false; // Used for sprint mobile control + window.keyboardFix = false; // keyboardFix ? "Standard Keyboard" : "Compatibility Mode" + window.inputFix = false; // If true, Duplicate Mode + window.blockNextInput = false; // Used for Duplicate Mode + window.hiddenInputFocused = false; // Used for keyboard display on mobile + window.canvasTouchMode = 0; // Used for canvas touch handling + /* + 0 Idle + 1 Touch initiated + 2 Primary touch + 3 Secondary touch + 4 Scroll + 5 Finished +*/ + window.canvasTouchStartX = null; + window.canvasTouchStartY = null; + window.canvasTouchPreviousX = null; + window.canvasTouchPreviousY = null; + window.canvasPrimaryID = null; + window.buttonTouchStartX = null; + + // charCodeAt is designed for unicode characters, and doesn't match the behavior of the keyCodes used by KeyboardEvents, thus necessitating this function + String.prototype.toKeyCode = function () { + const keyCodeList = { + '0': 48, + '1': 49, + '2': 50, + '3': 51, + '4': 52, + '5': 53, + '6': 54, + '7': 55, + '8': 56, + '9': 57, + backspace: 8, + tab: 9, + enter: 13, + shift: 16, + ctrl: 17, + alt: 18, + pause_break: 19, + caps_lock: 20, + escape: 27, + ' ': 32, + page_up: 33, + page_down: 34, + end: 35, + home: 36, + left_arrow: 37, + up_arrow: 38, + right_arrow: 39, + down_arrow: 40, + insert: 45, + delete: 46, + a: 65, + b: 66, + c: 67, + d: 68, + e: 69, + f: 70, + g: 71, + h: 72, + i: 73, + j: 74, + k: 75, + l: 76, + m: 77, + n: 78, + o: 79, + p: 80, + q: 81, + r: 82, + s: 83, + t: 84, + u: 85, + v: 86, + w: 87, + x: 88, + y: 89, + z: 90, + left_window_key: 91, + right_window_key: 92, + select_key: 93, + numpad_0: 96, + numpad_1: 97, + numpad_2: 98, + numpad_3: 99, + numpad_4: 100, + numpad_5: 101, + numpad_6: 102, + numpad_7: 103, + numpad_8: 104, + numpad_9: 105, + '*': 106, + '+': 107, + '-': 109, + '.': 110, + '/': 111, + f1: 112, + f2: 113, + f3: 114, + f4: 115, + f5: 116, + f6: 117, + f7: 118, + f8: 119, + f9: 120, + f10: 121, + f11: 122, + f12: 123, + num_lock: 144, + scroll_lock: 145, + ';': 186, + '=': 187, + ',': 188, + '-': 189, + '.': 190, + '/': 191, + '\u0060': 192, + '[': 219, + '\u005C': 220, + ']': 221, + '\u0022': 222, + }; + return keyCodeList[this]; + }; + // Overrides the addEventListener behavior to all code injection on keydown event listeners. This function has thrown TypeErrors on some Android devices because fn is not recognized as a function + // This is used by Compatibility Mode to block invalid keyEvents + const _addEventListener = EventTarget.prototype.addEventListener; + Object.defineProperty(EventTarget.prototype, 'addEventListener', { + value: function (type, fn, ...rest) { + if (type == 'keydown') { + // Check if a keydown event is being added + _addEventListener.call( + this, + type, + function (...args) { + if (args[0].isTrusted && window.keyboardFix) { + // When we are in compatibility mode, we ignore all trusted keyboard events + return; + } + return fn.apply(this, args); // Appends the rest of the function specified by addEventListener + }, + ...rest + ); + } else { + // If it's not a keydown event, behave like normal (hopefully) + _addEventListener.call(this, type, fn, ...rest); + } + }, + }); + // Overrides preventDefault, because on some (Android) devices you couldn't type into hiddenInput + const _preventDefault = Event.prototype.preventDefault; + Event.prototype.preventDefault = function (shouldBypass) { + if (document.activeElement.id != 'hiddenInput' || shouldBypass) { + // activeElement is what element is currently focused + this._preventDefault = _preventDefault; + this._preventDefault(); + } + }; + // Key and mouse events + // Note: the client must have the key, keyCode, and which parameters defined or it will crash + // Note: for text inputs, the client only reads from the "key" paramater + // * an exception to this appears to be the shift and backspace key + // Note: for inGame inputs, the client only reads from the "keyCode character" + function keyEvent(name, state) { + const charCode = name.toKeyCode(); + const evt = new KeyboardEvent(state, { + key: name, + keyCode: charCode, + which: charCode, + }); + window.dispatchEvent(evt); + } + function mouseEvent(number, state, element, event = { clientX: 0, clientY: 0, screenX: 0, screenY: 0 }) { + element.dispatchEvent( + new PointerEvent(state, { + button: number, + buttons: number, + clientX: event.clientX, + clientY: event.clientY, + screenX: event.screenX, + screenY: event.screenY, + }) + ); + } + function wheelEvent(element, delta) { + element.dispatchEvent( + new WheelEvent('wheel', { + wheelDeltaY: delta, + }) + ); + } + function setButtonVisibility(pointerLocked) { + const inGameStyle = document.getElementById('inGameStyle'); + const inMenuStyle = document.getElementById('inMenuStyle'); + inGameStyle.disabled = pointerLocked; + inMenuStyle.disabled = !pointerLocked; + } + // POINTERLOCK + // When requestpointerlock is called, this dispatches an event, saves the requested element to window.fakelock, and unhides the touch controls + window.fakelock = null; + + Object.defineProperty(Element.prototype, 'requestPointerLock', { + value: function () { + window.fakelock = this; + document.dispatchEvent(new Event('pointerlockchange')); + setButtonVisibility(true); + return true; + }, + }); + + // Makes pointerLockElement return window.fakelock + Object.defineProperty(Document.prototype, 'pointerLockElement', { + get: function () { + return window.fakelock; + }, + }); + // When exitPointerLock is called, this dispatches an event, clears the + Object.defineProperty(Document.prototype, 'exitPointerLock', { + value: function () { + window.fakelock = null; + document.dispatchEvent(new Event('pointerlockchange')); + setButtonVisibility(false); + return true; + }, + }); + + // FULLSCREEN + window.fakefull = null; + // Stops the client from crashing when fullscreen is requested + Object.defineProperty(Element.prototype, 'requestFullscreen', { + value: function () { + window.fakefull = this; + document.dispatchEvent(new Event('fullscreenchange')); + return true; + }, + }); + Object.defineProperty(document, 'fullscreenElement', { + get: function () { + return window.fakefull; + }, + }); + Object.defineProperty(Document.prototype, 'exitFullscreen', { + value: function () { + window.fakefull = null; + document.dispatchEvent(new Event('fullscreenchange')); + return true; + }, + }); + + // FILE UPLOADING + // Safari doesn't recognize the element.click() used to display the file uploader as an action performed by the user, so it ignores it. + // This hijacks the element.createElement() function to add the file upload to the DOM, so the user can manually press the button again. + const _createElement = document.createElement; + document.createElement = function (type, ignore) { + this._createElement = _createElement; + const element = this._createElement(type); + if (type == 'input' && !ignore) { + // We set the ingore flag to true when we create the hiddenInput + document.querySelectorAll('#fileUpload').forEach((e) => e.parentNode.removeChild(e)); // Get rid of any left over fileUpload inputs + element.id = 'fileUpload'; + element.addEventListener( + 'change', + function (e) { + element.hidden = true; + element.style.display = 'none'; + }, + { passive: false, once: true } + ); + window.addEventListener( + 'focus', + function (e) { + setTimeout(() => { + element.hidden = true; + element.style.display = 'none'; + }, 300); + }, + { once: true } + ); + document.body.appendChild(element); + } + return element; + }; + + // Lazy way to hide touch controls through CSS. + const inGameStyle = document.createElement('style'); + inGameStyle.id = 'inGameStyle'; + inGameStyle.textContent = ` + .inGame { + display: none; + }`; + document.documentElement.appendChild(inGameStyle); + + const inMenuStyle = document.createElement('style'); + inMenuStyle.id = 'inMenuStyle'; + inMenuStyle.textContent = ` + .inMenu { + display: none; + }`; + document.documentElement.appendChild(inMenuStyle); + + // The canvas is created by the client after it finishes unzipping and loading. When the canvas is created, this applies any necessary event listeners and creates buttons + function waitForElm(selector) { + return new Promise((resolve) => { + if (document.querySelector(selector)) { + return resolve(document.querySelector(selector)); + } + const observer = new MutationObserver((mutations) => { + if (document.querySelector(selector)) { + observer.disconnect(); + resolve(document.querySelector(selector)); + } + }); + observer.observe(document.documentElement, { + childList: true, + subtree: true, + }); + }); + } + function createTouchButton(buttonClass, buttonDisplay, elementName) { + const touchButton = document.createElement(elementName ?? 'button', true); + touchButton.classList.add(buttonClass); + touchButton.classList.add(buttonDisplay); + touchButton.classList.add('mobileControl'); + touchButton.addEventListener( + 'touchmove', + function (e) { + e.preventDefault(); + }, + false + ); + touchButton.addEventListener('contextmenu', function (e) { + e.preventDefault(); + }); + return touchButton; + } + + waitForElm('canvas').then(() => { + insertCanvasElements(); + }); + function insertCanvasElements() { + // Translates touchmove events to mousemove events when inGame, and touchmove events to wheele events when inMenu + const canvas = document.querySelector('canvas'); + canvas.addEventListener( + 'touchstart', + function (e) { + if (window.canvasTouchMode < 2) { + // If a touch is initiated but not assigned + if (window.canvasPrimaryID == null) { + window.canvasTouchMode = 1; + const primaryTouch = e.changedTouches[0]; + window.canvasPrimaryID = primaryTouch.identifier; + canvasTouchStartX = primaryTouch.clientX; + canvasTouchStartY = primaryTouch.clientY; + canvasTouchPreviousX = canvasTouchStartX; + canvasTouchPreviousY = canvasTouchStartY; + + window.touchTimer = setTimeout(function (e) { + // If our touch is still set to initiaited, set it to secondary touch + if (window.canvasTouchMode == 1) { + window.canvasTouchMode = 3; + mouseEvent(2, 'mousedown', canvas, primaryTouch); + if (window.fakelock) { + // We only dispatch mouseup inGame because we want to be able to click + drag items in GUI's + mouseEvent(2, 'mouseup', canvas, primaryTouch); + } + } + }, 300); + } else if (window.canvasTouchMode == 1 && !window.fakelock) { + // If we already have a primary touch, it means we're using two fingers + window.canvasTouchMode = 4; + clearTimeout(window.crouchTimer); // TODO: Find out why this isn't redudnant + } + } + }, + false + ); + + canvas.addEventListener( + 'touchmove', + function (e) { + e.preventDefault(); // Prevents window zoom when using two fingers + let primaryTouch; + for (let touchIndex = 0; touchIndex < e.targetTouches.length; touchIndex++) { + // Iterate through our touches to find a touch event matching the primary touch ID + if (e.targetTouches[touchIndex].identifier == window.canvasPrimaryID) { + primaryTouch = e.targetTouches[touchIndex]; + break; + } + } + if (primaryTouch) { + primaryTouch.distanceX = primaryTouch.clientX - canvasTouchStartX; + primaryTouch.distanceY = primaryTouch.clientY - canvasTouchStartY; + primaryTouch.squaredNorm = primaryTouch.distanceX * primaryTouch.distanceX + primaryTouch.distanceY * primaryTouch.distanceY; + primaryTouch.movementX = primaryTouch.clientX - canvasTouchPreviousX; + primaryTouch.movementY = primaryTouch.clientY - canvasTouchPreviousY; + if (window.canvasTouchMode == 1) { + // If the primary touch is still only initiated + if (primaryTouch.squaredNorm > 25) { + // If our touch becomes a touch + drag + clearTimeout(window.crouchTimer); + window.canvasTouchMode = 2; + if (!window.fakelock) { + // When we're inGame, we don't want to be placing blocks when we are moving the camera around + mouseEvent(1, 'mousedown', canvas, primaryTouch); + } + } + } else { + // If our touch is primary, secondary, scroll or finished + if (window.canvasTouchMode == 4) { + // If our touch is scrolling + wheelEvent(canvas, primaryTouch.movementY); + } else { + canvas.dispatchEvent( + new MouseEvent('mousemove', { + clientX: primaryTouch.clientX, + clientY: primaryTouch.clientY, + screenX: primaryTouch.screenX, + screenY: primaryTouch.screenY, // The top four are used for item position when in GUI's, the bottom two are for moving the camera inGame + movementX: primaryTouch.movementX, + movementY: primaryTouch.movementY, + }) + ); + } + } + canvasTouchPreviousX = primaryTouch.clientX; + canvasTouchPreviousY = primaryTouch.clientY; + } + }, + false + ); + + function canvasTouchEnd(e) { + for (let touchIndex = 0; touchIndex < e.changedTouches.length; touchIndex++) { + // Iterate through changed touches to find primary touch + if (e.changedTouches[touchIndex].identifier == window.canvasPrimaryID) { + const primaryTouch = e.changedTouches[touchIndex]; + // When any of the controlling fingers go away, we want to wait until we aren't receiving any other touch events + if (window.canvasTouchMode == 2) { + mouseEvent(1, 'mouseup', canvas, primaryTouch); + } else if (window.canvasTouchMode == 3) { + e.preventDefault(); // This prevents some mobile devices from dispatching a mousedown + mouseup event after a touch is ended + mouseEvent(2, 'mouseup', canvas, primaryTouch); + } + window.canvasTouchMode = 5; + } + } + if (e.targetTouches.length == 0) { + // We want to wait until all fingers are off the canvas before we reset for the next cycle + window.canvasTouchMode = 0; + window.canvasPrimaryID = null; + } + } + + canvas.addEventListener('touchend', canvasTouchEnd, false); + canvas.addEventListener('touchcancel', canvasTouchEnd, false); // TODO: Find out why this is different than touchend + setButtonVisibility(window.fakelock != null); //Updates our mobile controls when the canvas finally loads + // All of the touch buttons + const strafeRightButton = createTouchButton('strafeRightButton', 'inGame', 'div'); + strafeRightButton.classList.add('strafeSize'); + strafeRightButton.style.cssText = 'left:24vh;bottom:22vh;'; + document.body.appendChild(strafeRightButton); + const strafeLeftButton = createTouchButton('strafeLeftButton', 'inGame', 'div'); + strafeLeftButton.classList.add('strafeSize'); + strafeLeftButton.style.cssText = 'left:5.5vh;bottom:22vh;'; + document.body.appendChild(strafeLeftButton); + + const forwardButton = createTouchButton('forwardButton', 'inGame', 'div'); // We use a div here so can use the targetTouches property of touchmove events. If we didn't it would require me to make an actual touch handler and I don't want to + forwardButton.style.cssText = 'left:14vh;bottom:22vh;'; + forwardButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('w', 'keydown'); + strafeRightButton.classList.remove('hide'); + strafeLeftButton.classList.remove('hide'); + forwardButton.classList.add('active'); + }, + false + ); + forwardButton.addEventListener( + 'touchmove', + function (e) { + e.preventDefault(); + const touch = e.targetTouches[0]; // We are just hoping that the user will only ever use one finger on the forward button + + if (!buttonTouchStartX) { + // TODO: move this to a touchstart event handler + buttonTouchStartX = touch.pageX; + } + const movementX = touch.pageX - buttonTouchStartX; + if (movementX * 10 > window.innerHeight) { + strafeRightButton.classList.add('active'); + strafeLeftButton.classList.remove('active'); + keyEvent('d', 'keydown'); + keyEvent('a', 'keyup'); + } else if (movementX * 10 < 0 - window.innerHeight) { + strafeLeftButton.classList.add('active'); + strafeRightButton.classList.remove('active'); + keyEvent('a', 'keydown'); + keyEvent('d', 'keyup'); + } else { + strafeRightButton.classList.remove('active'); + strafeLeftButton.classList.remove('active'); + } + }, + false + ); + forwardButton.addEventListener( + 'touchend', + function (e) { + keyEvent('w', 'keyup'); // Luckily, it doesn't seem like eagler cares if we dispatch extra keyup events, so we can get away with just dispatching all of them here + keyEvent('d', 'keyup'); + keyEvent('a', 'keyup'); + strafeRightButton.classList.remove('active'); + strafeLeftButton.classList.remove('active'); + strafeRightButton.classList.add('hide'); + strafeLeftButton.classList.add('hide'); + forwardButton.classList.remove('active'); + + buttonTouchStartX = null; + }, + false + ); + strafeRightButton.classList.add('hide'); + strafeLeftButton.classList.add('hide'); + document.body.appendChild(forwardButton); + + const rightButton = createTouchButton('rightButton', 'inGame'); + rightButton.style.cssText = 'left:24vh;bottom:12vh;'; + rightButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('d', 'keydown'); + }, + false + ); + rightButton.addEventListener( + 'touchend', + function (e) { + keyEvent('d', 'keyup'); + }, + false + ); + document.body.appendChild(rightButton); + const leftButton = createTouchButton('leftButton', 'inGame'); + leftButton.style.cssText = 'left: 4vh; bottom:12vh;'; + leftButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('a', 'keydown'); + }, + false + ); + leftButton.addEventListener( + 'touchend', + function (e) { + keyEvent('a', 'keyup'); + }, + false + ); + document.body.appendChild(leftButton); + const backButton = createTouchButton('backButton', 'inGame'); + backButton.style.cssText = 'left:14vh;bottom:2vh;'; + backButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('s', 'keydown'); + }, + false + ); + backButton.addEventListener( + 'touchend', + function (e) { + keyEvent('s', 'keyup'); + }, + false + ); + document.body.appendChild(backButton); + const jumpButton = createTouchButton('jumpButton', 'inGame'); + jumpButton.style.cssText = 'right:20vh;bottom:20vh;'; + jumpButton.addEventListener( + 'touchstart', + function (e) { + keyEvent(' ', 'keydown'); + }, + false + ); + jumpButton.addEventListener( + 'touchend', + function (e) { + keyEvent(' ', 'keyup'); + }, + false + ); + document.body.appendChild(jumpButton); + + const crouchButton = createTouchButton('crouchButton', 'inGame'); + crouchButton.style.cssText = 'left:14vh;bottom:12vh;'; + crouchButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('shift', 'keydown'); + window.crouchLock = window.crouchLock ? null : false; + window.crouchTimer = setTimeout(function (e) { + // Allows us to lock the button after a long press + window.crouchLock = window.crouchLock != null; + crouchButton.classList.toggle('active'); + }, 1000); + }, + false + ); + + crouchButton.addEventListener( + 'touchend', + function (e) { + if (!window.crouchLock) { + keyEvent('shift', 'keyup'); + crouchButton.classList.remove('active'); + window.crouchLock = false; + } + clearTimeout(window.crouchTimer); + }, + false + ); + document.body.appendChild(crouchButton); + const inventoryButton = createTouchButton('inventoryButton', 'inGame'); + inventoryButton.classList.add('smallMobileControl'); + inventoryButton.style.cssText = 'right:19.5vh;bottom:0vh;'; + inventoryButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('e', 'keydown'); + }, + false + ); + inventoryButton.addEventListener( + 'touchend', + function (e) { + keyEvent('shift', 'keydown'); // Sometimes shift gets stuck on, which interferes with item manipulation in GUI's + keyEvent('shift', 'keyup'); // Sometimes shift gets stuck on, which interferes with item manipulation in GUI's + keyEvent('e', 'keyup'); + }, + false + ); + document.body.appendChild(inventoryButton); + const exitButton = createTouchButton('exitButton', 'inMenu'); + exitButton.classList.add('smallMobileControl'); + exitButton.style.cssText = 'top: 0.5vh; margin: auto; left: 1vh; right:8vh;'; + exitButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('`', 'keydown'); + }, + false + ); + exitButton.addEventListener( + 'touchend', + function (e) { + keyEvent('`', 'keyup'); + }, + false + ); + document.body.appendChild(exitButton); + // ---Input Handling--- + // This code is a mess, specifically because Android is so so SO inconsistent with how it handles the keyboard + // Some keyboards dispatch key events, some directly append text, and none of them meet the most basic standards supported by most other devices + // This mess is my attempt at dealing with that, and it will most likely only ever be triggered by Android + // + // It has three main modes. + // 1) Standard keyboard mode: + // This mode keeps the hiddenInput empty, saves the last key press, and on every keypress checks if it the keys are being pressed incorrectly. + // If there is a problem, it switches to compatibility mode, using beforeinput and input events instead of keydown and keyup + // 2) Compatibility mode: + // This most is most likely going to be used by Android, because Android only every dispatches keyCode 229 for any keypress + // When we enter this mode, we grab the last known key press and redispatch it, and programatically dispatch key events by reading e.inputType and e.data from beforeinput + // Unfortunately, Android is weird with this as well. Sometimes it only dispatches insertCompositionText events, and sometimes it gives the correct inputTypes as well + // Additionally, programmatically setting the input's text contents (BECAUSE ANDROID IGNORES PREVENTDEFAULT AGHHHHH) dispatches a repeat of the previous event + // Luckily, we can check if this happens when we first create the input, which necessitates the third mode: + // 3) Duplicate mode: + // If we are getting duplicate inputs, this mode ignores every other input if it matches the state saved in window.previousKey + // If users make it to this mode and still are having issues, it may be best just to remove support for their device + // ---Input Handling--- + const hiddenInput = document.createElement('input', true); + hiddenInput.id = 'hiddenInput'; + hiddenInput.classList.add('inMenu'); + hiddenInput.style.cssText = 'position:absolute;top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;font-size:20px;z-index: -10;color: transparent;text-shadow: 0 0 0 black;'; // We hide the input behind a key because display: none and opacity:0 causes issues + hiddenInput.addEventListener( + 'beforeinput', + function (e) { + // For some reason beforeinput doesn't have the same deletion problems that input has on Android + e.stopImmediatePropagation(); // Android ignores this and the prevent default, so this will probably be removed in the future + e.preventDefault(true); // We pass a value because we've hijacked the prevent default function to have a "should bypass" boolean value + const inputData = e.inputType == 'insertLineBreak' ? 'return' : e.data == null ? 'delete' : e.data.slice(-1); // Saves the last key press. + if (!window.lastKey) { + // When we first set hiddenInput's text contents to " " we can use this to check if Duplicate Mode is needed + window.console.warn('Enabling blocking duplicate key events. Some functionality may be lost.'); + window.inputFix = true; + } + if (window.keyboardFix) { + if (e.inputType == 'insertLineBreak') { + // Detects return key press + keyEvent('enter', 'keydown'); + keyEvent('enter', 'keyup'); + } else { + const sliceInputType = e.inputType.slice(0, 1); // Android doesn't constiently dispatch the correct inputType, but most of them either start with i for insert or d for delete, so this dumb solution should be good enough. + if (sliceInputType == 'i' && e.data) { + // Android sometimes always dispatches insertCompositionText inputTypes, so checking that e.data isn't null is necessary + const isDuplicate = window.lastKey == inputData && window.blockNextInput && window.inputFix; + if (isDuplicate) { + // If our key press matches the last unblocked key press and we are in duplicaye mode, ignore the input + window.blockNextInput = false; + } else { + const isShift = inputData.toLowerCase() != inputData; + if (isShift) { + // The Eaglerclient only uses e.key, e.keyCode and e.which, so we have to dispatch the shift key event separately + keyEvent('shift', 'keydown'); + keyEvent(inputData, 'keydown'); + keyEvent(inputData, 'keyup'); + keyEvent('shift', 'keyup'); + } else { + keyEvent(inputData, 'keydown'); + keyEvent(inputData, 'keyup'); + } + window.blockNextInput = true; + } + } else if (sliceInputType == 'd' || !e.data) { + keyEvent('backspace', 'keydown'); + keyEvent('backspace', 'keyup'); + window.blockNextInput = false; // If we delete a character, there couldn't be a duplicate of the previous key press + } + } + } + window.lastKey = inputData; // Saves the last key pressed + hiddenInput.value = ' '; //This previously allowed us to have a character to delete, but beforeinput doesn't require this. This does allow us to check wether Duplicate Mode is necessary though + }, + false + ); + hiddenInput.addEventListener( + 'input', + function (e) { + // Since we are using beforeInput for input detection, setting the text contents of hiddenInput causes weird behavior, so we use input instead + if (hiddenInput.value != ' ') { + // Avoid updating it if not needed so Duplicate Mode doesn't throw a fit + hiddenInput.value = ' '; + } + }, + false + ); + hiddenInput.addEventListener( + 'keydown', + function (e) { + // Enables Compatibility Mode if we receive an invalid key press event + if ((e.keyCode == 229 || e.which == 229) && !window.keyboardFix) { + window.console.warn('Switching from keydown to input events due to invalid KeyboardEvent. Some functionality will be lost.'); + window.keyboardFix = true; + if (window.lastKey) { + // Resend the last saved key press (which is being tracked by the beforeinput event listener) so the transition to Compatibility Mode isn't noticeable + keyEvent(window.lastKey, 'keydown'); + keyEvent(window.lastKey, 'keyup'); + } + } + }, + false + ); + hiddenInput.addEventListener('blur', function (e) { + // Updates window.hiddenInputFocused to reflect the actual state of the focus + window.hiddenInputFocused = false; + }); + document.body.appendChild(hiddenInput); + const keyboardButton = createTouchButton('keyboardButton', 'inMenu'); + keyboardButton.classList.add('smallMobileControl'); + keyboardButton.style.cssText = 'top: 0.5vh; margin: auto; left: 6vh; right:0vh;'; + keyboardButton.addEventListener( + 'touchstart', + function (e) { + e.preventDefault(); + }, + false + ); + keyboardButton.addEventListener( + 'touchend', + function (e) { + e.preventDefault(); + if (window.hiddenInputFocused) { + hiddenInput.blur(); + } else { + hiddenInput.select(); + window.hiddenInputFocused = true; + } + }, + false + ); + document.body.appendChild(keyboardButton); + const placeButton = createTouchButton('placeButton', 'inGame'); + placeButton.style.cssText = 'right:6vh;bottom:37vh;'; + placeButton.addEventListener( + 'touchstart', + function (e) { + mouseEvent(2, 'mousedown', canvas); + }, + false + ); + placeButton.addEventListener( + 'touchend', + function (e) { + mouseEvent(2, 'mouseup', canvas); + }, + false + ); + document.body.appendChild(placeButton); + const breakButton = createTouchButton('breakButton', 'inGame'); + breakButton.style.cssText = 'right:19vh;bottom:41vh;'; + breakButton.addEventListener( + 'touchstart', + function (e) { + mouseEvent(0, 'mousedown', canvas); + }, + false + ); + breakButton.addEventListener( + 'touchend', + function (e) { + mouseEvent(0, 'mouseup', canvas); + }, + false + ); + document.body.appendChild(breakButton); + const selectButton = createTouchButton('selectButton', 'inGame'); + selectButton.style.cssText = 'right:6vh;bottom:49vh;'; + selectButton.addEventListener( + 'touchstart', + function (e) { + mouseEvent(1, 'mousedown', canvas); + }, + false + ); + selectButton.addEventListener( + 'touchend', + function (e) { + mouseEvent(1, 'mouseup', canvas); + }, + false + ); + document.body.appendChild(selectButton); + const scrollUpButton = createTouchButton('scrollUpButton', 'inGame'); + scrollUpButton.classList.add('smallMobileControl'); + scrollUpButton.style.cssText = 'right:6.6vh;bottom:0vh;'; + scrollUpButton.addEventListener( + 'touchstart', + function (e) { + wheelEvent(canvas, -10); + }, + false + ); + document.body.appendChild(scrollUpButton); + const scrollDownButton = createTouchButton('scrollDownButton', 'inGame'); + scrollDownButton.classList.add('smallMobileControl'); + scrollDownButton.style.cssText = 'right:25.8vh;bottom:0vh;'; + scrollDownButton.addEventListener( + 'touchstart', + function (e) { + wheelEvent(canvas, 10); + }, + false + ); + document.body.appendChild(scrollDownButton); + const throwButton = createTouchButton('throwButton', 'inGame'); + throwButton.classList.add('smallMobileControl'); + throwButton.style.cssText = 'right:13vh;bottom:0vh;'; + throwButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('q', 'keydown'); + }, + false + ); + throwButton.addEventListener( + 'touchend', + function (e) { + keyEvent('q', 'keyup'); + }, + false + ); + document.body.appendChild(throwButton); + const sprintButton = createTouchButton('sprintButton', 'inGame'); + sprintButton.style.cssText = 'right:19vh;bottom:53vh;'; + sprintButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('r', 'keydown'); + window.sprintLock = window.sprintLock ? null : false; + window.sprintTimer = setTimeout(function (e) { + window.sprintLock = window.sprintLock != null; + sprintButton.classList.toggle('active'); + }, 1000); + }, + false + ); + + sprintButton.addEventListener( + 'touchend', + function (e) { + if (!window.sprintLock) { + keyEvent('r', 'keyup'); + sprintButton.classList.remove('active'); + window.sprintLock = false; + } + clearTimeout(window.sprintTimer); + }, + false + ); + document.body.appendChild(sprintButton); + const pauseButton = createTouchButton('pauseButton', 'inGame'); + pauseButton.classList.add('smallMobileControl'); + pauseButton.style.cssText = 'top: 0.5vh; margin: auto; left: 0vh; right: 0vh;'; + pauseButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('`', 'keydown'); + }, + false + ); + pauseButton.addEventListener( + 'touchend', + function (e) { + keyEvent('`', 'keyup'); + }, + false + ); + document.body.appendChild(pauseButton); + const chatButton = createTouchButton('chatButton', 'inGame'); + chatButton.classList.add('smallMobileControl'); + chatButton.style.cssText = 'top: 0.5vh; margin: auto; left: 0vh; right: 14vh;'; + chatButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('t', 'keydown'); + }, + false + ); // For some reason dispatching a keyup event for this closes the chat, which is really weird + document.body.appendChild(chatButton); + const perspectiveButton = createTouchButton('perspectiveButton', 'inGame'); + perspectiveButton.classList.add('smallMobileControl'); + perspectiveButton.style.cssText = 'top: 0.5vh; margin: auto; left: 0vh; right: 28vh;'; + perspectiveButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('f', 'keydown'); + keyEvent('5', 'keydown'); + }, + false + ); + perspectiveButton.addEventListener( + 'touchend', + function (e) { + keyEvent('f', 'keyup'); + keyEvent('5', 'keyup'); + }, + false + ); + document.body.appendChild(perspectiveButton); + const screenshotButton = createTouchButton('screenshotButton', 'inGame'); + screenshotButton.classList.add('smallMobileControl'); + screenshotButton.style.cssText = 'top: 0.5vh; margin: auto; left: 28vh; right: 0vh;'; + screenshotButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('f', 'keydown'); + keyEvent('2', 'keydown'); + }, + false + ); + screenshotButton.addEventListener( + 'touchend', + function (e) { + keyEvent('f', 'keyup'); + keyEvent('2', 'keyup'); + }, + false + ); + document.body.appendChild(screenshotButton); + const coordinatesButton = createTouchButton('coordinatesButton', 'inGame'); + coordinatesButton.classList.add('smallMobileControl'); + coordinatesButton.style.cssText = 'top: 0.5vh; margin: auto; left: 14vh; right: 0vh;'; + coordinatesButton.addEventListener( + 'touchstart', + function (e) { + keyEvent('f', 'keydown'); + keyEvent('3', 'keydown'); + }, + false + ); + coordinatesButton.addEventListener( + 'touchend', + function (e) { + keyEvent('f', 'keyup'); + keyEvent('3', 'keyup'); + }, + false + ); + document.body.appendChild(coordinatesButton); + } + // CSS for touch screen buttons, along with fixing iOS's issues with 100vh ignoring the naviagtion bar, and actually disabling zoom because safari ignores user-scalable=no :( + const customStyle = document.createElement('style'); + customStyle.textContent = ` + html, body, canvas { + height: 100svh !important; + height: -webkit-fill-available !important; + touch-action: pan-x pan-y; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + outline: none; + -webkit-tap-highlight-color: rgba(255, 255, 255, 0); + } + .mobileControl { + position: absolute; + width: 9vh; + height: 9vh; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + padding:0px; + background-color: transparent; + box-sizing: content-box; + image-rendering: pixelated; + background-size: cover; + outline:none; + box-shadow: none; + border: none; + margin: 1vh; + opacity: 0.5; + } + .mobileControl:active { + opacity: 0.75; + } + .strafeSize { + width: 7.5vh; + height: 7.5vh; + } + .smallMobileControl { + width: 6vh; + height: 6vh; + margin: 1vh 0vh; + } + .hide { + display: none; + } + #fileUpload { + position: absolute; + left: 0; + right: 100vw; + top: 0; + bottom: 100vh; + width: 100vw; + height: 100vh; + background-color:rgba(255,255,255,0.5); + } + .strafeRightButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAARRJREFUOE+dk0EORTAQhkci2HAEF3ESJ7Rgyw2cwAEkVhI7QhDJe/n7Mk3R0rxu0M7/dWb841RV9SHDmufZdHTbdwCCoO97a5EaOAwDtW1LEtQ0zQnkui4dx/EKBwTLyfNclAZQFEU0juOjGDHLsshLANr3/QdCWUjRdqnZWoO6rqO6rk93pGkqvx9BV3EQBFK4ritZgbIsEyJVrKajA03TdO8RQCYIgAziPqE0LQh/rSxLLQyQJEkojmN6BeFWE+xaFmKNGXE/dDATSBqSfXQ1mwpTy4KQzXtyNoN0Y8EwiNXfzplbG1Ln9r+c/TY2p2ZjYLnWN6HuXPqoKAryPE/EYDMMQzHRWNu2Sa3v++KdYzkezy+eN1EbyruiHgAAAABJRU5ErkJgggAA"); + } + .strafeLeftButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAUFJREFUOE+dlDGqhDAQhrMgKhYWHsCLeARP4Lk8hIW9N7CxtbQQrAQLQVFUhPf485gwyWbZfZvGJM58zvz546Oqqh/BxrZtfPnx/EEgEzCO40eQaZpE3/dCggBp21ZLdBxH3Pf9BOP7mHddJ2NegoiA4CAIxL7vVjCqkaCyLKVGqOhVFbYeKRag67p0ECUURaHlJkki4jjW9sIwFMuySH0UCMJCNA7yfV+tj+NQcxP6LxAvhaBZlsntr0FIBoyD1nX904haIwGHYRB1XQveHlUFSJqmAhpRRU8g3gIEN0EEiaJIWQGtaaB5njWfmCCzEvqo5iO0RiBbezYI95E0ZJ7n0pD8+E0/cU2443GF1KlxEJns3W3lcVbQO4DtvRIbFdGv4BsQcuSpAdQ0jWLg3tA4z1NOPc+TT9d11TskY59ifgE9QD9CDm6XPgAAAABJRU5ErkJgggAA"); + } + .forwardButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAcZJREFUSEulVEuqwkAQ7OAnoujalRfxCJ4gl/ESHsKFa72BJ3AhIiJk60IwKH6ZR43U0JlMiOH1xsx0T1ldU9PRarUy4sX1evW3aq+jxWJher2ePXg4HGoD+AdOp5PsdjuxwEhuNpsCaLPZlPf7bfcHg0Euf7lcROeZPB6P9tMBb7dbB8IiHEQQvKod1O/3+y/wbDYzw+HQMQaz2+1mwTQjMAwxRz1zBM6y7AuMA9CG7bKQDLFeLpd2OZlMXJ0vRRD4fD67ln0J5vO5ZU9ZkiRxqmjwghRkrLWlrmmaynq9ljiOJYoiud/vMh6PZTQaBS8bGj+fz7wUqPTbA9tGoyGtVssCvV4v+Xw+ollrQqXAmgbZdjodt22MkcfjUcoaditcni+Fz5ZO8VnrLp2PtSu0FJotWEJfBL4RYE2H+MA5xijUNtNsAQZd6YwyrStdEdKWbMkca+0QPqzcy/PtRrZgSZuFnrPW+mfgqrmg87CeBg76uA5gqBauKABzVmAIMbrdbm7IcB/W0wOIjvrpgdQdmwAPMtZ28tsEc90JH4vugMDOxxiZfDH/1RjnLfB0OjUhUCT7/X7wf5CDDRHtdtv+4sK4Rv4PfpOwZeVjdiYAAAAASUVORK5CYIIA"); + } + .rightButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAVhJREFUSEudlT2qhTAQhSOICKK1lRtxCS7PRVjYuwaxtbKyFewURUS4j5PHyNy8/OibKpiZb04mmdFrmuYjDLZtm2nL+d1TwSpsmiYJSdNU0NpGnedZjOMovLquP1EUibZtnSqeOAAKk2As+r53xvm+L67r0volSSKWZZFqX4MRoMIJSN+d4KqqZOY8z0WWZV8qbcoBXtf1txS4FBSdBwAchqE4jsOYQFcTgM/z/AZzRwLTN1sCLug1+EkC+AzDIIIg+KuYLkNVrB6bTlAUhUAMmbbGtlJgD08Nx+aGBBz+L7Dusozgsixlg+BV6BTrVMLPWQoX2FRb/r5R433fZanuy3sKtj03Dr47zwV2NYjahY/Appa2Tau7QbhiOhIFmibZazACqEF0k4yAfKKpIrSKbUqe7t0NglLQ7+RpsMtPjk2Au66Tow7DgxscyOI4lkv4wbgv9yP/HwHyemKMQPbVAAAAAElFTkSuQmCC"); + } + .leftButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAUdJREFUSEutlTuqhUAMQEcQEUFrt+a6XIa17sDG1sJSsBLsFEVFuJcEMoxhPm8uL5WOycl3YtA0zUcYZN930yfneaCCVdA8z05jncKyLGIcRxFUVYUR933/E4gbAfS+bzs4DEPxPM/LVncGClmWiXVdMVorGAAgHOxKC8AgP5VimibRti0CiqJ4+ZLgsiw/eZ6LYRic0anAOI7FeZ5a8LZtIgAwuIRumoQDSc8ExlLYwCYgB1Pj4PxVCh4xdLeua7SHlE3iFTFBCQhTQRPCHXjVmINt4+UFBpCrFJSFN5ii/Gvz1KyszePp/8u4qaPjckClAJvjOPCCeUVsc6C70riEdBcExitJEmyiLQPdxMjtxsGUFhnptptpddLNM0bsWo3qd+7kFTH9TnyANl253bquk3qQRhRF+A7PXK7rkkdpmqIO6QOQ5AuINXqeGbRlFgAAAABJRU5ErkJgggAA"); + } + .backButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAXtJREFUSEuVlT3KhEAMhiMsKgt6BC/iETyB59pDWFjrDWxsLSwFK8FCUFZ2RdiPCBky+ca/wBbjxGfeeROzVp7nPxDxfr/lo9trC8F7oK7rbgP7voemacBK03RTXFXVbYjpBYRiKHBd17Cuq8r1fR/meYbn8wnjOGqMx+OhcjGP7yN4WRazYp6MEAx+KK45nK81MHqJ3pyFhJ1aIcFJkpydoe3HcazWmscmsOu6l+CfzweM4NfrtXUFt6JtWyiKAo7g6Dn+wjCEIAiU56h4miawCDwMg1YgtIPACKAi8mtItbiniscV8+KcqUYoqeWHGcHSVK5a7km1JEoVz+QxQfZUm9ReBlOiSbXJWw7Wise7gn95UvWet3TLf11x9OVx1Sa1XMguGK8kBw8OmSzLNlFRFAGCKAh66vHecKEJxqGHs+KoK64MHQm/1G7yJf71yTHKi7cNelRMfyeXps7FpA1clqVKx+nP4/v9bkvP87Tn2KsUjuOAbdtqjXt/DvSA6WCSwboAAAAASUVORK5CYIIA"); + } + .jumpButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAARlJREFUOE+dlD2KhVAMhSMIWrkx1+CqXISFtS7C1sLSVrAQFEVFmOFcyH25eXNVJpWS+OXkz6Cu6x8Stq6rfH39HEjQX5BhGG5h4zhS3/cUlGVpFLVt+yp7GIZ0XZeNBQTmgJIkoW3bjEMGywwcw36AzvN0QTrbPM9UVZXhpGlKgGgDaFmWD6jrOkcFQ+I4Nt/u++7AOOkXSPZIQ1AGPtQwJHBAmAy6D9MQXYqGOT2SoKIoiMvxjRGwLMtMKywoz3MzflakQVyWhGqQGb8G/ac0BzRNk52aD3bbbK2IS3gaP8fZqWmQXMq7hfzaI5+ip8PDliPRK0USBgVs+mjNiUAR/wqeVPj8FtQ0jY3BJcOO4/ByoyiyPo77BUACR9z5EdScAAAAAElFTkSuQmCC"); + } + .crouchButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAASVJREFUOE+dlDsKhEAMhkcQtPJiVh7AU3kCKwsLKz2ErYWlrWAhKIqKsEsCmc08fLB/pSR++ZPM6FRV9RFMy7Lw19fPDgfZIH3f38KGYRBd1wknz3N01DTNq+qu64rzPGUuQEAKKAgCsa4rBngyr0A5FAfQcRwqSK82TZMoyxI5YRgKgOgC0DzPP1DbtooLgvi+j99u26bAqKgB4jPikCiK0EmapgYMCigg2AxMH6RDeCtFURgwZUYclGWZgHbAiU0Ei+MYRyFBSZLg+smRDoK2wCXJBsL166B/WlNA4zjKrdlgtvkow9YdUQtP66c8uTUdxA/l3YE0ztGVo6eLR0t45YjDwAFJv7R4RcAR/QqeXFzFJaiua5kDNxm07/sl1/M8GaO8L0fuVtxHFTIkAAAAAElFTkSuQmCC"); + } + .crouchButton:active { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAStJREFUOE+dlLGKhEAMhiOICqKdYGthaetL2GhlNQ9nZWFh5YtZiIKwRwLJjXHc3bu/WZnJfPknyazXtu0LACAMQ/whHcch3/qD44IgkK26rsFDUJIkjwc/bRRFAVmW/YJw4Rv5vg/neUooQlDiCEFpmsK2bbRhB9sJOIb3nSCdbV1XWJaFOE3TUCKtG6gsy4sLhkRRRGf3fb/AOKnTEWezIV3XkZNhGG4wjBeQMYbaz8XWEPsq8zzfYI+gcRwBr4NOXGKYMYZKIaC+7184XOxIg/Ba6JLlAuV5Dp4G/edqBOLJtrvmgrnqgy6rqroPpF2TT+3n2EeQPZTvBpLjBKRr9M17wxhugoD0HOlnwmBcZ9nv8ALiv4J3brAzWnEcy5I3TRNN9l9lQ/DsD7Mvyc0ay6bsAAAAAElFTkSuQmCC"); + } + .inventoryButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAedJREFUOE+tlM1OwkAQx6elTYiCkSMnvKAX38KLr+DRm69hfAIP3jwbE24ceA4+TDBpCMWEFGgMBCgBUlrzXzLLLsVYo3NpOjv72/987Bq1Wi2mf7AgCATFYOB0OiXTNCV6s9mQYRiUyWSkbzKZHDx6uVyS7/vU7Xa3wPl8Ts1mkxgSx7EAAQhfFEUaHH7ECEWGIb6u6+4UAliv16VCAMIwJMuyxAb8Q72qdl8q1MmUkW6j0dCAgADAZYAiVoON+OeD4O/1erpCABEAs237YK14nRcBRUlg/X6f1uv1toYodrvdFikiVQ7CBiiEArVhh05DyhLY6XRoNBpJIMAMTTtRCeBwOCQ057h8KRiB80a5XC61DzXUFAIYF8/o/u6W3t0Penh8ovJJNuErZU2yS+eJOMdxdk1Bygy8ub4iz/+k55dXCfzJd3F6JIZaU+h5Hi0WC/IiS5xUNEMqFAo0Ho81Xz6fp9lspvlQGplypVKJB4MBAci3I20j1DipUAWqQ4yZ+24ecfD+FCSAqKFq2MSDrN5bvi081BgxWAKIOVSvFwYcm9W7zI+BGsfXL1FDBvImfgiQGgNYKWfCzxti8NqgWQbXEO9ZGuMUkcF+UySw1WqJGvzFMIOr1WqrsFqtaiws/sYAYvsC6d7MZCUlFo0AAAAASUVORK5CYIIA"); + } + .chatButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAh5JREFUOE99lN8vA0EQx2fjkYY3HvB/8V4kEg8SBJUGVVJU4rfeVRO01bpIT8VviYjEq2tF678g5ZGVmbm9Hy02l9zd7MxnZ747u+Lhsig7OrtBDQkS8BFCkAn/BQiQUrILmvHTfqOfeXEDAkE4397Z5TqQn/BBmMtwtQACTy5vQU8kQTxcFSmJSqncsKAT4AGohBQwrqcBpHQzUiDlUB/gpFL3QSAsX5WGIBUcHJn4K47sWmyONERN4toBfftA6ISQz9q7q4ONRM2UiM2BVtAW50izFT3N2t9fmKTRa7lMhr7REIF2tExDVv3BXtqAlkAbJGIRml9JZmhjxN1ZgXatalk0MTAehs/am73DvHOubrxrmNH2/DSDUjkW+7ZokGfFYo0GQ9PwUXuDhJb9Vaf+YA80B9pgPRIm/9W9QwZdF3LYflCxShQ4FI4QSPWRl6ZsWNrqTIgkW9vPc2nnRtbOiEHDs/P/gNBDUGnxqXHqz80Dg97iNLcvsWtfnrghv7+/YGxhmWA4cPX6sTQ5xtpJgK3DY8B4YWZSEvsANcJRD4mODjeAmiiSu2EnbzKokN6l81gpPVPARCzuBP4GoeKwGe1DnDwy+UwqULWMIHe7fSeepXFuBVUWmvWjAouNIDS8WJyRs6K6PjzXinOVeIr1ZJSSj6UqGHmDU8SC7R70/3NrqnnVqM51oy9FZXRDd7Oxa+C7y9/ZXKGnfI9WP7ZEKinpenUjAAAAAElFTkSuQmCC"); + } + .pauseButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAjJJREFUOE9lVO1qE1EQnXkK+6P6FP7oI/gMraIoFIMr1WL9aNEfRWNtS1Li1ti6aYr5sEkXyRrRRC2IFNqCgptIE5/myszcO7tbLwuZPTv3zJmTOxcP+10zMXkB3DJggB5EZIjeERCMMZJCMIX2l/Ki3jdAIqLv5ybPJwmchxkS4RVyV4AIP/QPINiuAB5+6bKI0WD4X0HdkCJwghxhIWgAGJMockQu4ewGlXImYCJq37VGROn2RU3WlqQvwx7S98KbOscZIpc4ffOW7mm+3uRYMHFZMImLQUOiH72IPfo7HKqCmdxtNbRZ9jmeyXm8kZIdRnix0uQ/Br9/6nAH4zhWsy97c7YeQt3f4PiKd0dV1vwNjYvVlph90A2ZaBSLR7Suzt21McJuqUB5cE0xgN1SkTHKL73dE6KvnRZDo3igVa7P39NztFNYZ/zG/IK2y5j9Z17W2tLa5/CdVWSJEGB24YESba++YILZ+4TRQthaW9HD+6oeMil+bNUMndrT3/ZAIkLu4aJWLz/Pc5x7tKhE5ZU8m05Pee890H6MmlVD54A8cstbeqxm+8+WGfaWnujI+E+XtbWtdiREncYOz+No8EeEI0Lv+KcoMgYuTV3kuH/ySwsR5oa4sh9JAUc0HhKRnBNxIjXxAuit4NoiONjviNlERMBpLIqcKr0+UteKXiWaCZBSVDVHgzGE7VAkUsN2yLLvotV9zyinfcF63uQ3g0SN7UEmKWlV1abbp8G1F94/2dEyKXciad8AAAAASUVORK5CYIIA"); + } + .exitButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAiBJREFUOE99U11rE1EQnfsvFKwg/QkK+iaIio+KJNAnbUQoRaGgf0NRTFPU3TTJBjfdTZpN/aJtUHwU8anJJlH/y8o5c+92k1bnJXvn48yZMxNzmETZ2aXzQjMikunn6YYgkuZtb/+LGAAZY+TMuaX/IfwzBpBWMxAzHMQZaEyPxkw+rWeRpCPteFWDWIdRRiITC+Qm1KgRybJ5cOtzFKtBxO5mOIiyLBOZjca5RqXVCvPiRl01QYKIlCsVFu1sw6/p1VbEhgSCa3I0IoHSfQVxFtsigBfJRNt1BXKjHfQ77DcbpQyUKw9yEBSiCSkXDP6O7zN/sx3raJ97IcWejVOOEO1/lX4cHoMVFuDg7pRWpHzrGnO2gq6O+SFqM06NrPWG3wi2eFZ43y6tyN3rV/PcN+GuAu2FDTL/lU7owI6MGLm3vk6NcWMa0O01a5vHQxoRrzPQmt22bxkpEOpWHz2eE9aBoSNg69VXtomIFw1Uo27zbc4IHw83NsjCCax6z7/R2Xv5gmD13nslsONvcSe/0ykDa0+f6N3YYggLS7qhOye+Xz97zt9G/yNZmtCrZSjC+mEH33/mWwPIzSuXyO5wwX/j8kWOHySflFHo16jRdKQawVAEQ7JOpqOd9Iu0yMiIeefV+F+bjSe5gAv3d0L4on5kZOxoP9I/kvSTvDM6uCPiai0j+ItvsnU+PJYvLOteYfZ89WqKiZrg/LkONucvWvgqB/7CAfgAAAAASUVORK5CYIIA"); + } + .keyboardButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAfdJREFUOE+VVN1KG0EU/ubait7ZC9un6IXP4BvU9qJQJFhsEUGpPkAsWJL+xEjMxhRjEhMXyRqx2lYoRfCmF81uyca3UGKvp5xzZpJsFI3Lwjkz88035+ebUafHdf1w/DHsp6FBv1KKp2isoKC1FghNk2ss4byjH1BEROtj44+6AMapCInwCrk9gAj3j0/gbOSgTr/VOYjQ9yPAzo47nIRTBLTuRhT6waB7IzgmovRtakTEaRO7orR4cKefyG4zJkLUPcpWU6rRrc11P+kUBfHryOMatYIAz2Ze96VnOmXaZTtoi11aTyGZK3Fj1M/DGqPPGw08n53D0PAI/rUv2V61L/BgePTamPCEKaQ+IJmvSAlO6i4TtfwAL97MRyLiUI0UIm03c18+JvBpa0eIvtcqJD+EDR8v5xeQyZYQm5661RIpYXKJVXwuVCW1r26ZjyWi2OJbo2UR5E1RWDkSIrP6Dultl3uhDioFTapt/gnwamkZmWwZsemnbG/67BrZ9Eoc6zt7oP3KK+U16SBsBDg8+30vUU5OPEGm6glRrbjJ9zH0/0phSYzmgg7i53Y9uZP9RJ3bLe0SPd7iO7s1KTYREa5JEfXqb8AkeyLK6zO/BbfqSoiUsCGMjkVVdr1XY4xz3sd1fM3pnC9tN5vMm9QbnH2n+uv5HyQVKCmWNUnHAAAAAElFTkSuQmCC"); + } + .placeButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAd5JREFUOE+tlMlqQkEQRfs5gAv10xwQEf1Fd4J+h8PChYi6EKeNOCvqC6fltv2ISQxJgQjd1afq1vCCVqsVmn+w/X5vKYGAm83GxGIxh77dbiYIAhOPx93Zer1+Gfp0OpnVamVGo9EDuNvtTLfbNYKEYWhBADm73+8ROOf42IyCwP6Px+NnhgDb7bbLEMD1ejWJRMI+kJxsNvtlccjOSUZup9OJAIGS5eFwMM1m0zpXKhUHJEN8KBNBJ5NJNEOAOGDJZNI9JBjAVCpl8vl8JEOglASbTqfmcrk8akix+/2+lYhUOfHgeDxaIHflcvlbyQ44HA7Ncrl0QB4L6mdYLBbfBy4WC+tMg3xLp9MOTq2oqW+ZTMZ2nBpGMgQITA3QI78Rr+5rtZoNOBgMnk1Bsg+kATK/EQLqnnqXSiXbacYmkuFsNrOpMyr86Di1ZAtoDIZ8bQ9Zabi5c5Lr9Xo4n88NQG2HXyM1hbNqtfpzU3ygdllDyzwK6I8NgTUFiuAkC6guy0Gyt9utG+xCoeB2V0NNIOwTkDnESctOwTUm2pRcLmfPfD+t36caCkg0/7Ol4vtfGKnwG8TXBjWBJPM9e8ckEQW+IdkBe72ercFfjBk8n8+PDBuNRoTF5W8MkOwDqQTfZInxo/8AAAAASUVORK5CYIIA"); + } + .breakButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAeRJREFUOE+tlMmKAjEURVMO4EL9MZcKKn6iO0G/w2HhQkRdiNNCcVbUak7kplNlNU1DP6iFMXXq3vvyEnQ6ndD8Q51OJ0sJBNzv9yaVSjn08/k0QRCYdDrt1na7XeKnr9er2Ww2ZjKZvIHH49H0+30jSBiGFgSQtdfrFYGzfjgcHLxQKJjpdPqtEGC323UKATweD5PJZCyI36iXWty0220HrNfrVp2zzIZerxcBAgGgGFANnI8Dy+VyFoDdarVqZrNZVCFAIFQ2m03MSsoEwwVPuVw28/nc3O/3d4aEPRwOrUU2kBuFKhQmKZM67FJYdsDxeGzW67UFbrdbu6FYLDqVcWVxWCJwtVq5fNhQKpVMPp//yEw2a7Wa+yBOyDCi0AeSEWEDpQEo55GySqXiOk7jiGg0Gn03BcsAdb7URaBqgN9R5eufhI8MF4uFbcLlcokcC/nS8dD0oIr9Kme52WyGy+XSANR0xJsATN1MPE9+l33g+Xy2+5Ms+1Dl5sOdZQHJUeMUbwovak1KsYttNetHoF6kk+TlqwbMmGkM+a05/8jQV8jGRqNhHSl8ZpjibKr8BnHbcAMFssx9RjNU/qT4WckiBzyeoQMOBgN3/fzUxd/WmZLb7fZW2Gq1Ivv58y8FSPUFhmoAc4SumvkAAAAASUVORK5CYIIA"); + } + .selectButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAcRJREFUOE+tlMuuAUEQhqsZCWJ4KAsLL0pG4pKQWHgHzMJK2LlFIm5BMCd/SbXumRGccyoRme7O13/VX9Wq0+kE9A9xOByYogS43W4pkUho9O12I6UUJZNJvbbZbGKvPp1OtFqtaDKZPID7/Z6GwyEJJAgCBgGItfv9bsGxjjOsSCn+n06nT4UA9vt98jxPKyiXy9Rutz/6xtn1ev0EIt3BYEC1Wo3S6TRvFItF6vV6dL1eyXEc/Y09rJVKJd5HuhEgFAIohf3Uo263y3ABXi6XRw1R7NFoRJVKhdXg9mw2y3WCUaiTaZhciMwkkLIGjsdjWi6XDETKSDefz78VWq/XrZQt4GKxoGq1qoG5XM5qIXFUlEI1ah6bMhQCiFpKZDIZqwexjvYBAC2FX7PZ1Apns9nTZQG+UhiXO+prmhipIW5AH0oNXddlg9AWpikyPWj4RqMRraHnecF8PicBisu/NsUEHo9Hq4apVCrWadQPCmNdFmDY5UKhwCYgzLmV2cXabreL9qEAw32IKUCYYxc3hubocR+GgYB88zDIebgMxRqI98wcpbdjEjpgAX3f58fxL4F0z+fzQ2Gr1bJY2PwmAJL4ASn3ymSCH/2UAAAAAElFTkSuQmCC"); + } + .scrollUpButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAfhJREFUOE+tlL1OAkEUhe8sUAkklHQ2dj6Q0crEwsTCWFlZGJ/Aws7amNBR8Bz8mKBBwmJClp+YEGAJkIU1Z8gZdlnWaPQ27A53vnvO3TujSqWSL/8QrutqiiJwNBqJZVkGvVwuRSkliUTCrA2Hw52lZ7OZDAYDabVaa+BkMpFqtSqE+L6vQQBibbVaheBYR45WpJT+tW17oxDAcrlsFALgeZ4kk0m9Ae9QH1S7LRXqjGXYrVQqISAgAACEgox0Oq0foZCFULTdbocVAogERCqVCglAwb2DQ73mNl4kCEVLEJ1ORxaLxbqHaHa9XtcWYZVJUAGF+IIA3p6fysnVteQtT7LZbKgoLBtgs9mUfr8v4/E4doAIRMIuaATY6/XEz+9/O5FQ+Gp/6Jy7+wetNJPJ6H6ihyGFBGITg5u3qziDT730+PSsoehpo9HYfBRYJvD97TVW5c3lheyCoc8Ry47jyHQ6jYU5q6ScHR9FlHGDsVwoFPxutysA8nRsUzE2BNJm7FcOAnmWObScRwJRCD3L5XJmtFjcWCYQPQwG1HLQg+NEZSiKecXsIiJAzCGSeNgx4HgOnmVs5MXAPB6/SA8J5CZeBFDBQrxh6ITXG3Jw28CJomXcZz8JWoSDYMCyAdZqNd2DvwROyXw+XyssFoshFv78TQDE+AI1+8NkGpdDjwAAAABJRU5ErkJgggAA"); + } + .scrollDownButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAfxJREFUOE+tlMtKY0EQhqtzAdEkuPUBxI0PJO4EF4ILn2AYfItZDLhzBrLLIrjxHXIRIoSQEyGcXBgIuSckOfJ1qPYcY4KD1uZ0OtVf/39XdZt8Ph/IN8RoNLIUo8B+vy+xWMyhl8ulGGMkHo+7uV6v9+HW0+lUut2u1Ov1NXA4HEqpVBKFBEFgQQCZW61WETjz5FhFxtiv53lvCgEWCgWnEMBisZBEImEX8Bv1YbXvpaLOWcZusViMAIEAGI/Hbm0qlXJjFOpGbNpoNKIKAZJAJJNJ+2Wjg+NTOx5VnyQMZA4oR0I0m02Zz+frM+SwK5WKtYhVklAG7PbqQn7+urPATCaztR+w7IC1Wk06nY4DDgYDB3v2XuTPw6MFbgs22gC2222bT4FUGTAC4K4wvmfPMKIQ4Ecwv/tvJ+z3/V85zuxJtVp9KwqWAVIt7PqrhPy4uRaFsWhXnBzub1r2fd9WjVZBKdDL8zPLAXgUW2xlUn1nOZvNBq1WSwDq7dCWUagCP1XlMFDvsjbtZDKxSgkUKpCNtf9UtquyArXKmsAiGp0z1Uin0+7ualPTu8QGkD4kSS87Dc44fJdZqA+D5un12zhDBeoifQiwphvpC6OK9Xkjh9cGN0Yt8559JtQiDsKBZQcsl8v2DL4S3JLZbLZWmMvlIiz+/J8ApPEKFYzOZJBKmFAAAAAASUVORK5CYIIA"); + } + .throwButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAehJREFUOE+tlLtOAlEQhme5VAK9D2DnG1nZ2VpZG5/Aws7amNBR0PoAFnIxwYQQFhOyXEJCuEOANd/BOeyyG4PRaTZ7Lt/5/5k5xykWi778Q0ynU0NxFDgajSSRSFj0ZrMRx3EkmUzaseFwGHv0YrGQfr8vzWZzB5xMJlKpVEQhvu8bEEDGttttCM44a4wixzFf13X3CgGWSiWrEMB6vZZUKmU28I/6oNpDqaizlrFbLpdDQCAANA0oUjVs5F8PYrzVaoUVAmQBkU6nQwI4kMhkMqFxoKSEaLfbslqtdjkk2bVazVjEqi5iw3w+l5Ozc7NpWn+XXC4XWxgsW2Cj0ZBer2eBgIGiDNjt1aWBXFzfyGliHQuNALvdbuhkChWEfbifZv7u/sFAs9msXY8TchhSqEAmadI4mBIUSk4pHG7q9fq+KFgGSLXG4/GPsDgonRCx7HmezGazo2BAvf5AHp+ejX2UWsv5fN7vdDqCyqDNl9e32GoeDip0MBjschgEetvUUZDDRaiMAA+rTLK10YP3Vm+LNjUtRtgcqkL6MHi9aHA2B++yuavfD4OC9fpFcqhA3aQPAS2hB6lStazPG2t4begQm0Pes2NCLeIgGFi2wGq1anLwl6DCy+Vyp7BQKIRYTP4mAGl8AUQGwWSM9G4HAAAAAElFTkSuQmCC"); + } + .sprintButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAd1JREFUOE+tlEmKAkEQRSMdwIV6MbeKeEKVciXoORwWLkTUhThtxAFnq/lh/zSzyoaW7gCxDCtf/JjStNvtUP7BDoeDUgyB2+1WEomERd/vdzHGSDKZtL7NZvM29Ol0kvV6LePx+Anc7/fS6/WEkDAMFQQgfI/Hw4PDj3dUkTH6PZlMXgoB7HQ6ViEAt9tNUqmUHsBvqHfVRqVCnU0Z6Xa7XQ8ICAAsAxRRDQ7iNwPBP51OfYUA4gXY8Xi0AvL5vD4jKC2bzeojoCgJbDabyeVyedYQxR4MBpoiUq3VavZwoVAQAOr1uudjIDqRsgWORiNZrVYeMJPJ6LvoYKVSkWq1Kq4PgVxoDLhcLq0CNKnVaikAivEpFovSaDRi0Fwup6mjhp5CAlls1JFQKi2VShIEgReIJRkOh6+mIGUA0a10Oi3X61V2u11MZblc1lq+Sz2W8nw+V+kYFTdlqvsJxo7blIMgCBeLhQDI7Yg24KOmuEAO8bsRQRBatMPw25QJjHaZh9lFlIGGceFQY3bfAjGH7nphVNAkd5d1V78vBq4h1y9WQwJ5iBcBVouBeMNQKa83vIPbBpNhmDLus98YU0QGrqGGFtjv97WofzFsyfl8fipsNpseC39+YgDRvgDSDetkcdqdJgAAAABJRU5ErkJgggAA"); + } + .perspectiveButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAgxJREFUOE+VlM9L40AUx9+cF8Gbe1D/ld5F6FXv1h8gHirtaltXXGvU1o2g7ura1IJat61haaz4W7AieE6y2P4ZwrLnkfemM0lNl7JDIJnJy+d93zdvhj1d1/jH/kGQgwMHvBhjtIRzBgw45yIEl/Gxdcc46+oOGILwfV//gBdAcawNIrgCLhMg8Oz6Hoz9PLCnmxqJaDiuCvqfB90oAnDuKfKDIrEEsXJZrSuTQFi+LK3puKr0sVgC/v55hQ89vQpkZDW/NcoqPXdMfipQw3GUg5FYEkKhkILU63XIZVeCTgODTaMoEjxeWeRR0/U8Go+nCHRxbsHQcBgQ9H7sZ9K0tJk/oR/DHi6q9Neatq1iJ+cXQdd3wbJKChYOj8BEZJRisOS91SUBKpSF2fc1k0AN21V1T6eWCCRHNDoVULSTXqT4rcOSAN1Wy9h+QB61Gm3mczrg0daXVFsjyqzbRxVR2qX5s6XIUd0aXV4NKNIX5gNdjbDvxyats/PyEceufbFdlTG2kiEQliTv2WS8o6Ld0i/A75l1UuDYB+iRLO3T2lfliQStz812BP2oWAJULR7Qfmw4vwOGJjK6UqTFox27PH9qiT3ZDSS//hfIOK0KsxGEwS92UFHXjQYAPkUF/uw0wayYQiIW3Dp62ue0AdR7MfMdN8aGxrVvhkqOMsVx5gX5lclziiDMO/DeAFwyISmrQZieAAAAAElFTkSuQmCC"); + } + .screenshotButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAcdJREFUOE+llE1LAkEYx585R3Ssg/WVvEbn3g7RoVBRRJFMS0vBsFLXhHzJlyXcjNJKiBA67264+zUiOk88M+64WxAsLgs7M/vMb/7Pf2YeMhr06JJnBayHAgV8CSFsCPsECFBKeQgOY3PyxTil/wIEQfh/0bM8DWBxxAHhXA63FkDg3WAIUrEMZPTUYyIMTRdBbhoZqQ5A6VTRTCBM30rN1HSR+rovJERJ6YTdkt8WQaZUY34KkKFpzIMNXwi+vz4FaG5+AUrphNNhGy4r1flCb32FeWTqOmz6w27sgWIqDtlyg20MeX3osl0zVRW2g1GHGotaKDXEAlsba6yNSi+TMchWWtzsYU9mIEPVYSccYyBrIk7CtqI0BcjrXQUcR1A+HoXcdZODnrstPH6AHu1G4n8U/QfKxcJwVm3z1B7lm4kiDfYOkq5AmUgQzmsyO6fkvlWleGrHqg6+w5Qrj9IhP1w0bwHnE6VRoXgO0KPA0amrXTsO7EOhrXBQt37F7qOhfbiCWMHljsLv5KwgqdPlZiMI6WN1ZkUV+q6ZILdlLhETnpQeZ59dAPGf92zlRjpJ0EReEv6gTF7OpkF286w6xSBkWvB+AJ1TDClYPe7/AAAAAElFTkSuQmCC"); + } + .coordinatesButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAeZJREFUOE+VlF1LAkEUhs9cR3RZF9avqyiILgwtxRBJttJawT7NNaHS0pZwM/oOIoKud1fSvxHR9cQ544y7rmANgvNx5pn3vHtm2PtDk0+EpkA2DhzwxxijKRwzYMA5FyE4jd3uP8ZZ98/AEITr46HJXgDFMR9EcAVcHoDA64cXMI5KwN4fmySi7bgq6D8d3agAcN5TNAw0E4krfjGrqT6BMH2ZWsdxvan7rJiOxOHn+4s2j4yOAYKkVXrxjPxUoLbjBJ0EBqjEC8E+wrAhMGdUhIC3e4s86riDPZqNJnwgBEjYUSYNuVKVPgx7vW3QV+vY9kCP52JJBZIBhWIVwuF5OFxPQa5cE2a/NE0CtW23v0RgIZFSENwsG0J200mKz59cCNBTo4blB+SRp9AWk2kfBDfLlk8lVEHunNZFanfmeVeRoxbDa+sBiL4aC1Q1Sto/M2me3dROOVbtp+2SooiWCUCy8ehACMYfXFwB7mdWtcyxDtCj5Y3tAGRzZcmXcr+RhbolQI3KMd3HttOCeEYHXT8gK9ATLRoeeltKl5a4k/0gufMvEIw1LhvCbAThxKfdGnr6oACPojL/cDpg1k0hERPuPj3+MV0AtS5GnufG2NK4tmeow1CmeM56QV4l8p0iCOs9eL/YmiwplUjFQgAAAABJRU5ErkJgggAA"); + } + `; + document.documentElement.appendChild(customStyle); +} diff --git a/src/resources/scripts/eagler-launch/1.8.8/main.ts b/src/resources/scripts/eagler-launch/1.8.8/main.ts new file mode 100644 index 0000000..13e6d29 --- /dev/null +++ b/src/resources/scripts/eagler-launch/1.8.8/main.ts @@ -0,0 +1,26 @@ +// @ts-nocheck +window.addEventListener('load', () => { + const relayId = Math.floor(Math.random() * 3); + window.eaglercraftXOpts = { + container: 'game_frame', + assetsURI: `${window.location.pathname}/assets.epk`, + localesURI: `${window.location.pathname}/lang/`, + servers: [ + { addr: 'wss://mc.ricenetwork.xyz', name: 'Rice Network' }, + { addr: 'wss://mc.lamplifesteal.xyz', name: 'LampLifesteal' }, + { addr: 'wss://electronmc.club', name: 'Electron Network' }, + { addr: 'wss://play.brickmc.net', name: 'BrickMC' }, + ], + relays: [ + { addr: 'wss://relay.deev.is/', comment: 'lax1dude relay #1', primary: relayId === 0 }, + { addr: 'wss://relay.lax1dude.net/', comment: 'lax1dude relay #2', primary: relayId === 1 }, + { addr: 'wss://relay.shhnowisnottheti.me/', comment: 'ayunami relay #1', primary: relayId === 2 }, + ], + }; + + const urlParams = new URLSearchParams(window.location.search); + window.eaglercraftXOpts.joinServer = urlParams.get('server') ?? undefined; + + history.replaceState({}, '', '/play'); + main(); +}); diff --git a/src/resources/scripts/eagler-launch/1.9.4/main.ts b/src/resources/scripts/eagler-launch/1.9.4/main.ts new file mode 100644 index 0000000..3ef1ed2 --- /dev/null +++ b/src/resources/scripts/eagler-launch/1.9.4/main.ts @@ -0,0 +1,21 @@ +// @ts-nocheck +window.addEventListener('load', () => { + const relayId = Math.floor(Math.random() * 3); + window.eaglercraftXOpts = { + container: 'game_frame', + assetsURI: `${window.location.pathname}/assets.epk`, + localesURI: `${window.location.pathname}/lang/`, + servers: [{ addr: 'wss://eagler.xyz', name: 'TemuzX' }], + relays: [ + { addr: 'wss://relay.deev.is/', comment: 'lax1dude relay #1', primary: relayId === 0 }, + { addr: 'wss://relay.lax1dude.net/', comment: 'lax1dude relay #2', primary: relayId === 1 }, + { addr: 'wss://relay.shhnowisnottheti.me/', comment: 'ayunami relay #1', primary: relayId === 2 }, + ], + }; + + const urlParams = new URLSearchParams(window.location.search); + window.eaglercraftXOpts.joinServer = urlParams.get('server') ?? undefined; + + history.replaceState({}, '', '/play'); + main(); +}); diff --git a/src/resources/scripts/eagler-launch/b1.3/main.ts b/src/resources/scripts/eagler-launch/b1.3/main.ts new file mode 100644 index 0000000..7ab2b53 --- /dev/null +++ b/src/resources/scripts/eagler-launch/b1.3/main.ts @@ -0,0 +1,10 @@ +// @ts-nocheck +window.addEventListener('load', function () { + window.minecraftOpts = ['game_frame', `${window.location.pathname}/assets.epk`]; + + const urlParams = new URLSearchParams(window.location.search); + window.minecraftOpts.push(urlParams.get('server') ?? undefined); + + history.replaceState({}, '', '/play'); + main(); +}); diff --git a/src/resources/scripts/google-tag.ts b/src/resources/scripts/google-tag.ts new file mode 100644 index 0000000..1beca01 --- /dev/null +++ b/src/resources/scripts/google-tag.ts @@ -0,0 +1,12 @@ +// @ts-nocheck +const googleTag = document.createElement('script'); +googleTag.async = true; +googleTag.src = 'https://www.googletagmanager.com/gtag/js?id=G-GD4SJRCR7Z'; +document.head.appendChild(googleTag); + +window.dataLayer = window.dataLayer || []; +function gtag(...args: unknown[]) { + dataLayer.push(...args); +} +gtag('js', new Date()); +gtag('config', 'G-GD4SJRCR7Z'); diff --git a/src/resources/scripts/main.ts b/src/resources/scripts/main.ts new file mode 100644 index 0000000..9218342 --- /dev/null +++ b/src/resources/scripts/main.ts @@ -0,0 +1,631 @@ +import { gt, coerce } from 'semver'; +import { inflate, deflate } from 'pako'; + +let selectedVersion: string | undefined = undefined; +let articleAnimationLock = false; + +const theme = { + load: function (themeToLoad?: string) { + const themeElement = document.querySelector('#theme') as HTMLLinkElement | null; + if (themeElement) themeElement.href = themeToLoad ? `/resources/styles/themes/${themeToLoad}.css` : `/resources/styles/themes/${storage.local.get('theme') ?? 'default'}.css`; + }, + set: function (newTheme: string) { + storage.local.set('theme', newTheme); + theme.load(); + }, +}; + +const versionSelector = { + open: function () { + const customOptions = document.querySelector('.custom-options'); + const customSelect = document.querySelector('.custom-select'); + if (customOptions && customSelect) { + customOptions.classList.add('open'); + customSelect.classList.add('open'); + } + }, + close: function () { + const customOptions = document.querySelector('.custom-options'); + const customSelect = document.querySelector('.custom-select'); + if (customOptions && customSelect) { + customOptions.classList.remove('open'); + customSelect.classList.remove('open'); + } + }, + toggle: function () { + const customOptions = document.querySelector('.custom-options'); + const customSelect = document.querySelector('.custom-select'); + if (customOptions && customSelect) { + customOptions.classList.toggle('open'); + customSelect.classList.toggle('open'); + } + }, +}; + +const game = { + play: function (version?: string) { + if (version) { + document.body.style.display = 'none'; + storage.session.set('lastGame', version); + // @ts-expect-error + window.top.location.href = version; + } else if (selectedVersion) { + document.body.style.display = 'none'; + storage.session.set('lastGame', selectedVersion); + // @ts-expect-error + window.top.location.href = selectedVersion; + } else { + alert('Please select a version to play.'); + return; + } + }, + select: function (path: string, name?: string) { + selectedVersion = path; + const selector = document.querySelector('.custom-select'); + if (selector?.textContent) { + if (name) { + selector.textContent = `Selected: ${name}`; + } else { + selector.textContent = `Selected: ${path}`; + } + } + versionSelector.close(); + }, + archive: function (client: string) { + const clients: Record = { + '1.8': '18-client-version', + '1.5': '15-client-version', + 'b1.3': 'b13-client-version', + }; + const dropdown = clients[client] ? (document.querySelector(`#${clients[client]}`) as HTMLSelectElement | null) : null; + if (dropdown?.value) { + selectedVersion = `https://archive.eaglercraft.rip/Eaglercraft${client === '1.8' ? 'X_1.8' : `_${client}`}/client/${dropdown.value}/index.html`; + game.play(); + } + }, +}; + +const navigate = { + home: { + game: function () { + document.body.style.display = 'none'; + const navUrl = '/home/game/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + clients: function () { + document.body.style.display = 'none'; + const navUrl = '/home/clients/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + archive: function () { + document.body.style.display = 'none'; + const navUrl = '/home/archive/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + downloads: function () { + document.body.style.display = 'none'; + const navUrl = '/home/downloads/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + }, + mods: { + mods: function () { + document.body.style.display = 'none'; + const navUrl = '/mods/mods/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + resourcepacks: function () { + document.body.style.display = 'none'; + const navUrl = '/mods/resourcepacks/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + }, + articles: function () { + const navUrl = '/articles/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + mobile: function () { + const navUrl = '/mobile/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + updates: function () { + const navUrl = '/updates/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + servers: function () { + const navUrl = '/servers/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, + settings: function () { + const navUrl = '/settings/'; + storage.session.set('lastPage', navUrl); + window.location.href = navUrl; + }, +}; + +const article = { + open: function (articleId: string) { + const modal = document.querySelector(`#article-${articleId}`) as HTMLElement | null; + const modalContent = document.querySelector(`#article-${articleId} .article-content`) as HTMLElement | null; + if (!articleAnimationLock && modal && modalContent) { + articleAnimationLock = true; + modal.style.animation = '0.5s ease-in-out 1 normal article'; + modal.style.display = 'flex'; + modalContent.scroll({ top: 0, left: 0, behavior: 'instant' }); + modal.addEventListener( + 'animationend', + () => { + const closeArticleHandler = (event: Event) => { + if (event.target === modal) { + modal.removeEventListener('click', closeArticleHandler); + article.close(articleId); + } + }; + modal.addEventListener('click', closeArticleHandler); + articleAnimationLock = false; + }, + { once: true } + ); + } + }, + close: function (articleId: string) { + const modal = document.querySelector(`#article-${articleId}`) as HTMLElement | null; + if (!articleAnimationLock && modal) { + articleAnimationLock = true; + modal.style.animation = '0.5s ease-in-out 1 reverse article-tempfix'; + modal.addEventListener( + 'animationend', + () => { + modal.style.display = 'none'; + articleAnimationLock = false; + }, + { once: true } + ); + } + }, +}; + +/*const cookie = { + set: function (key: string, value: string | number | object | [] | boolean | null | undefined, days: number) { + let maxAge; + if (days) { + maxAge = days * 60 * 60 * 24; + } else { + maxAge = 31536000; + } + document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Max-Age=${maxAge}; Path=/; SameSite=Lax; Secure`; + }, + get: function (key: string) { + for (const cookie of document.cookie.split('; ')) { + const cookiePair = cookie.split('='); + if (encodeURIComponent(key) === cookiePair[0]) { + return decodeURIComponent(cookiePair[1]); + } + } + return undefined; + }, + delete: function (key: string) { + document.cookie = `${encodeURIComponent(key)}=; Max-Age=0; Path=/`; + }, +};*/ + +const storage = { + local: { + get: function (key: string) { + const item = localStorage.getItem('minexlauncher'); + if (item !== null) { + const json = JSON.parse(item); + if (json[key] !== undefined) { + return json[key]; + } + return undefined; + } + return undefined; + }, + set: function (key: string, value: string | number | object | boolean | null | undefined) { + let item = localStorage.getItem('minexlauncher'); + if (item === null) { + item = '{}'; + } + const json = JSON.parse(item); + json[key] = value; + localStorage.setItem('minexlauncher', JSON.stringify(json)); + }, + }, + session: { + get: function (key: string) { + const item = sessionStorage.getItem('minexlauncher'); + if (item !== null) { + const json = JSON.parse(item); + if (json[key] !== undefined) { + return json[key]; + } + return undefined; + } + return undefined; + }, + set: function (key: string, value: string | number | object | boolean | null | undefined) { + let item = sessionStorage.getItem('minexlauncher'); + if (item === null) { + item = '{}'; + } + const json = JSON.parse(item); + json[key] = value; + sessionStorage.setItem('minexlauncher', JSON.stringify(json)); + }, + }, +}; + +const query = { + get: function (name: string) { + return new URLSearchParams(window.top?.location.search).get(name); + }, +}; + +const detect = { + mobile: function (): boolean { + try { + document.exitPointerLock(); + return /Mobi/i.test(window.navigator.userAgent); + } catch { + return true; + } + }, + landscape: function () { + return window.innerWidth > window.innerHeight; + }, +}; + +const mods = { + check: function (mod: string): boolean { + const mods: string[] = storage.local.get('mods') ?? []; + return mods.includes(mod); + }, + add: function (mod: string): void { + const mods: string[] = storage.local.get('mods') ?? []; + if (!mods.includes(mod)) { + mods.push(mod); + mods.sort(); + storage.local.set('mods', mods); + } + }, + remove: function (mod: string): void { + const mods: string[] = storage.local.get('mods') ?? []; + const modIndex = mods.indexOf(mod); + if (modIndex !== -1) { + mods.splice(modIndex, 1); + storage.local.set('mods', mods); + } + }, + toggle: function (modId: string): void { + const mod = `/resources/mods/downloads/${modId}.js`; + const mods: string[] = storage.local.get('mods') ?? []; + const modIndex = mods.indexOf(mod); + if (modIndex === -1) { + mods.push(mod); + mods.sort(); + storage.local.set('mods', mods); + const modInstallElem = document.querySelector(`#mod-install-${modId}`); + if (modInstallElem) { + modInstallElem.textContent = 'Uninstall'; + modInstallElem.classList.add('installed'); + } + } else { + mods.splice(modIndex, 1); + storage.local.set('mods', mods); + const modInstallElem = document.querySelector(`#mod-install-${modId}`); + if (modInstallElem) { + modInstallElem.textContent = 'Install'; + modInstallElem.classList.remove('installed'); + } + } + }, +}; + +const sw = { + register: function (url: string) { + if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker.register(url).then(() => { + navigator.serviceWorker.addEventListener('message', (event) => { + if (event.origin === window.location.origin) { + if (event.data.title === 'sw-install-complete') { + alert('MineXLauncher is now ready for offline use!'); + } + } + }); + }); + }); + } + }, +}; + +const base64Gzip = { + decode: function (base64: string) { + // Decode Base64 to binary string + const binaryString = atob(base64); + + // Convert binary string to Uint8Array + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + // Use pako to decompress the Uint8Array + const decompressed = inflate(bytes, { to: 'string' }); + + return decompressed; + }, + encode: function (inputString: string) { + // Convert the input string to a Uint8Array + const encoder = new TextEncoder(); + const inputBytes = encoder.encode(inputString); + + // Use pako to compress the Uint8Array + const compressedBytes = deflate(inputBytes); + + // Convert the compressed Uint8Array to a binary string + let binaryString = ''; + for (const byte of compressedBytes) { + binaryString += String.fromCharCode(byte); + } + + // Encode the binary string to Base64 + const base64String = btoa(binaryString); + + return base64String; + }, +}; + +if (detect.mobile()) { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = '/resources/styles/mobile.css'; + document.head.appendChild(link); +} + +if (window.location.pathname === '/') { + const lastPage = storage.session.get('lastPage'); + const isMobile = detect.mobile(); + const iframe = document.createElement('iframe'); + iframe.src = !storage.local.get('lastVersion') ? '/welcome/' : lastPage ? lastPage : isMobile ? '/mobile/' : '/home/game/'; + + document.addEventListener('DOMContentLoaded', () => document.body.appendChild(iframe)); + + /* document.addEventListener('load', () => { + if (storage.local.get('offlineCache')) { + sw.register('/sw-full.js'); + } else { + sw.register('/sw.js'); + } + }); */ + document.addEventListener('load', () => sw.register('/sw.js')); + + window.addEventListener('beforeinstallprompt', (event) => { + // @ts-expect-error + if (iframe.contentWindow) iframe.contentWindow.installPwaEvent = event; + }); +} else { + theme.load(); + + document.addEventListener('DOMContentLoaded', async () => { + const profileName = document.querySelector('.profile-name'); + const titleBarText = document.querySelector('.title-bar span'); + + const lastVersion = storage.local.get('lastVersion'); + const updateData = (await (await fetch('/resources/data/main.json')).json()).updates; + const currentVersion = updateData[0].version; + const changelog = updateData[0].changelog.map((change: string) => ` - ${change}`).join('\n'); + + if (profileName) profileName.textContent = storage.local.get('username'); + if (titleBarText) titleBarText.textContent += ` ${currentVersion}`; + + // @ts-expect-error + if (lastVersion && gt(coerce(currentVersion, { includePrerelease: true }), coerce(lastVersion, { includePrerelease: true }))) { + alert(`MineXLauncher has been updated to v${currentVersion}!\n\nChanges in v${currentVersion}:\n${changelog}`); + storage.local.set('lastVersion', currentVersion); + } + }); + + /* if (storage.local.get('showAds')) { + const googleAdsScript = document.createElement('script'); + googleAdsScript.async = true; + googleAdsScript.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1132419379737567'; + googleAdsScript.crossOrigin = 'anonymous'; + document.head.appendChild(googleAdsScript); + + document.addEventListener('DOMContentLoaded', () => { + const googleAdsPush = document.createElement('script'); + googleAdsPush.text = '(adsbygoogle = window.adsbygoogle || []).push({});'; + document.body.appendChild(googleAdsPush); + + const adsContainers = document.querySelectorAll('.ads-container'); + adsContainers.forEach((adsContainer) => { + adsContainer.style.display = 'flex'; + }); + }); + } */ +} + +if (window.location.pathname === '/settings/') { + document.addEventListener('DOMContentLoaded', async () => { + const profileName = document.querySelector('.profile-name'); + const usernameInput = document.querySelector('#username-input') as HTMLInputElement | null; + const themeSelect = document.querySelector('#theme-select') as HTMLSelectElement | null; + const offlineCheckbox = document.querySelector('#offline-checkbox') as HTMLInputElement | null; + const adsCheckbox = document.querySelector('#ads-checkbox') as HTMLInputElement | null; + const themeData: { id: string; name: string }[] = (await (await fetch('/resources/data/main.json')).json()).themes; + + if (usernameInput) { + usernameInput.placeholder = storage.local.get('username') ?? ''; + usernameInput.addEventListener('input', () => { + let username = usernameInput.value.replace(/[^A-Za-z0-9]/g, '_').substring(0, 16); + usernameInput.value = username; + while (username.length < 3) username += '_'; + + storage.local.set('username', username); + if (profileName) profileName.textContent = username; + }); + } + + if (themeSelect) { + themeData.forEach((theme: { id: string; name: string }) => { + const option = document.createElement('option'); + option.value = theme.id; + option.textContent = theme.name; + themeSelect?.appendChild(option); + }); + themeSelect.value = storage.local.get('theme') ?? ''; + themeSelect.addEventListener('change', () => theme.set(themeSelect.value ?? 'default')); + } + + if (offlineCheckbox) { + offlineCheckbox.checked = storage.local.get('offlineCache') ?? false; + offlineCheckbox.addEventListener('change', () => { + storage.local.set('offlineCache', offlineCheckbox.checked); + if (offlineCheckbox.checked) { + sw.register('/sw-full.js'); + alert( + 'Offline cache is now downloading.\nThe download size is about 1GB, so it may take a while.\n\nPlease do not leave this page while the download is in progress.\nYou will be notified when the download is complete.' + ); + } else { + sw.register('/sw.js'); + alert('Offline cache has been deleted.'); + } + }); + } + + if (adsCheckbox) { + adsCheckbox.checked = storage.local.get('showAds'); + adsCheckbox.addEventListener('change', () => { + storage.local.set('showAds', adsCheckbox.checked); + const adsContainers = document.querySelectorAll('.ads-container'); + adsContainers.forEach((adsContainer: Element) => ((adsContainer as HTMLElement).style.display = 'none')); + }); + } + }); +} else if (window.location.pathname === '/welcome/') { + document.addEventListener('DOMContentLoaded', async () => { + const setupForm = document.querySelector('#setup-form') as HTMLFormElement | null; + const usernameInput = document.querySelector('#username-input') as HTMLInputElement | null; + const themeSelect = document.querySelector('#theme-select') as HTMLSelectElement | null; + const offlineCheckbox = document.querySelector('#offline-checkbox') as HTMLInputElement | null; + const themeData: { id: string; name: string }[] = (await (await fetch('/resources/data/main.json')).json()).themes; + + if (setupForm) { + if (usernameInput) { + usernameInput.addEventListener('input', () => { + const username = usernameInput.value.replace(/[^A-Za-z0-9]/g, '_').substring(0, 16); + usernameInput.value = username; + }); + } + + if (themeSelect) { + themeData.forEach((theme: { id: string; name: string }) => { + const option = document.createElement('option'); + option.value = theme.id; + option.textContent = theme.name; + themeSelect?.appendChild(option); + }); + themeSelect.addEventListener('change', () => theme.load(themeSelect.value ?? 'default')); + } + + setupForm.addEventListener('submit', async (event) => { + event.preventDefault(); + + let username = usernameInput?.value.replace(/[^A-Za-z0-9]/g, '_').substring(0, 16); + if (usernameInput) usernameInput.value = username ?? ''; + + if (!username) { + alert('Please type a username.'); + return; + } else { + while (username.length < 3) username += '_'; + + storage.local.set('username', username); + storage.local.set('theme', themeSelect?.value ?? 'default'); + // storage.local.set('offlineCache', offlineCheckbox?.checked ?? false); + // storage.local.set('showAds', true); + storage.local.set('mods', []); + storage.local.set('lastVersion', (await (await fetch('/resources/data/main.json')).json()).updates[0].version); + + if (offlineCheckbox?.checked) { + sw.register('/sw-full.js'); + alert( + 'Offline cache is now downloading.\nThe download size is about 1GB, so it may take a while.\n\nPlease do not leave this page while the download is in progress.\nYou will be notified when the download is complete.' + ); + } else sw.register('/sw.js'); + + // @ts-expect-error + window.top.location.href = '/'; + } + }); + } + }); +} else if (window.location.pathname === '/mods/mods/' || window.location.pathname === '/mods/resourcepacks/') { + document.addEventListener('DOMContentLoaded', async () => { + const addonType: 'mods' | 'resourcepacks' = window.location.pathname === '/mods/mods/' ? 'mods' : 'resourcepacks'; + const addonData: { id: string; name: string; description: string; author: string; authorLink: string; source: string }[] = (await (await fetch('/resources/data/main.json')).json()).addons; + const modList = document.querySelector('.mod-list'); + // @ts-expect-error + addonData[addonType].forEach((addon) => { + const modItem = document.createElement('div'); + modItem.classList.add('mod-item'); + modItem.innerHTML = `
${ + addon.name + }

By ${addon.author}

${addon.description}

`; + modList?.appendChild(modItem); + }); + + if (addonType === 'mods') { + const installedMods = storage.local.get('mods') ?? []; + const modElements = document.querySelectorAll('.mod-install'); + modElements.forEach((modElement) => { + const modId = /^mod-install-(.*)$/.exec(modElement.id)?.[1]; + if (installedMods.includes(`/resources/mods/downloads/${modId}.js`)) { + modElement.textContent = 'Uninstall'; + modElement.classList.add('installed'); + } + }); + } + }); +} else if (window.location.pathname === '/updates/') { + document.addEventListener('DOMContentLoaded', async () => { + const updatesContainer = document.querySelector('.updates-container'); + const updateData: { version: string; changelog: string[] }[] = (await (await fetch('/resources/data/main.json')).json()).updates; + updateData.forEach((update) => { + const versionHeader = document.createElement('strong'); + versionHeader.textContent = `MineXLauncher ${update.version}`; + updatesContainer?.appendChild(versionHeader); + + const changelog = document.createElement('ul'); + update.changelog.forEach((change) => { + const changelogItem = document.createElement('li'); + changelogItem.textContent = change; + changelog.appendChild(changelogItem); + }); + + updatesContainer?.appendChild(changelog); + }); + }); +} + +if (window.location.hostname === null) { + // Stop the minifier from removing these functions + console.debug([navigate, query, versionSelector, game, mods, base64Gzip, article]); +} diff --git a/tsconfig.json b/tsconfig.json index 08a6d4a..2895e1d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,4 @@ { "extends": ["@tsconfig/bun", "@tsconfig/strictest"], - "exclude": ["node_modules", "public"], - "compilerOptions": { - //"outDir": "dist", - //"noEmit": false, - //"allowImportingTsExtensions": false - } + "exclude": ["node_modules", "public"] }