From 1b539f840298a78ce826c830ab5d84030e7ab2ef Mon Sep 17 00:00:00 2001 From: fenopy <fenopie@gmail.com> Date: Mon, 9 Sep 2024 08:36:30 -0500 Subject: [PATCH] Merge pull request #16 from MikeRatcliffe/add-prettier --- .github/workflows/main-osx.yml | 2 +- .husky/pre-commit | 0 .prettierignore | 12 + .prettierrc | 14 + .vscode/launch.json | 29 +- bitbucket-pipelines.yml | 22 +- main.js | 394 +- package-lock.json | 1626 ++++++++- package.json | 6 +- readme.md | 9 +- removeLocales.js | 12 +- tools.js | 5522 +++++++++++++++-------------- versioncheck.js | 35 +- views/browse_include.twig | 53 +- views/css/bootstrap-slate.min.css | 2 +- views/css/component.css | 112 +- views/css/main.css | 48 +- views/device_info.twig | 51 +- views/error.twig | 40 +- views/index.twig | 231 +- views/js/browse.js | 110 +- views/js/installed.js | 57 +- views/js/main.js | 19 +- views/js/search.js | 25 +- views/layout.twig | 675 ++-- views/modals/app_start.twig | 38 +- views/modals/app_tools.twig | 129 +- views/modals/appinfo.twig | 108 +- views/modals/appinfo_events.twig | 118 +- views/modals/confirm.twig | 70 +- views/modals/donate.twig | 48 +- views/modals/installed.twig | 102 +- views/modals/processing.twig | 24 +- views/modals/prompt.twig | 38 +- views/modals/scrcpy.twig | 146 +- views/modals/sideload-queue.twig | 177 +- views/modals/sideload.twig | 155 +- views/modals/tweaks.twig | 397 ++- views/settings_include.twig | 319 +- 39 files changed, 6505 insertions(+), 4470 deletions(-) create mode 100644 .husky/pre-commit create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.github/workflows/main-osx.yml b/.github/workflows/main-osx.yml index e230704..027f26e 100644 --- a/.github/workflows/main-osx.yml +++ b/.github/workflows/main-osx.yml @@ -52,4 +52,4 @@ jobs: uses: actions/upload-artifact@v2 with: name: sidenoder-darwin-x64-zip - path: /tmp/builds/darwin/zip/ \ No newline at end of file + path: /tmp/builds/darwin/zip/ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..e69de29 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..48fcc3a --- /dev/null +++ b/.prettierignore @@ -0,0 +1,12 @@ +.github/ +node_modules/ +syncedversion.txt +synced.txt +com.sample.android.apk +/dist/* +/out/* +**/*.min.css +**/*.min.css.map +**/*.min.js +**/*.min.js.map +**/*.custom.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c72176a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,14 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "always", + "requirePragma": false, + "insertPragma": false, + "proseWrap": "preserve", + "plugins": ["@destination/prettier-plugin-twig"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 4abca7b..ba6e70b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,17 +2,28 @@ "version": "0.2.0", "configurations": [ { - "name": "Launch", + "name": "Electron: Main", "type": "node", "request": "launch", - "program": "${workspaceFolder}/main.js", - "stopOnEntry": false, - "args": [], - "cwd": "${workspaceRoot}", - "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", - "runtimeArgs": [".", "--enable-logging"], - "env": {}, - "sourceMaps": false + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "start"], + "env": { + "MAIN_ARGS": "--inspect=5858 --remote-debugging-port=9223" + } + }, + { + "name": "Electron: Renderer", + "type": "chrome", + "request": "attach", + "port": 9223, + "webRoot": "${workspaceFolder}", + "timeout": 60000 + } + ], + "compounds": [ + { + "name": "Electron: All", + "configurations": ["Electron: Main", "Electron: Renderer"] } ] } diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 0f3a763..8fa4836 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -7,14 +7,14 @@ image: node:10.15.3 pipelines: default: - - step: - name: Build and Test - caches: - - node - script: - - npm install - - npm run-script pack - - npm run-script dist - artifacts: - - dist/SideNoder*.AppImage - - dist/SideNoder*.snap \ No newline at end of file + - step: + name: Build and Test + caches: + - node + script: + - npm install + - npm run-script pack + - npm run-script dist + artifacts: + - dist/SideNoder*.AppImage + - dist/SideNoder*.snap diff --git a/main.js b/main.js index bda6604..e75412d 100644 --- a/main.js +++ b/main.js @@ -1,26 +1,29 @@ -const { app, BrowserWindow, Notification, powerSaveBlocker, ipcMain, dialog } = require('electron'); +const { + app, + BrowserWindow, + Notification, + powerSaveBlocker, + ipcMain, + dialog, +} = require("electron"); // const { BrowserWindow } = require('@electron/remote') -const fs = require('fs'); -const path = require('path'); -global.twig = require('electron-twig'); +const fs = require("fs"); +const path = require("path"); +global.twig = require("electron-twig"); app.disableHardwareAcceleration(); -const { - EOL, - platform, - arch, - homedir, - tmpdir, -} = require('os'); +const { EOL, platform, arch, homedir, tmpdir } = require("os"); global.endOfLine = EOL; global.platform = platform(); // process.platform global.arch = arch(); -global.homedir = homedir().replace(/\\/g, '/'); -global.tmpdir = tmpdir().replace(/\\/g, '/'); -global.mountFolder = path.join(global.tmpdir, 'mnt').replace(/\\/g, '/'); -global.sidenoderHome = path.join(global.homedir, 'sidenoder').replace(/\\/g, '/'); +global.homedir = homedir().replace(/\\/g, "/"); +global.tmpdir = tmpdir().replace(/\\/g, "/"); +global.mountFolder = path.join(global.tmpdir, "mnt").replace(/\\/g, "/"); +global.sidenoderHome = path + .join(global.homedir, "sidenoder") + .replace(/\\/g, "/"); global.adbDevice = false; global.mounted = false; @@ -28,99 +31,96 @@ global.updateAvailable = false; global.currentConfiguration = {}; global.rcloneSections = []; global.installedApps = []; -global.hash_alg = 'sha256'; -global.locale = 'en-US'; +global.hash_alg = "sha256"; +global.locale = "en-US"; -global.platform = global.platform.replace('32', '').replace('64', ''); -if (global.platform == 'darwin') global.platform = 'mac'; +global.platform = global.platform.replace("32", "").replace("64", ""); +if (global.platform == "darwin") global.platform = "mac"; -app.on('ready', () => { +app.on("ready", () => { global.locale = app.getLocale(); }); -eval(fs.readFileSync(path.join(__dirname, 'versioncheck.js'), 'utf8')); - -const tools = require('./tools'); +eval(fs.readFileSync(path.join(__dirname, "versioncheck.js"), "utf8")); +const tools = require("./tools"); // const id = powerSaveBlocker.start('prevent-display-sleep'); // console.log(powerSaveBlocker.isStarted(id)); - - -ipcMain.on('get_installed', async (event, arg) => { - console.log('get_installed received'); +ipcMain.on("get_installed", async (event, arg) => { + console.log("get_installed received"); const apps = await tools.getInstalledApps(); - console.log('get_installed', apps.length); + console.log("get_installed", apps.length); - event.reply('get_installed', { success: true, apps }); + event.reply("get_installed", { success: true, apps }); return; }); -ipcMain.on('get_installed_with_updates', async (event, arg) => { - console.log('get_installed_with_updates received'); +ipcMain.on("get_installed_with_updates", async (event, arg) => { + console.log("get_installed_with_updates received"); const apps = await tools.getInstalledAppsWithUpdates(); - console.log('get_installed_with_updates', apps.length); + console.log("get_installed_with_updates", apps.length); - event.reply('get_installed_with_updates', { success: true, apps }); + event.reply("get_installed_with_updates", { success: true, apps }); }); -ipcMain.on('get_device_info', async (event, arg) => { +ipcMain.on("get_device_info", async (event, arg) => { getDeviceInfo(event); }); async function getDeviceInfo(event) { - console.log('get_device_info received'); + console.log("get_device_info received"); const res = await tools.getDeviceInfo(); - event.reply('get_device_info', res); + event.reply("get_device_info", res); } -ipcMain.on('connect_wireless', async (event, arg) => { - console.log('connect_wireless received'); +ipcMain.on("connect_wireless", async (event, arg) => { + console.log("connect_wireless received"); if (!global.adbDevice && !global.currentConfiguration.lastIp) { - console.log('Missing device, sending ask_device'); - event.reply('connect_wireless', { success: false }); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("connect_wireless", { success: false }); + event.reply("ask_device", ""); return; } const ip = await tools.connectWireless(); - event.reply('connect_wireless', { success: !!ip, ip }); + event.reply("connect_wireless", { success: !!ip, ip }); return; }); -ipcMain.on('disconnect_wireless', async (event, arg) => { - console.log('disconnect_wireless received'); +ipcMain.on("disconnect_wireless", async (event, arg) => { + console.log("disconnect_wireless received"); const res = await tools.disconnectWireless(); - event.reply('connect_wireless', { success: !res }); + event.reply("connect_wireless", { success: !res }); return; }); -ipcMain.on('check_deps', async (event, arg) => { - console.log('check_deps received', arg); +ipcMain.on("check_deps", async (event, arg) => { + console.log("check_deps received", arg); const res = await tools.checkDeps(arg); - event.reply('check_deps', res); + event.reply("check_deps", res); }); -ipcMain.on('mount', async (event, arg) => { +ipcMain.on("mount", async (event, arg) => { await tools.mount(); setTimeout(() => checkMount(event), 1000); return; }); -ipcMain.on('check_mount', async (event, arg) => { +ipcMain.on("check_mount", async (event, arg) => { checkMount(event); }); let rcloneProgress = false; async function checkMount(event) { await tools.checkMount(); - event.reply('check_mount', { success: global.mounted }); + event.reply("check_mount", { success: global.mounted }); if (global.mounted && !rcloneProgress) { tools.updateRcloneProgress(); rcloneProgress = true; @@ -129,68 +129,67 @@ async function checkMount(event) { return; } -ipcMain.on('start_sideload', async (event, arg) => { - console.log('start_sideload received'); +ipcMain.on("start_sideload", async (event, arg) => { + console.log("start_sideload received"); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); - return + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); + return; } - event.reply('start_sideload', { success: true, path: arg.path }); + event.reply("start_sideload", { success: true, path: arg.path }); await tools.sideloadFolder(arg); getDeviceInfo(event); return; }); -ipcMain.on('folder_install', async (event, { path, update }) => { - console.log('folder_install received', path); +ipcMain.on("folder_install", async (event, { path, update }) => { + console.log("folder_install received", path); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } const install = await tools.getApkFromFolder(path); console.log({ install }); - event.reply('ask_sideload', { success: true, install, update }); + event.reply("ask_sideload", { success: true, install, update }); return; }); -ipcMain.on('filedrop', async (event, path) => { - console.log('filedrop received'); +ipcMain.on("filedrop", async (event, path) => { + console.log("filedrop received"); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } // TODO: check isApk - event.reply('ask_sideload', { success: true, install: { path } }); + event.reply("ask_sideload", { success: true, install: { path } }); return; }); - -ipcMain.on('reset_cache', async (event, arg) => { +ipcMain.on("reset_cache", async (event, arg) => { await tools.resetCache(arg); }); -ipcMain.on('get_dir', async (event, arg) => { - console.log('get_dir received', arg); - if ((typeof arg === 'string') && arg.endsWith('.apk')) { +ipcMain.on("get_dir", async (event, arg) => { + console.log("get_dir received", arg); + if (typeof arg === "string" && arg.endsWith(".apk")) { const install = { path: arg, - } - const lastslashindex = install.path.lastIndexOf('/'); + }; + const lastslashindex = install.path.lastIndexOf("/"); const folder = install.path.substring(0, lastslashindex); install.install_desc = await tools.detectInstallTxt(folder); install.notes = await tools.detectNoteTxt(folder); - event.reply('ask_sideload', { success: true, install }); // TODO: install_desc + event.reply("ask_sideload", { success: true, install }); // TODO: install_desc return; } @@ -203,275 +202,269 @@ ipcMain.on('get_dir', async (event, arg) => { dirList = []; incList = []; notSupported = []; - if (!list) incList = [{ name: 'ERROR: Browse failed' }]; - else for (const item of list) { - if (!item.isFile) { - dirList.push(item); - continue; - } - - if ((item.name.endsWith('.apk') || item.name.endsWith('.obb'))) { - incList.push(item); - continue; + if (!list) incList = [{ name: "ERROR: Browse failed" }]; + else + for (const item of list) { + if (!item.isFile) { + dirList.push(item); + continue; + } + + if (item.name.endsWith(".apk") || item.name.endsWith(".obb")) { + incList.push(item); + continue; + } + + notSupported.push(item); } - notSupported.push(item); - } - response = {}; response.success = true; response.list = dirList.concat(incList, notSupported); response.path = folder; // console.log(response.list, response.list.length, incList.length, notSupported.length); - win.webContents.send('get_dir',response); + win.webContents.send("get_dir", response); //event.reply('get_dir', response) }); -ipcMain.on('enable_mtp', async (event, arg) => { - console.log('enable_mtp received'); +ipcMain.on("enable_mtp", async (event, arg) => { + console.log("enable_mtp received"); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } try { const res = await tools.enableMTP(); - event.reply('cmd_sended', { success: res }); - } - catch (err) { - event.reply('cmd_sended', { success: err }); + event.reply("cmd_sended", { success: res }); + } catch (err) { + event.reply("cmd_sended", { success: err }); } return; }); -ipcMain.on('scrcpy_start', async (event, arg) => { - console.log('scrcpy_start received'); +ipcMain.on("scrcpy_start", async (event, arg) => { + console.log("scrcpy_start received"); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } const res = await tools.startSCRCPY(); - console.log('startSCRCPY', res); - event.reply('scrcpy_start', { success: !!res }); + console.log("startSCRCPY", res); + event.reply("scrcpy_start", { success: !!res }); return; }); -ipcMain.on('reboot_device', async (event, arg) => { - console.log('reboot_device received'); +ipcMain.on("reboot_device", async (event, arg) => { + console.log("reboot_device received"); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } try { const res = await tools.rebootDevice(); - event.reply('cmd_sended', { success: res }); - } - catch (err) { - event.reply('cmd_sended', { success: err }); + event.reply("cmd_sended", { success: res }); + } catch (err) { + event.reply("cmd_sended", { success: err }); } return; }); -ipcMain.on('reboot_recovery', async (event, arg) => { - console.log('reboot_recovery received'); +ipcMain.on("reboot_recovery", async (event, arg) => { + console.log("reboot_recovery received"); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } try { const res = await tools.rebootRecovery(); - event.reply('cmd_sended', { success: res }); - } - catch (err) { - event.reply('cmd_sended', { success: err }); + event.reply("cmd_sended", { success: res }); + } catch (err) { + event.reply("cmd_sended", { success: err }); } return; }); -ipcMain.on('reboot_bootloader', async (event, arg) => { - console.log('reboot_bootloader received'); +ipcMain.on("reboot_bootloader", async (event, arg) => { + console.log("reboot_bootloader received"); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } try { const res = await tools.rebootBootloader(); - event.reply('cmd_sended', { success: res }); - } - catch (err) { - event.reply('cmd_sended', { success: err }); + event.reply("cmd_sended", { success: res }); + } catch (err) { + event.reply("cmd_sended", { success: err }); } return; }); -ipcMain.on('sideload_update', async (event, arg) => { - console.log('sideload_update received'); +ipcMain.on("sideload_update", async (event, arg) => { + console.log("sideload_update received"); if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } if (!arg) { - console.log('update.zip not defined'); - event.reply('cmd_sended', { success: 'Update.zip path not defined' }); + console.log("update.zip not defined"); + event.reply("cmd_sended", { success: "Update.zip path not defined" }); return; } try { const res = await tools.sideloadFile(arg); - event.reply('cmd_sended', { success: res }); - } - catch (err) { - event.reply('cmd_sended', { success: err }); + event.reply("cmd_sended", { success: res }); + } catch (err) { + event.reply("cmd_sended", { success: err }); } return; }); -ipcMain.on('device_tweaks', async (event, arg) => { - console.log('device_tweaks received', arg); +ipcMain.on("device_tweaks", async (event, arg) => { + console.log("device_tweaks received", arg); - if (arg.cmd == 'get') { + if (arg.cmd == "get") { const res = await tools.deviceTweaksGet(arg); - event.reply('device_tweaks', res); + event.reply("device_tweaks", res); } - if (arg.cmd == 'set') { + if (arg.cmd == "set") { if (!global.adbDevice) { - console.log('Missing device, sending ask_device'); - event.reply('ask_device', ''); + console.log("Missing device, sending ask_device"); + event.reply("ask_device", ""); return; } const res = await tools.deviceTweaksSet(arg); - event.reply('device_tweaks', arg); + event.reply("device_tweaks", arg); } return; }); -ipcMain.on('uninstall', async (event, arg) => { - console.log('uninstall received'); +ipcMain.on("uninstall", async (event, arg) => { + console.log("uninstall received"); resp = await tools.uninstall(arg); - event.reply('uninstall', { success: true }); + event.reply("uninstall", { success: true }); getDeviceInfo(event); return; }); -ipcMain.on('get_activities', async (event, arg) => { - console.log('get_activities received', arg); +ipcMain.on("get_activities", async (event, arg) => { + console.log("get_activities received", arg); const activities = await tools.getActivities(arg); - event.reply('get_activities', { success: !!activities, activities }); + event.reply("get_activities", { success: !!activities, activities }); return; }); -ipcMain.on('start_activity', async (event, arg) => { - console.log('start_activity received', arg); +ipcMain.on("start_activity", async (event, arg) => { + console.log("start_activity received", arg); const resp = await tools.startActivity(arg); - event.reply('start_activity', { success: !!resp }); + event.reply("start_activity", { success: !!resp }); return; }); -ipcMain.on('start_app', async (event, arg) => { - console.log('start_app received', arg); +ipcMain.on("start_app", async (event, arg) => { + console.log("start_app received", arg); const activity = await tools.getLaunchActivity(arg); const resp = await tools.startActivity(activity); - event.reply('start_app', { success: !!resp }); + event.reply("start_app", { success: !!resp }); return; }); -ipcMain.on('dev_open_url', async (event, arg) => { - console.log('dev_open_url received', arg); +ipcMain.on("dev_open_url", async (event, arg) => { + console.log("dev_open_url received", arg); const resp = await tools.devOpenUrl(arg); - event.reply('dev_open_url', { success: !!resp }); + event.reply("dev_open_url", { success: !!resp }); return; }); - -ipcMain.on('change_config', async (event, { key, val }) => { - console.log('change_config received', {key, val}); +ipcMain.on("change_config", async (event, { key, val }) => { + console.log("change_config received", { key, val }); val = await tools.changeConfig(key, val); - event.reply('change_config', { success: true, key, val }); + event.reply("change_config", { success: true, key, val }); return; }); -ipcMain.on('app_config_set', async (event, { pkg, key, val }) => { - console.log('change_config received', { pkg, key, val }); +ipcMain.on("app_config_set", async (event, { pkg, key, val }) => { + console.log("change_config received", { pkg, key, val }); const res = await tools.changeAppConfig(pkg, key, val); - event.reply('app_config_set', res); + event.reply("app_config_set", res); return; }); -ipcMain.on('app_info', async (event, arg) => { - console.log('app_info received', arg); +ipcMain.on("app_info", async (event, arg) => { + console.log("app_info received", arg); const res = await tools.appInfo(arg); // console.log({ res }); - event.reply('app_info', res); + event.reply("app_info", res); return; }); -ipcMain.on('app_events_info', async (event, arg) => { - console.log('app_events_info received', arg); +ipcMain.on("app_events_info", async (event, arg) => { + console.log("app_events_info received", arg); const res = await tools.appInfoEvents(arg); // console.log({ res }); - event.reply('app_events_info', res); + event.reply("app_events_info", res); return; }); -ipcMain.on('app_tools', async (event, arg) => { - console.log('app_tools received', arg); +ipcMain.on("app_tools", async (event, arg) => { + console.log("app_tools received", arg); const resp = await tools.checkAppTools(arg); - event.reply('app_tools', resp); + event.reply("app_tools", resp); return; }); -ipcMain.on('app_backup', async (event, arg) => { - console.log('app_backup received', arg); +ipcMain.on("app_backup", async (event, arg) => { + console.log("app_backup received", arg); const resp = await tools.backupApp(arg); - event.reply('app_backup', { success: resp }); + event.reply("app_backup", { success: resp }); return; }); -ipcMain.on('data_backup', async (event, arg) => { - console.log('data_backup received', arg); +ipcMain.on("data_backup", async (event, arg) => { + console.log("data_backup received", arg); const resp = await tools.backupAppData(arg); - event.reply('data_backup', { success: resp }); + event.reply("data_backup", { success: resp }); return; }); -ipcMain.on('data_restore', async (event, arg) => { - console.log('data_restore received', arg); +ipcMain.on("data_restore", async (event, arg) => { + console.log("data_restore received", arg); const resp = await tools.restoreAppData(arg); - event.reply('data_restore', { success: resp }); + event.reply("data_restore", { success: resp }); return; }); - global.close = false; -function createWindow () { +function createWindow() { global.win = new BrowserWindow({ width: 1000, minWidth: 920, height: 800, minHeight: 500, - title: 'Quest-Sidenoder', + title: "Quest-Sidenoder", //frame:false, webPreferences: { nodeIntegration: true, enableRemoteModule: true, contextIsolation: false, webView: true, - } + }, }); - require('@electron/remote/main').initialize(); + require("@electron/remote/main").initialize(); require("@electron/remote/main").enable(win.webContents); win.setMenu(null); win.maximize(true); @@ -484,10 +477,10 @@ function createWindow () { sidenoderHome: global.sidenoderHome, version: global.version, currentConfiguration: global.currentConfiguration, - } + }; win.loadURL(`file://${__dirname}/views/index.twig`); - if (process.argv[2] == '--dev') { + if (process.argv[2] == "--dev") { win.webContents.openDevTools(); } @@ -512,7 +505,7 @@ function createWindow () { });*/ } -global.notify = function (title, body, urgency = 'normal') { +global.notify = function (title, body, urgency = "normal") { const not = new Notification({ title, body, @@ -520,14 +513,13 @@ global.notify = function (title, body, urgency = 'normal') { urgency, // 'normal' | 'critical' | 'low' }); not.show(); -} +}; async function startApp() { try { await tools.reloadConfig(); - } - catch(e) { - console.error('reloadConfig', e); + } catch (e) { + console.error("reloadConfig", e); // tools.returnError('Could not (re)load config file.'); } @@ -535,14 +527,14 @@ async function startApp() { await app.whenReady(); createWindow(); - app.on('activate', () => { + app.on("activate", () => { if (BrowserWindow.getAllWindows().length != 0) return; createWindow(); }); - app.on('window-all-closed', (e) => { + app.on("window-all-closed", (e) => { // powerSaveBlocker.stop(id) - console.log('quit'); - if (global.platform !== 'mac') { + console.log("quit"); + if (global.platform !== "mac") { app.quit(); } }); diff --git a/package-lock.json b/package-lock.json index a3e9689..1aac756 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "dependencies": { "@devicefarmer/adbkit": "3.2.6", "@electron/remote": "2.1.2", + "@fortawesome/fontawesome-free": "^6.6.0", "adbkit-apkreader": "3.2.0", "command-exists": "1.2.9", "compare-versions": "6.1.1", @@ -19,13 +20,18 @@ "electron-twig": "1.1.1", "fix-path": "4.0.0", "jquery": "3.7.1", + "jquery-ui": "^1.14.0", "node-fetch": "3.3.2", "socks-proxy-agent": "8.0.4", "web-auto-extractor": "1.0.17" }, "devDependencies": { + "@destination/prettier-plugin-twig": "^1.5.0", "electron": "32.0.2", - "electron-builder": "25.0.5" + "electron-builder": "25.0.5", + "husky": "^9.1.5", + "lint-staged": "^15.2.10", + "prettier": "^3.3.3" } }, "node_modules/@babel/runtime": { @@ -39,6 +45,20 @@ "node": ">=6.9.0" } }, + "node_modules/@destination/prettier-plugin-twig": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@destination/prettier-plugin-twig/-/prettier-plugin-twig-1.5.0.tgz", + "integrity": "sha512-cTgy7lQOs0eTdNIqNQL9YDZ2ZFRyOzF5MfF07Lp8o1LLDMqjJgYfD382LIrXZ1E5s+ViYSgQJacR2U4Zx16XKw==", + "dev": true, + "dependencies": { + "html-styles": "^1.0.0", + "line-column": "^1.0.2", + "ohm-js": "^16.3.0" + }, + "peerDependencies": { + "prettier": "^2.0.0 || ^3.0.0" + } + }, "node_modules/@develar/schema-utils": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", @@ -415,6 +435,14 @@ "node": ">= 10.0.0" } }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", + "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==", + "engines": { + "node": ">=6" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -866,6 +894,21 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1257,6 +1300,18 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1545,15 +1600,18 @@ } }, "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-spinners": { @@ -1646,6 +1704,12 @@ "color-support": "bin.js" } }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1860,11 +1924,11 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2412,6 +2476,18 @@ "node": ">=6" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", @@ -2425,9 +2501,9 @@ "optional": true }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" @@ -2445,6 +2521,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2585,6 +2667,18 @@ "node": ">=10" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fix-path": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fix-path/-/fix-path-4.0.0.tgz", @@ -2733,6 +2827,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", @@ -2943,9 +3049,9 @@ "dev": true }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "optional": true, "dependencies": { "function-bind": "^1.1.2" @@ -2966,6 +3072,12 @@ "node": ">=10" } }, + "node_modules/html-styles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/html-styles/-/html-styles-1.0.0.tgz", + "integrity": "sha512-cDl5dcj73oI4Hy0DSUNh54CAwslNLJRCCoO+RNkVo+sBrjA/0+7E/xzvj3zH/GxbbBLGJhE0hBe1eg+0FINC6w==", + "dev": true + }, "node_modules/htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -3040,6 +3152,21 @@ "ms": "^2.0.0" } }, + "node_modules/husky": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.5.tgz", + "integrity": "sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==", + "dev": true, + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -3177,6 +3304,15 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -3204,8 +3340,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/isbinaryfile": { "version": "5.0.2", @@ -3224,6 +3359,18 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, + "node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -3274,6 +3421,14 @@ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, + "node_modules/jquery-ui": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.14.0.tgz", + "integrity": "sha512-mPfYKBoRCf0MzaT2cyW5i3IuZ7PfTITaasO5OFLAQxrHuI+ZxruPa+4/K1OMNT8oElLWGtIxc9aRbyw20BKr8g==", + "dependencies": { + "jquery": ">=1.12.0 <5.0.0" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3371,143 +3526,616 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/locutus": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/locutus/-/locutus-2.0.15.tgz", - "integrity": "sha512-2xWC4RkoAoCVXEb/stzEgG1TNgd+mrkLBj6TuEDNyUoKeQ2XzDTyJUC23sMiqbL6zJmJSP3w59OZo+zc4IBOmA==", + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, "engines": { - "node": ">= 10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" - }, - "node_modules/lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "node_modules/line-column": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/line-column/-/line-column-1.0.2.tgz", + "integrity": "sha512-Ktrjk5noGYlHsVnYWh62FLVs4hTb8A3e+vucNZMgPeAOITdshMSgv4cCZQeRDjm7+goqmo6+liZwTXo+U3sVww==", "dev": true, - "peer": true - }, - "node_modules/lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, - "node_modules/lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + "dependencies": { + "isarray": "^1.0.0", + "isobject": "^2.0.0" + } }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "node_modules/lint-staged": { + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", "dev": true, - "peer": true - }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" - }, - "node_modules/lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" - }, - "node_modules/lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + "dependencies": { + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.6", + "execa": "~8.0.1", + "lilconfig": "~3.1.2", + "listr2": "~8.2.4", + "micromatch": "~4.0.8", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.5.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } }, - "node_modules/lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "node_modules/lint-staged/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, - "peer": true + "engines": { + "node": ">=18" + } }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=16.17.0" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "devOptional": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, - "dependencies": { + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/locutus": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/locutus/-/locutus-2.0.15.tgz", + "integrity": "sha512-2xWC4RkoAoCVXEb/stzEgG1TNgd+mrkLBj6TuEDNyUoKeQ2XzDTyJUC23sMiqbL6zJmJSP3w59OZo+zc4IBOmA==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + }, + "node_modules/lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" + }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "node_modules/lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + }, + "node_modules/lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "devOptional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", @@ -3569,6 +4197,19 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -3610,6 +4251,18 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -3741,9 +4394,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/negotiator": { "version": "0.6.3", @@ -3966,6 +4619,15 @@ "node": ">= 0.4" } }, + "node_modules/ohm-js": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/ohm-js/-/ohm-js-16.6.0.tgz", + "integrity": "sha512-X9P4koSGa7swgVQ0gt71UCYtkAQGOjciJPJAz74kDxWt8nXbH5HrDOQG6qBDH7SR40ktNv4x61BwpTDE9q4lRA==", + "dev": true, + "engines": { + "node": ">=0.12.1" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4011,6 +4673,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -4122,6 +4809,30 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -4136,6 +4847,21 @@ "node": ">=10.4.0" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4322,16 +5048,46 @@ } }, "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/retry": { @@ -4343,6 +5099,12 @@ "node": ">= 4" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4668,6 +5430,15 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4864,6 +5635,18 @@ "tmp": "^0.2.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -5112,6 +5895,18 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "devOptional": true }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -5207,6 +6002,17 @@ "regenerator-runtime": "^0.13.4" } }, + "@destination/prettier-plugin-twig": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@destination/prettier-plugin-twig/-/prettier-plugin-twig-1.5.0.tgz", + "integrity": "sha512-cTgy7lQOs0eTdNIqNQL9YDZ2ZFRyOzF5MfF07Lp8o1LLDMqjJgYfD382LIrXZ1E5s+ViYSgQJacR2U4Zx16XKw==", + "dev": true, + "requires": { + "html-styles": "^1.0.0", + "line-column": "^1.0.2", + "ohm-js": "^16.3.0" + } + }, "@develar/schema-utils": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", @@ -5489,6 +6295,11 @@ } } }, + "@fortawesome/fontawesome-free": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", + "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==" + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -5841,6 +6652,15 @@ "dev": true, "requires": {} }, + "ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "requires": { + "environment": "^1.0.0" + } + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6164,6 +6984,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -6384,13 +7213,13 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "requires": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^5.0.0" } }, "cli-spinners": { @@ -6456,6 +7285,12 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, + "colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -6627,11 +7462,11 @@ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "decompress-response": { @@ -7059,6 +7894,12 @@ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" }, + "environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true + }, "err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", @@ -7072,9 +7913,9 @@ "optional": true }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, "escape-string-regexp": { @@ -7083,6 +7924,12 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "optional": true }, + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -7188,6 +8035,15 @@ } } }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "fix-path": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fix-path/-/fix-path-4.0.0.tgz", @@ -7298,6 +8154,12 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true + }, "get-intrinsic": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", @@ -7450,9 +8312,9 @@ "dev": true }, "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "optional": true, "requires": { "function-bind": "^1.1.2" @@ -7467,6 +8329,12 @@ "lru-cache": "^6.0.0" } }, + "html-styles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/html-styles/-/html-styles-1.0.0.tgz", + "integrity": "sha512-cDl5dcj73oI4Hy0DSUNh54CAwslNLJRCCoO+RNkVo+sBrjA/0+7E/xzvj3zH/GxbbBLGJhE0hBe1eg+0FINC6w==", + "dev": true + }, "htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -7529,6 +8397,12 @@ "ms": "^2.0.0" } }, + "husky": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.5.tgz", + "integrity": "sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==", + "dev": true + }, "iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -7624,6 +8498,12 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -7639,8 +8519,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "peer": true + "dev": true }, "isbinaryfile": { "version": "5.0.2", @@ -7653,6 +8532,15 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, "jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -7691,6 +8579,14 @@ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, + "jquery-ui": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.14.0.tgz", + "integrity": "sha512-mPfYKBoRCf0MzaT2cyW5i3IuZ7PfTITaasO5OFLAQxrHuI+ZxruPa+4/K1OMNT8oElLWGtIxc9aRbyw20BKr8g==", + "requires": { + "jquery": ">=1.12.0 <5.0.0" + } + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -7778,6 +8674,222 @@ } } }, + "lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true + }, + "line-column": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/line-column/-/line-column-1.0.2.tgz", + "integrity": "sha512-Ktrjk5noGYlHsVnYWh62FLVs4hTb8A3e+vucNZMgPeAOITdshMSgv4cCZQeRDjm7+goqmo6+liZwTXo+U3sVww==", + "dev": true, + "requires": { + "isarray": "^1.0.0", + "isobject": "^2.0.0" + } + }, + "lint-staged": { + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "dev": true, + "requires": { + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.6", + "execa": "~8.0.1", + "lilconfig": "~3.1.2", + "listr2": "~8.2.4", + "micromatch": "~4.0.8", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.5.0" + }, + "dependencies": { + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true + }, + "execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + } + }, + "get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true + }, + "human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "listr2": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "dev": true, + "requires": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + } + }, + "emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + } + }, + "string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "requires": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "requires": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + } + } + } + }, "locutus": { "version": "2.0.15", "resolved": "https://registry.npmjs.org/locutus/-/locutus-2.0.15.tgz", @@ -7880,6 +8992,89 @@ "is-unicode-supported": "^0.1.0" } }, + "log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "requires": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "requires": { + "get-east-asian-width": "^1.0.0" + } + }, + "slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "requires": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + } + }, + "string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "requires": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "requires": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + } + } + } + }, "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -7951,6 +9146,16 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -7977,6 +9182,12 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true + }, "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -8070,9 +9281,9 @@ "dev": true }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "negotiator": { "version": "0.6.3", @@ -8223,6 +9434,12 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "optional": true }, + "ohm-js": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/ohm-js/-/ohm-js-16.6.0.tgz", + "integrity": "sha512-X9P4koSGa7swgVQ0gt71UCYtkAQGOjciJPJAz74kDxWt8nXbH5HrDOQG6qBDH7SR40ktNv4x61BwpTDE9q4lRA==", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8254,6 +9471,27 @@ "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" + }, + "dependencies": { + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + } } }, "p-cancelable": { @@ -8331,6 +9569,18 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true + }, "plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -8342,6 +9592,12 @@ "xmlbuilder": "^15.1.1" } }, + "prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -8489,13 +9745,30 @@ } }, "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "dependencies": { + "onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "requires": { + "mimic-function": "^5.0.0" + } + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } } }, "retry": { @@ -8504,6 +9777,12 @@ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true }, + "rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -8748,6 +10027,12 @@ "safe-buffer": "~5.1.0" } }, + "string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -8905,6 +10190,15 @@ "tmp": "^0.2.0" } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -9091,6 +10385,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "devOptional": true }, + "yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true + }, "yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 1d73f3a..5525018 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,12 @@ "web-auto-extractor": "1.0.17" }, "devDependencies": { + "@destination/prettier-plugin-twig": "^1.5.0", "electron": "32.0.2", - "electron-builder": "25.0.5" + "electron-builder": "25.0.5", + "husky": "^9.1.5", + "lint-staged": "^15.2.10", + "prettier": "^3.3.3" }, "scripts": { "start": "electron ./ --dev", diff --git a/readme.md b/readme.md index fe393e3..efd7846 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,3 @@ - **SideNoder** - A **cross platform sideloader** for Quest(1&2&3) standalone vr headset. # Quest 3 compatibility fix added in v 0.8.0 @@ -24,32 +23,37 @@ What makes sidenoder better than other sideloaders ? ## Running the compiled version #### Run precompiled release on windows: + 1. Install latest [winfsp](https://github.com/billziss-gh/winfsp/releases/latest) 2. Download and unpack(or run Setup.exe) the latest [windows release](https://github.com/VRPirates/sidenoder/releases/latest) 3. Run the `sidenoder.exe` application #### Run precompiled release on linux: + 1. Download and unpack the latest [AppImage/deb](https://github.com/VRPirates/sidenoder/releases/latest) 2. Make the AppImage executable. Or install deb package 3. Run the AppImage #### Run precompiled release on mac: + 1. Install latest [osxfuse](https://github.com/osxfuse/osxfuse/releases) (4.2.0+) 2. Download and unpack the latest [mac release](https://github.com/VRPirates/sidenoder/releases/latest) (.App) 3. Run the .App - --- ## MacOS compatibility To install on mac one has to manually install latest rclone in Terminal using following command: + ```bash sudo -v ; curl https://rclone.org/install.sh | sudo bash ``` + Other version of rclone will not work. Also android tools and scrcpy is required: + ```bash brew install scrcpy brew install android-platform-tools @@ -60,4 +64,3 @@ brew install android-platform-tools Please report any issues here : https://github.com/VRPirates/sidenoder/issues - diff --git a/removeLocales.js b/removeLocales.js index a5cffcb..14301b4 100644 --- a/removeLocales.js +++ b/removeLocales.js @@ -1,12 +1,12 @@ //https://www.electron.build/configuration/configuration#afterpack -const LOCALES = ['en-US.pak', 'ru.pak']; +const LOCALES = ["en-US.pak", "ru.pak"]; -exports.default = async function(context) { +exports.default = async function (context) { // console.log(context); - var fs = require('fs'); - var localeDir = context.appOutDir + '/locales/'; + var fs = require("fs"); + var localeDir = context.appOutDir + "/locales/"; - fs.readdir(localeDir, function(err, files) { + fs.readdir(localeDir, function (err, files) { //files is array of filenames (basename form) if (!(files && files.length)) return; for (const file of files) { @@ -14,4 +14,4 @@ exports.default = async function(context) { fs.unlinkSync(localeDir + file); } }); -} +}; diff --git a/tools.js b/tools.js index 4e511b7..dda4c95 100644 --- a/tools.js +++ b/tools.js @@ -1,2689 +1,2833 @@ -const exec = require('child_process').exec; -// const fs = require('fs'); -const fs = require('fs'); -const fsp = fs.promises; -const util = require('util'); -const path = require('path'); -const crypto = require('crypto'); -const commandExists = require('command-exists'); -const { dialog } = require('electron') -const ApkReader = require('adbkit-apkreader'); -const adbkit = require('@devicefarmer/adbkit').default; -const adb = adbkit.createClient(); -const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); -const WAE = require('web-auto-extractor').default -// const HttpProxyAgent = require('https-proxy-agent'); // TODO add https proxy support -const { SocksProxyAgent } = require('socks-proxy-agent'); -const url = require('url'); -// const ApkReader = require('node-apk-parser'); -const fixPath = (...args) => import('fix-path').then(({default: fixPath}) => fixPath(...args)); -// adb.kill(); - -const pkg = require('./package.json'); -const _sec = 1000; -const _min = 60 * _sec; - -let CHECK_META_PERIOD = 2 * _min; -const l = 32; -const configLocationOld = path.join(global.homedir, 'sidenoder-config.json'); -const configLocation = path.join(global.sidenoderHome, 'config.json'); - -let agentOculus, - agentSteam, - agentSQ; - -init(); - -const GAME_LIST_NAMES = global.currentConfiguration.gameListNames || [ - 'FFA.txt', - 'GameList.txt', - 'VRP-GameList.txt/VRP-GameList.txt', - 'VRP-GameList.txt', - 'Dynamic.txt', -]; -let META_VERSION = []; -let QUEST_ICONS = []; -let cacheOculusGames = false; -let KMETAS = {}; -let KMETAS2 = {}; - -let adbCmd = 'adb'; -let grep_cmd = '| grep '; -if (platform == 'win') { - grep_cmd = '| findstr '; -} - -let RCLONE_ID = 0; - - -module.exports = -{ -//properties - resetCache, -//methods - getDeviceSync, - trackDevices, - checkDeps, - checkMount, - mount, - killRClone, - getDir, - returnError, - sideloadFolder, - getInstalledApps, - getInstalledAppsWithUpdates, - getApkFromFolder, - uninstall, - getDirListing, - getPackageInfo, - wifiGetStat, - wifiEnable, - connectWireless, - disconnectWireless, - enableMTP, - startSCRCPY, - rebootDevice, - rebootRecovery, - rebootBootloader, - sideloadFile, - getLaunchActivity, - getActivities, - startActivity, - devOpenUrl, - checkAppTools, - changeAppConfig, - backupApp, - backupAppData, - restoreAppData, - getDeviceInfo, - getStorageInfo, - getUserInfo, - getFwInfo, - getBatteryInfo, - changeConfig, - reloadConfig, - execShellCommand, - updateRcloneProgress, - deviceTweaksGet, - deviceTweaksSet, - appInfo, - appInfoEvents, - isIdle, - wakeUp, - detectInstallTxt, - detectNoteTxt, - // ... -} - -async function getDeviceInfo() { - if (!global.adbDevice) { - return { - success: false, - }; - } - // console.log('getDeviceInfo()'); - - const storage = await getStorageInfo(); - const user = await getUserInfo(); - const fw = await getFwInfo(); - const battery = await getBatteryInfo(); - const ip = await getDeviceIp(); - const wifi = await wifiGetStat(); - - const res = { - success: !!storage, - storage, - user, - fw, - battery, - ip, - wifi, - }; - - // console.log('getDeviceInfo', res); - return res; -} - -async function getFwInfo() { - console.log('getFwInfo()'); - const res = await adbShell('getprop ro.build.branch'); - if (!res) return false; - - return { - version: res.replace('releases-oculus-', ''), - } -} - -async function getBatteryInfo() { - console.log('getBatteryInfo()'); - const res = await adbShell('dumpsys battery'); - if (!res) return false; - - return parceOutOptions(res); -} - -async function getUserInfo() { - if (global.currentConfiguration.userHide) return { - name: '<i>hidden</i>' - }; - - console.log('getUserInfo()'); - const res = await adbShell('dumpsys user | grep UserInfo'); - if (!res) return false; - - return { - name: res.split(':')[1], - } -} - -async function deviceTweaksGet(arg) { - console.log('deviceTweaksGet()', arg); - let res = { - cmd: 'get', - // mp_name: '', - // guardian_pause: '0', - // frc: '0', - // gRR: '72', - // gCA: '-1', - // gFFR: '2', - // CPU: '2', - // GPU: '2', - // vres: '1024', - // cres: '640x480', - // gSSO: '1440x1584', - }; - - if (arg.key === 'mp_name') res.mp_name = (await adbShell('settings get global username')); - if (arg.key === 'guardian_pause') res.guardian_pause = (await adbShell('getprop debug.oculus.guardian_pause')); - if (arg.key === 'frc') res.frc = (await adbShell('getprop debug.oculus.fullRateCapture')); - if (arg.key === 'gRR') res.gRR = (await adbShell('getprop debug.oculus.refreshRate')); - if (arg.key === 'gCA') res.gCA = (await adbShell('getprop debug.oculus.forceChroma')); - if (arg.key === 'gFFR') res.gFFR = (await adbShell('getprop debug.oculus.foveation.level')); - if (arg.key === 'CPU') res.CPU = (await adbShell('getprop debug.oculus.cpuLevel')); - if (arg.key === 'GPU') res.GPU = (await adbShell('getprop debug.oculus.gpuLevel')); - if (arg.key === 'vres') res.vres = (await adbShell('getprop debug.oculus.videoResolution')); - if (arg.key === 'cres') { - let captureDims = - (await adbShell("getprop debug.oculus.capture.width")) + - "x" + - (await adbShell("getprop debug.oculus.capture.height")); - - // Default when not set - if (captureDims === "x") { - captureDims = "3840x1920"; - } - res.cres = captureDims; - } - if (arg.key === 'gSSO') res.gSSO = (await adbShell('getprop debug.oculus.textureWidth')) + 'x' + (await adbShell('getprop debug.oculus.textureHeight')); - //oculus.capture.bitrate - - return res; -} - -async function deviceTweaksSet(arg) { - console.log('deviceTweaksSet()', arg); - let res = {cmd: 'set'}; - if (typeof arg.mp_name != 'undefined') { - res.mp_name = await adbShell('settings put global username ' + arg.mp_name); - } - - if (typeof arg.guardian_pause != 'undefined') { - res.guardian_pause = await adbShell('setprop debug.oculus.guardian_pause ' + (arg.guardian_pause ? '1' : '0')); - } - if (typeof arg.frc != 'undefined') { - res.frc = await adbShell('setprop debug.oculus.fullRateCapture ' + (arg.frc ? '1' : '0')); - } - - if (typeof arg.gRR != 'undefined') { - res.gRR = await adbShell('setprop debug.oculus.refreshRate ' + arg.gRR); - } - - if (typeof arg.gCA != 'undefined') { - res.gCA = await adbShell('setprop debug.oculus.forceChroma ' + arg.gCA); - } - - if (typeof arg.gFFR != 'undefined') { - res.gFFR = await adbShell('setprop debug.oculus.foveation.level ' + arg.gFFR); - } - - if (typeof arg.CPU != 'undefined') { - res.CPU = await adbShell('setprop debug.oculus.cpuLevel ' + arg.CPU); - } - - if (typeof arg.GPU != 'undefined') { - res.GPU = await adbShell('setprop debug.oculus.gpuLevel ' + arg.GPU); - } - - if (typeof arg.vres != 'undefined') { - res.vres = await adbShell('setprop debug.oculus.videoResolution ' + arg.vres); - } - - if (typeof arg.cres != 'undefined') { - const [width, height] = arg.cres.split('x'); - await adbShell('setprop debug.oculus.capture.width ' + width); - res.cres = await adbShell('setprop debug.oculus.capture.height ' + height); - } - - if (typeof arg.gSSO != 'undefined') { - const [width, height] = arg.gSSO.split('x'); - await adbShell('setprop debug.oculus.textureWidth ' + width); - await adbShell('setprop debug.oculus.textureHeight ' + height); - res.gSSO = await adbShell('settings put system font_scale 0.85 && settings put system font_scale 1.0'); - } - - return res; -} - -async function getStorageInfo() { - console.log('getStorageInfo()'); - - const linematch = await adbShell('df -h | grep "/storage/emulated"'); - if (!linematch) return false; - - const refree = new RegExp('([0-9(.{1})]+[a-zA-Z%])', 'g'); - const storage = linematch.match(refree); - console.log(storage) - - if (storage.length == 3) { - return { - size: storage[0], - used: storage[1], - free: 0, - percent: storage[2], - }; - } - - return { - size: storage[0], - used: storage[1], - free: storage[2], - percent: storage[3], - }; -} - -async function getLaunchActivity(pkg) { - console.log('startApp()', pkg); - const activity = await adbShell(`dumpsys package ${pkg} | grep -A 1 'filter' | head -n 1 | cut -d ' ' -f 10`); - return startActivity(activity); -} - -async function getActivities(pkg, activity = false) { - console.log('getActivities()', pkg); - - let activities = await adbShell(`dumpsys package | grep -Eo '^[[:space:]]+[0-9a-f]+[[:space:]]+${pkg}/[^[:space:]]+' | grep -oE '[^[:space:]]+$'`); - if (!activities) return false; - - activities = activities.split('\n'); - // activities.pop(); - console.log({ pkg, activities }); - // TODO: check manifest.application.launcherActivities - - return activities; -} - -async function startActivity(activity) { - console.log('startActivity()', activity); - wakeUp(); - const result = await adbShell(`am start ${activity}`); // TODO activity selection - - console.log('startActivity', activity, result); - return result; -} - -async function devOpenUrl(url) { - console.log('devOpenUrl', url); - wakeUp(); - const result = await adbShell(`am start -a android.intent.action.VIEW -d "${url}"`); // TODO activity selection - - console.log('devOpenUrl', url, result); - return result; -} - -async function readAppCfg(pkg) { - let config = await adbShell(`cat /sdcard/Android/data/${pkg}/private/config.json 1>&1 2> /dev/null`); - try { - config = config && JSON.parse(config); - } - catch (e) { - console.error('readAppCfg', e); - config = false; - } - - return config; -} - -async function checkAppTools(pkg) { - const backupPath = path.join(global.sidenoderHome, 'backup_data', pkg); - const availableBackup = await adbFileExists(`/sdcard/Android/data/${pkg}`); - let availableRestore = false; - let availableConfig = false; - if (await fsp.exists(backupPath)) { - try { - availableRestore = await fsp.readFile(`${backupPath}/time.txt`, 'utf8'); - } - catch (err) { - availableRestore = 1; - } - } - - if (availableBackup) { - availableConfig = await readAppCfg(pkg); - } - - return { - success: true, - pkg, - availableRestore, - availableConfig, - }; -} - -async function changeAppConfig(pkg, key, val) { - console.log('changeAppConfig()', { pkg, key, val }); - const res = { - pkg, - key, - val, - success: false, - }; - - let config = await readAppCfg(pkg); - try { - config = Object.assign(config, { [key]: val }); - adbShell(`echo '${JSON.stringify(config)}' > "/sdcard/Android/data/${pkg}/private/config.json"`); - config = await readAppCfg(pkg); - res.val = config && config[key]; - res.success = !!config; - } - catch(e) { - console.error('changeAppConfig', res, e); - } - - return res; -} - -// Implementation ---------------------------------- - -async function getDeviceIp() { - // let ip = await adb.getDHCPIpAddress(global.adbDevice); - // if (ip) return ip; - if (!global.adbDevice && global.currentConfiguration.lastIp) { - return global.currentConfiguration.lastIp; - } - - let ip = await adbShell(`ip -o route get to 8.8.8.8 | sed -n 's/.*src \\([0-9.]\\+\\).*/\\1/p'`); - console.log({ip}); - if (ip) return ip; - - ip = await adbShell(`ip addr show wlan0 | grep 'inet ' | cut -d ' ' -f 6 | cut -d / -f 1`); - console.log({ip}); - if (ip) return ip; - return false; -} - -async function wifiGetStat() { - const on = await adbShell('settings get global wifi_on'); - return on && +on; -} - -async function wifiEnable(enable) { - return adbShell(`svc wifi ${enable ? 'enable' : 'disable'}`); -} - -async function connectWireless() { - const on = await adbShell('settings get global wifi_on'); - if (!(await wifiGetStat())) { - console.error('connectWireless', 'wifi disabled'); - await wifiEnable(true); - return false; - } - - // await adbShell(`setprop service.adb.tcp.port 5555`); - // TODO: save ip & try use it - const ip = await getDeviceIp(); - console.log({ ip }); - if (!ip) return false; - - try { - if (global.adbDevice) { - const device = adb.getDevice(global.adbDevice); - const port = await device.tcpip(); - await device.waitForDevice(); - console.log('set tcpip', port); - await changeConfig('lastIp', ip); - } - - const deviceTCP = await adb.connect(ip, 5555); - // await deviceTCP.waitForDevice(); - console.log('connectWireless', { ip, res: deviceTCP }); - - return ip; - } - catch (err) { - console.error('connectWireless', err); - await changeConfig('lastIp', ''); - return false; - } -} - -async function disconnectWireless() { - const ip = await getDeviceIp(); - if (!ip) return false; - - try { - const res = await adb.disconnect(ip, 5555); - // const res = await adb.usb(global.adbDevice); - console.log('disconnectWireless', { ip, res }); - // await changeConfig('lastIp', ''); - // await getDeviceSync(); - return res; - } - catch (err) { - console.error('disconnectWireless.error', err); - return !(await isWireless()); - } -} - -async function isWireless() { - try { - const devices = await adb.listDevices(); - for (const device of devices) { - if (!device.id.includes(':5555')) continue; - if (['offline', 'authorizing'].includes(device.type)) continue; - if (['unauthorized'].includes(device.type)) { - win.webContents.send('alert', 'Please authorize adb access on your device'); - continue; - } - - console.log('device.id', device.type); - return device.id; - } - - return false; - } - catch (err) { - console.error('Something went wrong:', err.stack); - return false; - } -} - -async function enableMTP() { - const res = await adbShell(`svc usb setFunctions mtp`); - console.log('enableMTP', { res }); - return res; -} - -async function isIdle() { - const res = await adbShell(`dumpsys deviceidle | grep mScreenOn`); - console.log(res, res.includes('true')); - return !res.includes('true'); -} - -async function wakeUp() { - if (!(await isIdle())) return; - return adbShell(`input keyevent KEYCODE_POWER`); -} - -async function startSCRCPY() { - console.log('startSCRCPY()'); - if (!global.currentConfiguration.scrcpyPath && !(await commandExists('scrcpy'))) { - returnError('Can`t find scrcpy binary'); - return; - } - - const scrcpyCmd = `"${global.currentConfiguration.scrcpyPath || 'scrcpy'}" ` + - (global.currentConfiguration.scrcpyCrop ? `--crop ${global.currentConfiguration.scrcpyCrop} `: '') + - `-b ${global.currentConfiguration.scrcpyBitrate || 1}M ` + - (global.currentConfiguration.scrcpyFps ? `--max-fps ${global.currentConfiguration.scrcpyFps} ` : '') + - (global.currentConfiguration.scrcpySize ? `--max-size ${global.currentConfiguration.scrcpySize} ` : '') + - (!global.currentConfiguration.scrcpyWindow ? '-f ' : '') + - (global.currentConfiguration.scrcpyOnTop ? '--always-on-top ' : '') + - (!global.currentConfiguration.scrcpyControl ? '-n ' : '') + - '--window-title "SideNoder Stream" ' + - `-s ${global.adbDevice} `; - console.log({ scrcpyCmd }); - wakeUp(); - exec(scrcpyCmd, (error, stdout, stderr) => { - if (error) { - console.error('scrcpy error:', error); - win.webContents.send('cmd_sended', { success: error }); - return; - } - - if (stderr) { - console.error('scrcpy stderr:', stderr); - // win.webContents.send('cmd_sended', { success: stderr }); - return; - } - - console.log('scrcpy stdout:', stdout); - }); - - return scrcpyCmd; -} - -async function rebootDevice() { - const res = await adbShell(`reboot`); - console.log('rebootDevice', { res }); - return res; -} -async function rebootRecovery() { - const res = await adbShell(`reboot recovery`); - console.log('rebootRecovery', { res }); - return res; -} -async function rebootBootloader() { - const res = await adbShell(`reboot bootloader`); - console.log('rebootBootloader', { res }); - return res; -} -async function sideloadFile(path) { - const res = await execShellCommand(`"${adbCmd}" sideload "${path}"`); - console.log('sideloadFile', { res }); - return res; -} - -async function getDeviceSync(attempt = 0) { - try { - // const lastDevice = global.adbDevice; - const devices = await adb.listDevices(); - console.log({ devices }); - global.adbDevice = false; - for (const device of devices) { - if (['offline', 'authorizing'].includes(device.type)) continue; - if (['unauthorized'].includes(device.type)) { - win.webContents.send('alert', 'Please authorize adb access on your device'); - continue; - } - - if ( - !global.currentConfiguration.allowOtherDevices - && await adbShell('getprop ro.product.brand', device.id) != 'oculus' - ) continue; - - global.adbDevice = device.id; - } - - - /*if (!global.adbDevice && devices.length > 0 && attempt < 1) { - return setTimeout(()=> getDeviceSync(attempt + 1), 1000); - }*/ - // if (lastDevice == global.adbDevice) return; - - win.webContents.send('check_device', { success: global.adbDevice }); - - return global.adbDevice; - } - catch (err) { - console.error('Something went wrong:', err.stack); - } -} - - -/** - * Executes a shell command and return it as a Promise. - * @param cmd {string} - * @return {Promise<string>} - */ -async function adbShell(cmd, deviceId = global.adbDevice, skipRead = false) { - try { - if (!deviceId) { - throw 'device not defined'; - } - - global.adbError = null; - const r = await adb.getDevice(deviceId).shell(cmd); - // console.timeLog(cmd); - if (skipRead) { - console.log(`adbShell[${deviceId}]`, { cmd, skipRead }); - return true; - } - - let output = await adbkit.util.readAll(r); - output = await output.toString(); - // output = output.split('\n'); - // const end = output.pop(); - // if (end != '') output.push(); - console.log(`adbShell[${deviceId}]`, { cmd, output }); - if (output.substr(-1) == '\n') return output.slice(0, -1); - return output; - } - catch (err) { - console.error(`adbShell[${deviceId}]: err`, {cmd}, err); - global.adbError = err; - if (err.toString() == `FailError: Failure: 'device offline'`) { - getDeviceSync(); - } - - return false; - } -} - -function parceOutOptions(line) { - let opts = {}; - for (let l of line.split('\n')) { - l = l.split(' ').join(''); - let [k, v] = l.split(':'); - - if (v == 'true') v = true; - if (v == 'false') v = false; - if (!isNaN(+v)) v = +v; - - opts[k] = v; - } - - return opts; -} - -// on empty dirrectory return false -async function adbFileExists(path) { - const r = await adbShell(`ls "${path}" 1>&1 2> /dev/null`); - return r; -} - -async function adbPull(orig, dest, sync = false) { - console.log('adbPull', orig, dest); - const transfer = sync - ? await sync.pull(orig) - : await adb.getDevice(global.adbDevice).pull(orig); - return new Promise(function(resolve, reject) { - let c = 0; - transfer.on('progress', (stats) => { - c++; - if (c % 40 != 1) return; // skip 20 events - - // console.log(orig + ' pulled', stats); - const res = { - cmd: 'pull', - bytes: stats.bytesTransferred, - size: 0, - percentage: 0, - speedAvg: 0, - eta: 0, - name: orig, - } - win.webContents.send('process_data', res); - }); - transfer.on('end', () => { - console.log(orig, 'pull complete'); - win.webContents.send('process_data', false); - resolve(true); - }); - transfer.on('error', (err) => { - console.error('adb_pull_stderr', err); - win.webContents.send('process_data', false); - reject(err); - }); - transfer.pipe(fs.createWriteStream(dest)); - }); -} - -async function adbPullFolder(orig, dest, sync = false) { - console.log('pullFolder', orig, dest); - /*let need_close = false; - if (!sync) { - need_close = true; - sync = await adb.getDevice(global.adbDevice).syncService(); - }*/ - - let actions = []; - await fsp.mkdir(dest, { recursive: true }); - const files = sync - ? await sync.readdir(orig) - : await adb.getDevice(global.adbDevice).readdir(orig); - - for (const file of files) { - const new_orig = `${orig}/${file.name}`; - const new_dest = path.join(dest, file.name); - if (file.isFile()) { - actions.push(adbPull(new_orig, new_dest, sync)); // file.size - continue; - } - - actions.push(adbPullFolder(new_orig, new_dest, sync)); - } - - await Promise.all(actions); - - // if (need_close) sync.end(); - - return true; -} - -async function adbPush(orig, dest, sync = false) { - console.log('adbPush', orig, dest); - const transfer = sync - ? await sync.pushFile(orig, dest) - : await adb.getDevice(global.adbDevice).push(orig, dest); - const stats = await fsp.lstat(orig); - const size = stats.size; - - return new Promise(function(resolve, reject) { - let c = 0 - transfer.on('progress', (stats) => { - c++; - if (c % 40 != 1) return; // skip 20 events - - // console.log(orig + ' pushed', stats); - const res = { - cmd: 'push', - bytes: stats.bytesTransferred, - size, - percentage: (stats.bytesTransferred * 100 / size).toFixed(2), - speedAvg: 0, - eta: 0, - name: orig, - } - win.webContents.send('process_data', res); - }); - transfer.on('end', () => { - console.log(orig, 'push complete'); - win.webContents.send('process_data', false); - resolve(true); - }); - transfer.on('error', (err) => { - console.error('adb_push_stderr', err); - win.webContents.send('process_data', false); - reject(err); - }); - }); -} - -async function adbPushFolder(orig, dest, sync = false) { - console.log('pushFolder', orig, dest); - - const stat = await fsp.lstat(orig); - console.log({ orig, stat }, stat.isFile()); - if (stat.isFile()) return adbPush(orig, dest); - - /*let need_close = false; - if (!sync) { - need_close = true; - sync = await adb.getDevice(global.adbDevice).syncService(); - }*/ - - let actions = []; - await adbShell(`mkdir -p ${dest}`, global.adbDevice, true); - const files = await fsp.readdir(orig, { withFileTypes: true }); - for (const file of files) { - const new_orig = path.join(orig, file.name); - const new_dest = `${dest}/${file.name}`; - if (file.isFile()) { - actions.push(adbPush(new_orig, new_dest, sync)); - continue; - } - - actions.push(adbPushFolder(new_orig, new_dest, sync)); - } - - await Promise.all(actions); - - // if (need_close) sync.end(); - - return true; -} - -async function adbInstall(apk) { - console.log('adbInstall', apk); - const temp_path = '/data/local/tmp/install.apk'; - - await adbPush(apk, temp_path, false, false); - try { - await adb.getDevice(global.adbDevice).installRemote(temp_path); - } - catch (err) { - adbShell(`rm ${temp_path}`); - throw err; - } - - return true; -} - -function execShellCommand(cmd, ignoreError = false, buffer = 100) { - console.log({cmd}); - return new Promise((resolve, reject) => { - exec(cmd, {maxBuffer: 1024 * buffer}, (error, stdout, stderr) => { - if (error) { - if (ignoreError) return resolve(false); - console.error('exec_error', cmd, error); - return reject(error); - } - - if (stdout || !stderr) { - console.log('exec_stdout', cmd, stdout); - return resolve(stdout); - } - else { - if (ignoreError) return resolve(false); - console.error('exec_stderr', cmd, stderr); - return reject(stderr); - } - }); - }); -} - - -async function trackDevices() { - console.log('trackDevices()'); - await getDeviceSync(); - - try { - const tracker = await adb.trackDevices() - tracker.on('add', async (device) => { - console.log('Device was plugged in', device.id); - // await getDeviceSync(); - }); - - tracker.on('remove', async (device) => { - console.log('Device was unplugged', device.id); - // await getDeviceSync(); - }); - - tracker.on('change', async (device) => { // TODO: // need fix double run - console.log('Device was changed', device.id); - await getDeviceSync(); - }); - - tracker.on('end', () => { - console.error('Tracking stopped'); - trackDevices(); - }); - } - catch (err) { - console.error('Something went wrong:', err.stack); - returnError(err); - } -} - -async function appInfo(args) { - const { res, pkg } = args; - const app = KMETAS[pkg]; - - let data = { - res, - pkg, - id: 0, - name: app.simpleName, - short_description: '', - detailed_description: '', - about_the_game: '', - supported_languages: '', - genres: [], - header_image: '', - screenshots: [], - url: '', - }; - - try { - if (res == 'steam') { - const steam = app && app.steam; - if (!steam || !steam.id) throw 'incorrect args'; - - data.id = steam.id; - data.url = `https://store.steampowered.com/app/${data.id}/`; - - const resp = await fetchTimeout(`https://store.steampowered.com/api/appdetails?appids=${data.id}`, { - headers: { 'Accept-Language': global.locale + ',en-US;q=0.5,en;q=0.3' }, - agent: agentSteam, - }); - const json = await resp.json(); - // console.log({ json }); - - Object.assign(data, json[data.id].data); - } - - if (res == 'oculus') { - const oculus = app && app.oculus; - if (!oculus || !oculus.id) throw 'incorrect args'; - // console.log({ oculus }); - - data.id = oculus.id; - data.url = `https://www.oculus.com/experiences/quest/${data.id}`; - // data.genres = oculus.genres && oculus.genres.split(', '); - - //https://computerelite.github.io - let resp = await fetchTimeout(`https://graph.oculus.com/graphql?forced_locale=${global.locale}`, { - method: 'POST', - body: `access_token=OC|1317831034909742|&variables={"itemId":"${oculus.id}","first":1}&doc_id=5373392672732392`, - headers: { - 'Accept-Language': global.locale + ',en-US;q=0.5,en;q=0.3', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Origin': 'https://www.oculus.com', - }, - agent: agentOculus, - }); - try { - let json = await resp.json(); - // console.log('json', json); - if (json.error) throw json.error; - - const meta = json.data.node; - if (!meta) throw 'empty json.data.node'; - - data.name = meta.appName; - data.detailed_description = meta.display_long_description && meta.display_long_description.split('\n').join('<br/>'); - data.genres = meta.genre_names; - - if (meta.supported_in_app_languages) { - data.supported_languages = meta.supported_in_app_languages.map(({name}) => name).join(', '); - } - //meta.internet_connection_name, - //meta.quality_rating_aggregate, - //meta.release_date, - - if (meta.website_page_meta) { - data.header_image = meta.website_page_meta.image_url; - data.short_description = meta.website_page_meta.description && meta.website_page_meta.description.split('\n').join('<br/>'); - data.url = meta.website_page_meta.page_url; - } - - if (meta.screenshots) { - for (const { uri } of meta.screenshots) { - data.screenshots.push({ - // id, - path_thumbnail: uri, - }); - } - } - - if (meta.trailer && meta.trailer.uri) { - data.movies = [{ mp4: { '480': meta.trailer.uri } }]; - } - } - catch(err) { - console.error(res, 'fetch error', err); - - resp = await fetchTimeout(`${data.url}?locale=${global.locale}`, { - agent: agentOculus, - }); - const meta = await WAE().parse(await resp.text()); - const { metatags } = meta; - // console.log('meta', meta); - - data.name = metatags['og:title'][0].replace(' on Oculus Quest', ''); - data.header_image = metatags['og:image'][0]; - data.short_description = metatags['og:description'][0] && metatags['og:description'][0].split('\n').join('<br/>'); - data.url = metatags['al:web:url'][0]; - - const jsonld = meta.jsonld.Product && meta.jsonld.Product[0] || JSON.parse(metatags['json-ld'][0]); - // console.log(jsonld); - - if (jsonld) { - if (jsonld.name) data.name = jsonld.name; - data.detailed_description = jsonld.description && jsonld.description.split('\n').join('<br/>'); - - if (jsonld.image) { - for (const id in jsonld.image) { - if (['0', '1', '2'].includes(id)) continue; // skip resizes of header - - data.screenshots.push({ - id, - path_thumbnail: jsonld.image[id], - }); - } - } - } - } - } - - if (res == 'sq') { - const sq = app && app.sq; - if (!sq || !sq.id) throw 'incorrect args'; - // console.log({ sq }); - - data.id = sq.id; - data.url = `https://sidequestvr.com/app/${data.id}/`; - - const resp = await fetchTimeout(`https://api.sidequestvr.com/get-app`, { - method: 'POST', - body: JSON.stringify({ apps_id: data.id }), - headers: { - 'Accept-Language': global.locale + ',en-US;q=0.5,en;q=0.3', - 'Content-Type': 'application/json', - 'Origin': 'https://sidequestvr.com', - 'Cookie': ' __stripe_mid=829427af-c8dd-47d1-a857-1dc73c95b947201218; cf_clearance=LkOSetFAXEs255r2rAMVK_hm_I0lawkUfJAedj1nkD0-1633288577-0-250; __stripe_sid=6e94bd6b-19a4-4c34-98d5-1dc46423dd2e2f3688', - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0', - }, - agent: agentSQ, - }); - const json = await resp.json(); - const meta = json.data[0]; - data.name = meta.name; - data.header_image = meta.image_url; - data.short_description = meta.summary; - data.detailed_description = meta.description.split('\n').join('<br/>'); - if (meta.video_url) - data.youtube = [meta.video_url - .replace('youtube.com', 'youtube.com/embed') - .replace('youtu.be', 'youtube.com/embed') - .replace('/embed/embed', '/embed') - .replace('/watch?v=', '/') - ]; - - const resp_img = await fetchTimeout(`https://api.sidequestvr.com/get-app-screenshots`, { - method: 'POST', - body: JSON.stringify({ apps_id: data.id }), - headers: { - 'Content-Type': 'application/json', - 'Origin': 'https://sidequestvr.com', - 'Cookie': ' __stripe_mid=829427af-c8dd-47d1-a857-1dc73c95b947201218; cf_clearance=LkOSetFAXEs255r2rAMVK_hm_I0lawkUfJAedj1nkD0-1633288577-0-250; __stripe_sid=6e94bd6b-19a4-4c34-98d5-1dc46423dd2e2f3688', - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0', - - }, - agent: agentSQ, - }); - const json_img = await resp_img.json(); - for (const id in json_img.data) { - data.screenshots.push({ - id, - path_thumbnail: json_img.data[id].image_url, - }); - } - } - } - catch (err) { - console.error('appInfo', { args, data }, err); - } - - return { success: true, data }; -} - -async function appInfoEvents(args) { - const { res, pkg } = args; - const app = KMETAS[pkg]; - let data = { - res, - pkg, - events: [], - }; - - try { - if (res == 'steam') { - const steam = app && app.steam; - if (!steam || !steam.id) throw 'incorrect args'; - - data.url = `https://store.steampowered.com/news/app/${steam.id}/`; - - const resp = await fetchTimeout(`http://api.steampowered.com/ISteamNews/GetNewsForApp/v0002?appid=${steam.id}`, { - headers: { 'Accept-Language': global.locale + ',en-US;q=0.5,en;q=0.3' }, - agent: agentSteam, - }); - const json = await resp.json(); - // console.log({ json }); - - const events = json.appnews.newsitems; - for (const e of events) { - const event = { - title: e.title, - url: e.url, - date: (new Date(e.date * _sec)).toLocaleString(), - // author: e.author, - } - - event.contents = e.contents - .split('\n').join('<br/>') - .split('[img]').join('<center><img src="') - .split('[/img]').join('" /></center>') - .split('{STEAM_CLAN_IMAGE}').join('https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/clans') - .split('[list]').join('<ul>') - .split('[/list]').join('</ul>') - .split('[*]').join('</li><li>') - // .split('[b]').join('<b>') - // .split('[/b]').join('</b>') - // .split('[i]').join('<i>') - // .split('[/i]').join('</i>') - .split('[').join('<') - .split(']').join('>') - data.events.push(event); - } - } - - if (res == 'oculus') { - const oculus = app && app.oculus; - if (!oculus || !oculus.id) throw 'incorrect args'; - - // data.url = `https://store.steampowered.com/news/app/${steam.id}/`; - - let resp = await fetchTimeout(`https://graph.oculus.com/graphql?forced_locale=${global.locale}`, { - method: 'POST', - body: `access_token=OC|1317831034909742|&variables={"id":"${oculus.id}"}&doc_id=1586217024733717`, - headers: { - 'Accept-Language': global.locale + ',en-US;q=0.5,en;q=0.3', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Origin': 'https://www.oculus.com', - }, - agent: agentOculus, - }); - try { - let json = await resp.json(); - if (json.error) throw json.error; - - // console.log({ json }); - const events = json.data.node.supportedBinaries.edges; - for (const { node } of events) { - const e = node; - const event = { - id: e.id, - title: `${e.version} (versionCode: ${e.versionCode})`, - contents: e.changeLog && e.changeLog.split('\n').join('<br/>'), - // richChangeLog: e.richChangeLog, - // url: '', - // date: '', - // author: '', - }; - - if (e.richChangeLog) console.log('RICHCHANGELOG', e.richChangeLog); - - data.events.push(event); - } - } - catch (err) { - console.error(res, 'fetch error', err); - } - - resp = await fetch(`https://computerelite.github.io/tools/Oculus/OlderAppVersions/${oculus.id}.json`); - json = await resp.json(); - // console.log({ json }); - const events = json.data.node.binaries.edges; - for (const { node } of events) { - const e = node; - let found = false; - for (const i in data.events) { - if (data.events[i].id != e.id) continue; - - data.events[i].date = (new Date(e.created_date * _sec)).toLocaleString(); - found = true; - break; - } - if (found) continue; - - const event = { - id: e.id, - title: `${e.version} (versionCode: ${e.version_code})`, - date: (new Date(e.created_date * _sec)).toLocaleString(), - contents: e.change_log.split('\n').join('<br/>'), - // url: '', - // author: '', - }; - - data.events.push(event); - } - } - - if (res == 'sq') { - const sq = app && app.sq; - if (!sq || !sq.id) throw 'incorrect args'; - // console.log({ sq }); - - for (const is_news of [true, false]) { - const resp = await fetchTimeout(`https://api.sidequestvr.com/events-list`, { - method: 'POST', - body: JSON.stringify({ apps_id: sq.id, is_news }), - headers: { - 'Accept-Language': global.locale + ',en-US;q=0.5,en;q=0.3', - 'Content-Type': 'application/json', - 'Origin': 'https://sidequestvr.com', - 'Cookie': ' __stripe_mid=829427af-c8dd-47d1-a857-1dc73c95b947201218; cf_clearance=LkOSetFAXEs255r2rAMVK_hm_I0lawkUfJAedj1nkD0-1633288577-0-250; __stripe_sid=6e94bd6b-19a4-4c34-98d5-1dc46423dd2e2f3688', - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0', - }, - agent: agentSQ, - }); - const json = await resp.json(); - // console.log({ json }); - for (const e of json.data) { - const event = { - id: e.events_id, - title: e.event_name, - url: e.event_url, - date: (new Date(e.start_time * _sec)).toLocaleString(), - contents: '', - // author: '', - }; - - if (e.event_image) { - event.contents += `<center><img src="${e.event_image[0] == '/' ? 'https://sidequestvr.com':''}${e.event_image}" /></center>`; - } - - if (e.event_description) { - event.contents += e.event_description.split('\n').join('<br/>'); - } - - data.events.push(event); - } - } - - } - } - catch (err) { - console.error('appInfoEvents', { args, data }, err); - } - - return { success: true, data }; -} - -async function checkMount(attempt = 0) { - console.log('checkMount()', attempt); - try { - attempt++; - - if (!(await fsp.readdir(global.mountFolder)).length && attempt < 15) { - return new Promise((res, rej) => { - setTimeout(() => { - checkMount(attempt).then(res).catch(rej); - }, _sec); - }); - } - - const resp = await fetch('http://127.0.0.1:5572/rc/noop', { - method: 'post', - }); - - global.mounted = resp.ok; - console.log('checkMount', global.mounted); - return global.mounted; - //setTimeout(updateRcloneProgress, 2000); - } - catch (e) { - console.warn('checkMount', e); - global.mounted = false; - return false; - } -} - -async function checkDeps(arg){ - console.log('checkDeps()', arg); - let res = { - [arg]: { - version: false, - cmd: false, - error: false, - } - }; - - try { - if (arg == 'adb') { - let globalAdb = false; - try { - globalAdb = await commandExists('adb'); - } catch (e) {} - - res[arg].cmd = adbCmd = globalAdb ? 'adb' : (await fetchBinary('adb')); - try { - await execShellCommand(`"${res[arg].cmd}" start-server`); - } - catch (err) { - if (!err.toString().includes('daemon started successfully')) - throw err; - } - - res[arg].version = 'adbkit v.' + (await adb.version()) + '\n' + await execShellCommand(`"${res[arg].cmd}" version`); - - await trackDevices(); - } - - if (arg == 'rclone') { - // module with autodownload https://github.com/sntran/rclone.js/blob/main/index.js - // res.rclone.cmd = global.currentConfiguration.rclonePath || await commandExists('rclone'); - res[arg].cmd = await fetchBinary('rclone'); - res[arg].version = await execShellCommand(`"${res[arg].cmd}" --version`); - } - - if (arg == 'zip') { - res[arg].cmd = await fetchBinary('7za'); - res[arg].version = await execShellCommand(`"${res[arg].cmd}" --help ${grep_cmd} "7-Zip"`); - console.log(res[arg].version); - } - - if (arg == 'scrcpy') { - res[arg].cmd = global.currentConfiguration.scrcpyPath || await commandExists('scrcpy'); - try { - res[arg].version = await execShellCommand(`"${res[arg].cmd}" --version`); - } - catch(err) { - res[arg].version = err; // don`t know why version at std_err(( - } - } - } - catch (e) { - console.error('checkDeps', arg, e); - res[arg].error = e && e.toString(); - } - - res.success = true; - return res; -} - -async function fetchBinary(bin) { - const cfgKey = `${bin}Path`; - const cmd = global.currentConfiguration[cfgKey]; - if (cmd) return cmd; - - const file = global.platform == 'win' ? `${bin}.exe` : bin; - - const binPath = path.join(sidenoderHome, file); - const branch = /*bin == 'rclone' ? 'new' :*/ 'master'; - const binUrl = `https://raw.githubusercontent.com/vKolerts/${bin}-bin/${branch}/${global.platform}/${global.arch}/${file}`; - await fetchFile(binUrl, binPath); - - if (bin == 'adb' && global.platform == 'win') { - const libFile = 'AdbWinApi.dll'; - const libUrl = `https://raw.githubusercontent.com/vKolerts/${bin}-bin/master/${global.platform}/${global.arch}/${libFile}`; - await fetchFile(libUrl, path.join(sidenoderHome, libFile)); - - const usbLibFile = 'AdbWinUsbApi.dll'; - const usbLibUrl = `https://raw.githubusercontent.com/vKolerts/${bin}-bin/master/${global.platform}/${global.arch}/${usbLibFile}`; - await fetchFile(usbLibUrl, path.join(sidenoderHome, usbLibFile)); - } - - return changeConfig(cfgKey, binPath); -} - -async function fetchFile(url, dest) { - console.log('fetchFile', { url, dest }); - const resp = await fetch(url); - if (!resp.ok) throw new Error(`Can't download '${url}': ${resp.statusText}`); - - if (await fsp.exists(dest)) await fsp.unlink(dest); - return fsp.writeFile(dest, await resp.buffer(), { mode: 0o755 }); -} - -function returnError(message) { - console.log('returnError()'); - global.win.loadURL(`file://${__dirname}/views/error.twig`) - global.twig.view = { - message: message, - } -} - - -async function killRClone() { - RCLONE_ID++; - const killCmd = platform == 'win' - ? `taskkill.exe /F /T /IM rclone.exe` - : `killall -9 rclone`; - console.log('try kill rclone'); - return new Promise((res, rej) => { - exec(killCmd, (error, stdout, stderr) => { - if (error) { - console.log(killCmd, 'error:', error); - return rej(error); - } - - if (stderr) { - console.log(killCmd, 'stderr:', stderr); - return rej(stderr); - } - - console.log(killCmd, 'stdout:', stdout); - return res(stdout); - }); - }) -} - -async function parseRcloneSections(newCfg = false) { - console.warn('parseRcloneSections', newCfg); - if (!global.currentConfiguration.rclonePath) { - return console.error('rclone binary not defined'); - } - - if (!global.currentConfiguration.rcloneConf) { - return console.error('rclone config not defined'); - } - - try { - const rcloneCmd = global.currentConfiguration.rclonePath; - const out = await execShellCommand(`"${rcloneCmd}" --config="${global.currentConfiguration.rcloneConf}" listremotes`); - if (!out) { - return console.error('rclone config is empty', global.currentConfiguration.rcloneConf, out); - } - - const sections = out.split('\n').map(section => section.replace(/:$/, '')); - if (sections.length) sections.pop(); - if (!sections.length) { - return console.error('rclone config sections not found', global.currentConfiguration.rcloneConf, { out, sections }); - } - - global.rcloneSections = sections; - } - catch (err) { - const cfg = await fsp.readFile(global.currentConfiguration.rcloneConf, 'utf8'); - - if (!cfg) return console.error('rclone config is empty', global.currentConfiguration.rcloneConf); - - const lines = cfg.split('\n'); - let sections = []; - for (const line of lines) { - if (line[0] != '[') continue; - const section = line.match(/\[(.*?)\]/)[1]; - sections.push(section); - } - - global.rcloneSections = sections; - } - - if (newCfg || !global.currentConfiguration.cfgSection) { - await changeConfig('cfgSection', global.rcloneSections[0]); - } - - // console.log({ sections: global.rcloneSections }); - return global.rcloneSections; -} - -async function umount() { - if (platform == 'win') { - if (!(await fsp.exists(global.mountFolder))) return; - - await fsp.rmdir(global.mountFolder, { recursive: true }); - return; - } - - await execShellCommand(`umount "${global.mountFolder}"`, true); - await execShellCommand(`fusermount -uz "${global.mountFolder}"`, true); - await fsp.mkdir(global.mountFolder, { recursive: true }); -} - -async function mount() { - if (!global.currentConfiguration.rclonePath || !global.currentConfiguration.rcloneConf) { - win.webContents.send('alert', 'Rclone not configured'); - } - - // if (await checkMount(13)) { - // return; - try { - await killRClone(); - } - catch (err) { - console.log('rclone not started'); - } - // } - - await umount(); - - if (global.mounted) { - return global.mounted = false; - } - - const myId = RCLONE_ID; - const mountCmd = global.currentConfiguration.mountCmd; - const rcloneCmd = global.currentConfiguration.rclonePath; - console.log('start rclone'); - exec(`"${rcloneCmd}" ${mountCmd} --read-only --rc --rc-no-auth --config="${global.currentConfiguration.rcloneConf}" ${global.currentConfiguration.cfgSection}: "${global.mountFolder}"`, (error, stdout, stderr) => { - if (error) { - console.error('rclone error:', error); - if (RCLONE_ID != myId) error = false; - console.log({ RCLONE_ID, myId }); - win.webContents.send('check_mount', { success: false, error }); - // checkMount(); - /*if (error.message.search('transport endpoint is not connected')) { - console.log('GEVONDE'); - }*/ - - return; - } - - if (stderr) { - console.log('rclone stderr:', stderr); - return; - } - - console.log('rclone stdout:', stdout); - }); -} - -function resetCache(folder) { - console.log('resetCache', folder); - const oculusGamesDir = path.join(global.mountFolder, global.currentConfiguration.mntGamePath).replace(/\\/g, '/'); - - if (folder == oculusGamesDir) { - cacheOculusGames = false; - return true; - } - - return false; -} - - -async function getDir(folder) { - const oculusGamesDir = path.join(global.mountFolder, global.currentConfiguration.mntGamePath).replace(/\\/g, '/'); - //console.log(folder, oculusGamesDir); - if ( - folder == oculusGamesDir - && global.currentConfiguration.cacheOculusGames - && cacheOculusGames - ) { - console.log('getDir return from cache', folder); - return cacheOculusGames; - } - - try { - const files = await fsp.readdir(folder/*, { withFileTypes: true }*/); - let gameList = {}; - let gameListName2Package = {}; - let installedApps = {} - let gameListName = false; - try { - // throw 'test'; - for (const name of GAME_LIST_NAMES) { - if (!fs.existsSync(path.join(folder, name))) continue; - // if (!files.includes(name)) continue; - gameListName = name; - break; - } - - if (gameListName) { - const list = (await fsp.readFile(path.join(folder, gameListName), 'utf8')).split('\n'); - let listVer; - if (!list.length) throw gameListName + ' is empty'; - - for (const line of list) { - const meta = line.split(';'); - if (!listVer) { - listVer = meta[2] == 'Release APK Path' ? 1 : 2; - console.log({ gameListName, listVer }); - continue; - } - - if (listVer == 1) { - gameList[meta[1]] = { - simpleName: meta[0], - releaseName: meta[1], - packageName: meta[3], - versionCode: meta[4], - versionName: meta[5], - imagePath: `file://${global.tmpdir}/mnt/${global.currentConfiguration.mntGamePath}/.meta/thumbnails/${meta[3]}.jpg`, - } - } - else if (listVer == 2) { - gameList[meta[1]] = { - simpleName: meta[0], - releaseName: meta[1], - packageName: meta[2], - versionCode: meta[3], - imagePath: `file://${global.tmpdir}/mnt/${global.currentConfiguration.mntGamePath}/.meta/thumbnails/${meta[2]}.jpg`, - size: meta[5], - } - } - - } - } - } - catch (err) { - console.error(`${gameListName} failed`, err); - gameListName = { - err: `Can't parse GameList.txt - <br/>Maybe issue of server - please attempt to switch mirror at settings. - <br/>Actual mirrors posted there <a class="btn btn-sm btn-info" onclick=shell.openExternal('http://t.me/sidenoder')> http://t.me/sidenoder</a>`, - }; - } - - // console.log(gameList); - - try { - if (global.adbDevice) { - installedApps = await getInstalledApps(true); - } - } - catch (err) { - console.error('Can`t get installed apps', err); - } - - let fileNames = await Promise.all(files.map(async (fileName) => { - // console.log(fileName); - - const info = await fsp.lstat(path.join(folder, fileName)); - let steamId = false, - sqId = false, - oculusId = false, - imagePath = false, - versionCode = '', - versionName = '', - simpleName = fileName, - packageName = false, - note = '', - kmeta = false, - mp = false, - installed = 0, - size = false, - newItem = false; - - let isGameFolder = false; - - if (info.isDirectory()) { - const dirCont = await fsp.readdir(path.join(folder, fileName)); - - isGameFolder = - dirCont.filter((file) => { - return /.*\.apk/.test(file); - }).length > 0; - } - - let gameMeta = false; - - if (isGameFolder) { - gameMeta = gameList[fileName]; - - if (!gameMeta) { - // If gameMeta is still not defined then there is no game with a - // matching version number. We now query gameList using the game name - // without the version number. - let regex = /^([\w -.,!?&+™®'"]+) v\d+\+/; - if (regex.test(fileName)) { - // Only do this if this is a folder containing an apk file. - if (info.isDirectory()) { - const dirCont = await fsp.readdir(path.join(folder, fileName)); - const isGameFolder = - dirCont.filter((file) => { - return /.*\.apk/.test(file); - }).length > 0; - - if (isGameFolder) { - const match = fileName.match(regex)[1]; - gameMeta = gameList[match]; - } - } - } - } - } - - if (gameMeta) { - simpleName = gameMeta.simpleName; - packageName = gameMeta.packageName; - versionCode = gameMeta.versionCode; - versionName = gameMeta.versionName; - simpleName = gameMeta.simpleName; - size = gameMeta.size; - imagePath = gameMeta.imagePath; - - let regex = /\((.*?)\)/; - if (regex.test(gameMeta.releaseName)) { - const match = gameMeta.releaseName.match(regex)[0]; - note += match.replace(", only autoinstalls with Rookie", ""); - } - } - - // Include local notes. This allows users to add notes in brackets to - // their filenames to override the original note. These notes are rendered - // in the game's browse card. - let regex = /\((.*?)\)/; - if (regex.test(fileName)) { - const match = fileName.match(/\((.*?)\)/)[0]; - if (match !== note) { - // This note differs from the original so it has been overriden in the - // filename, so we replace it. - note = match.replace(", only autoinstalls with Rookie", ""); - } - } - - regex = /^([\w -.,!?&+™®'"]+) v\d+\+/; - if (gameListName && !packageName && regex.test(fileName)) { - simpleName = fileName.match(regex)[1]; - packageName = KMETAS2[escString(simpleName)]; - } - - regex = /v(\d+)\+/; - if (!versionCode && regex.test(fileName)) { - versionCode = fileName.match(regex)[1]; - } - - regex = /v\d+\+([\w.]*) /; - if (!versionName && regex.test(fileName)) { - versionName = fileName.match(regex)[1]; - } - - if (!versionCode && (new RegExp('.*\ -versionCode-')).test(fileName)) { - versionCode = fileName.match(/-versionCode-([0-9]*)/)[1]; - } - - if (!packageName && (new RegExp('.*\ -packageName-')).test(fileName)) { - packageName = fileName.match(/-packageName-([a-zA-Z0-9.]*)/)[1]; - } - - // obbs path the same =( - if (gameListName && !packageName && KMETAS[fileName]) { - packageName = fileName; - } - - - if (packageName) { - if (!imagePath) { - if (QUEST_ICONS.includes(packageName + '.jpg')) { - imagePath = `https://raw.githubusercontent.com/vKolerts/quest_icons/master/250/${packageName}.jpg`; - } else if (!imagePath) { - imagePath = 'unknown.png'; - } - } - - kmeta = KMETAS[packageName]; - installedApp = installedApps[packageName]; - if (installedApp) { - installed = 1; - if (versionCode && versionCode > installedApp.versionCode) { - installed++; - } - } - } - - if (gameListName && !packageName && versionCode) { - packageName = 'can`t parse package name'; - imagePath = 'unknown.png'; - } - - if (kmeta) { - steamId = !!(kmeta.steam && kmeta.steam.id); - oculusId = !!(kmeta.oculus && kmeta.oculus.id); - sqId = !!(kmeta.sq && kmeta.sq.id); - simpleName = simpleName || kmeta.simpleName; - mp = kmeta.mp || !!kmeta.mp; - } - else { - newItem = true; - } - - simpleName = await cleanUpFoldername(simpleName); - const isFile = info.isFile() - || (info.isSymbolicLink() && fileName.includes('.')); // not well - - return { - name: fileName, - simpleName, - isFile, - isLink: info.isSymbolicLink(), - steamId, - sqId, - oculusId, - imagePath, - versionCode, - versionName, - packageName, - size, - note, - newItem, - info, - mp, - installed, - createdAt: new Date(info.mtimeMs), - filePath: path.join(folder, fileName).replace(/\\/g, '/'), - }; - })); - // console.log({ fileNames }); - - const sortFileMode = global.currentConfiguration.sortFiles || "name"; - const sortByName = sortFileMode.startsWith("name"); - const asc = !sortFileMode.endsWith("-desc"); - fileNames - .sort((a, b) => { - const valA = sortByName ? a.simpleName.toLowerCase() : a.info.mtimeMs; - const valB = sortByName ? b.simpleName.toLowerCase() : b.info.mtimeMs; - - if (valA < valB) { - return asc ? -1 : 1; - } - if (valA > valB) { - return asc ? 1 : -1; - } - return 0; - }) - .sort((a, b) => { - if (a.isFile && !b.isFile) { - return 1; - } - if (!a.isFile && b.isFile) { - return -1; - } - return 0; - }); - // console.log(fileNames) - - if ( - folder == oculusGamesDir - && global.currentConfiguration.cacheOculusGames - ) { - console.log('getDir cached', folder); - cacheOculusGames = fileNames; - } - - if (gameListName && gameListName.err) { - fileNames.unshift({name: gameListName.err}); - } - - return fileNames; - } - catch (error) { - console.error('Can`t open folder ' + folder, error); - //returnError(e.message) - return false; - } -} - -async function cleanUpFoldername(simpleName) { - // simpleName = simpleName.split('-packageName-')[0]; - simpleName = simpleName.split('-versionCode-')[0]; - simpleName = simpleName.split(/ v[0-9]/)[0]; - return simpleName; -} - -async function getDirListing(folder){ - const files = await fsp.readdir(folder); - let fileNames = await Promise.all(files.map(async (file) => { - return path.join(folder, file).replace(/\\/g, '/') - })); - - return fileNames; -} - - -async function backupApp({ location, pkg }) { - console.log('backupApp()', pkg, location); - let apk = await adbShell(`pm path ${pkg}`); - apk = apk.replace("package:", ""); - - let folderName = pkg; - - for (const app of global.installedApps) { - if (app['packageName'] != pkg) continue; - folderName = `${app['simpleName']} -versionCode-${app['versionCode']} -packageName-${pkg}`; - break; - } - - location = path.join(location, folderName); - console.log({ location, apk }); - - await fsp.mkdir(location, { recursive: true }); - await adbPull(apk, path.join(location, 'base.apk')); - const obbsPath = `/sdcard/Android/obb/${pkg}`; - if (!(await adbFileExists(obbsPath))) return true; - - await adbPullFolder(obbsPath, path.join(location, pkg)); - - return true; -} - -const backupPrefsPath = '/sdcard/Download/backup/data/data'; -async function backupAppData(packageName, backupPath = path.join(global.sidenoderHome, 'backup_data')) { - console.log('backupAppData()', packageName); - backupPath = path.join(backupPath, packageName); - if (await adbFileExists(`/sdcard/Android/data/${packageName}`)) { - await adbPullFolder(`/sdcard/Android/data/${packageName}`, path.join(backupPath, 'Android', packageName)); - } - else { - console.log(`skip backup Android/data/${packageName}`); - } - - await copyAppPrefs(packageName); - await adbPullFolder(`${backupPrefsPath}/${packageName}`, path.join(backupPath, 'data', packageName)); - await adbShell(`rm -r "${backupPrefsPath}/${packageName}"`); - - fsp.writeFile(`${backupPath}/time.txt`, Date.now()); - return true; -} - -async function restoreAppData(packageName, backupPath = path.join(global.sidenoderHome, 'backup_data')) { - console.log('restoreAppData()', packageName); - backupPath = path.join(backupPath, packageName); - if (!(await fsp.exists(backupPath))) throw `Backup not found ${backupPath}`; - - await adbPushFolder(path.join(backupPath, 'Android', packageName), `/sdcard/Android/data/${packageName}`); - await adbPushFolder(path.join(backupPath, 'data', packageName), `${backupPrefsPath}/${packageName}`); - await restoreAppPrefs(packageName); - return true; -} - -async function copyAppPrefs(packageName, removeAfter = false) { - const cmd = removeAfter ? 'mv -f' : 'cp -rf'; - await adbShell(`mkdir -p "${backupPrefsPath}"`); - return adbShell(`run-as ${packageName} ${cmd} "/data/data/${packageName}" "${backupPrefsPath}/"`); -} - -async function restoreAppPrefs(packageName, removeAfter = true) { - const cmd = removeAfter ? 'mv -f' : 'cp -rf'; - const backup_path = `${backupPrefsPath}/${packageName}`; - if (!(await adbFileExists(backup_path))) return; - - return adbShell(`run-as ${packageName} ${cmd} "${backup_path}" "/data/data/"`); -} - -async function sideloadFolder(arg) { - location = arg.path; - console.log('sideloadFolder()', arg); - let res = { - device: 'done', - aapt: false, - check: false, - backup: false, - uninstall: false, - restore: false, - download: false, - apk: false, - download_obb: false, - push_obb: false, - done: false, - update: false, - error: '', - location, - } - - win.webContents.send('sideload_process', res); - - if (location.endsWith('.apk')) { - apkfile = location; - location = path.dirname(location); - } - else { - returnError('not an apk file'); - return; - } - - console.log('start sideload: ' + apkfile); - - fromremote = false; - if (location.includes(global.mountFolder)) { - fromremote = true; - } - - console.log('fromremote:', fromremote); - - packageName = ''; - let apktmp = ''; - try { - if (!fromremote) { - res.download = 'skip'; - } - else { - res.download = 'processing'; - win.webContents.send('sideload_process', res); - - apktmp = path.join(global.tmpdir, path.basename(apkfile)); - console.log('is remote, copying to '+ apktmp) - - if (await fsp.exists(apktmp)) { - console.log('is remote, ' + apktmp + 'already exists, using'); - res.download = 'skip'; - } - else { - const tmpname = `${apktmp}.part`; - if (await fsp.exists(tmpname)) await fsp.unlink(tmpname); - await fsp.copyFile(apkfile, tmpname); - await fsp.rename(tmpname, apktmp); - res.download = 'done'; - } - - apkfile = apktmp; - } - } - catch (e) { - // returnError(e); - console.error(e); - res.download = 'fail'; - res.done = 'fail'; - res.error = e; - return win.webContents.send('sideload_process', res); - } - - res.aapt = 'processing'; - win.webContents.send('sideload_process', res); - - try { - packageinfo = await getPackageInfo(apkfile); - - packageName = packageinfo.packageName; - console.log({ apkfile, packageinfo, packageName }); - } - catch (e) { - // returnError(e); - console.error(e); - res.aapt = 'fail'; - res.done = 'fail'; - res.error = e; - return win.webContents.send('sideload_process', res); - } - - if (!packageName) { - const e = `Can't parse packageName of ${apkfile}`; - // returnError(new Error(e)); - console.error(e); - res.aapt = 'fail'; - res.done = 'fail'; - res.error = e; - return win.webContents.send('sideload_process', res); - } - - - res.aapt = 'done'; - res.check = 'processing'; - win.webContents.send('sideload_process', res); - - console.log('checking if installed'); - let installed = false; - try { - installed = await adb.getDevice(global.adbDevice).isInstalled(packageName); - res.check = 'done'; - } - catch (err) { - console.error('check', e); - res.check = 'fail'; - res.error = e; - // TODO: maybe return; - } - - res.backup = 'processing'; - win.webContents.send('sideload_process', res); - // const backup_path = `${global.tmpdir}/sidenoder_restore_backup/`; - const backup_path = '/sdcard/Download/backup/Android/data/'; - - // TODO: if adbExist - if (installed) { - console.log('doing adb pull appdata (ignore error)'); - try { - await adbShell(`mkdir -p "${backup_path}"`); - await adbShell(`mv "/sdcard/Android/data/${packageName}" "${backup_path}"`); - await copyAppPrefs(packageName); - // await backupAppData(packageName, backup_path); - res.backup = 'done'; - } - catch (e) { - console.error('backup', e); - res.backup = 'fail'; - res.error = e; - // TODO: maybe return; - } - } - else { - res.backup = 'skip'; - } - - res.uninstall = 'processing'; - win.webContents.send('sideload_process', res); - - if (installed) { - console.log('doing adb uninstall (ignore error)'); - try { - await adb.getDevice(global.adbDevice).uninstall(packageName); - res.uninstall = 'done'; - console.log('uninstall done', packageName); - } - catch (e) { - console.error('uninstall', e); - res.uninstall = 'fail'; - res.error = e; - } - } - else { - res.uninstall = 'skip'; - } - - console.log('doing adb install'); - res.apk = 'processing'; - win.webContents.send('sideload_process', res); - - try { - await adbInstall(apkfile); - console.log('apk done', packageName); - res.apk = 'done'; - } - catch (e) { - // returnError(e); - console.error(e); - res.apk = 'fail'; - res.done = 'fail'; - res.error = e; - return win.webContents.send('sideload_process', res); - } - - res.restore = 'processing'; - win.webContents.send('sideload_process', res); - - if (/*installed || */await adbFileExists(`${backup_path}${packageName}`)) { - console.log('doing adb push appdata (ignore error)'); - try { - // await restoreAppData(packageName, backup_path); - await adbShell(`mv "${backup_path}${packageName}" "/sdcard/Android/data/"`); - await restoreAppPrefs(packageName); - res.restore = 'done'; - console.log('restore done', packageName); - } - catch (e) { - console.error('restore', e); - res.restore = 'fail'; - res.error = e; - // TODO: maybe return; - } - } - else { - res.restore = 'skip'; - } - - res.remove_obb = 'processing'; - win.webContents.send('sideload_process', res); - - const obbFolderOrig = path.join(location, packageName); - console.log({ obbFolderOrig }); - try { - if (!(await fsp.exists(obbFolderOrig))) throw 'Can`t find obbs folder'; - obbFolderDest = `/sdcard/Android/obb/${packageName}`; - console.log('DATAFOLDER to copy:' + obbFolderDest); - } - catch (error) { - console.log(error); - obbFolderDest = false; - res.remove_obb = 'skip'; - res.download_obb = 'skip'; - res.push_obb = 'skip'; - win.webContents.send('sideload_process', res); - } - - let obbFiles = []; - if (!obbFolderDest) { - res.download_obb = 'skip'; - res.push_obb = 'skip'; - } - else { - console.log('doing obb rm'); - try { - await adbShell(`rm -r "${obbFolderDest}"`); - await adbShell(`mkdir -p ${obbFolderDest}`, global.adbDevice, true) - res.remove_obb = 'done'; - console.log('remove_obb done', packageName); - } - catch (e) { - res.remove_obb = 'skip'; - console.log(e); - } - - res.download_obb = 'processing'; - win.webContents.send('sideload_process', res); - - try { - obbFiles = await fsp.readdir(obbFolderOrig); - console.log('obbFiles: ', obbFiles.length); - - res.download_obb = (fromremote ? '0' : obbFiles.length) + '/' + obbFiles.length; - res.push_obb = '0/' + obbFiles.length; - win.webContents.send('sideload_process', res); - - const tmpFolder = path.join(global.tmpdir, packageName); - if (fromremote) { - await fsp.mkdir(tmpFolder, { recursive: true }); - } - - for (const obbName of obbFiles) { - const obb = path.join(obbFolderOrig, obbName); - console.log('obb File: ' + obbName); - console.log('doing obb push'); - const destFile = `${obbFolderDest}/${obbName}`; - - if (fromremote) { - const obbtmp = path.join(tmpFolder, obbName); - console.log('obb is remote, copying to ' + obbtmp); - - if (await fsp.exists(obbtmp)) { - console.log(`obb is remote, ${obbtmp} already exists, using`); - } - else { - const tmpname = `${obbtmp}.part`; - if (await fsp.exists(tmpname)) await fsp.unlink(tmpname); - await fsp.copyFile(obb, tmpname); - await fsp.rename(tmpname, obbtmp); - } - - res.download_obb = (+res.download_obb.split('/')[0] + 1) + '/' + obbFiles.length; - win.webContents.send('sideload_process', res); - await adbPush(obbtmp, `${destFile}`, false); - } - else { - await adbShell(`mkdir -p ${obbFolderDest}`, global.adbDevice, true) - await adbPush(obb, `${destFile}`, false); - } - - res.push_obb = (+res.push_obb.split('/')[0] + 1) + '/' + obbFiles.length; - win.webContents.send('sideload_process', res); - } - - if (fromremote) { - //TODO: check settings - await fsp.rm(tmpFolder, { recursive: true }); - } - } - catch (e) { - console.error('obbs processing', e); - if (fromremote) { - res.download_obb = 'fail'; - } - - res.push_obb = 'fail'; - res.done = 'fail'; - res.error = e; - return win.webContents.send('sideload_process', res); - } - } - - if (fromremote) { - //TODO: check settings - await fsp.unlink(apktmp); - } - - res.done = 'done'; - res.update = arg.update; - win.webContents.send('sideload_process', res); - console.log('DONE'); - return; -} - - - -async function getPackageInfo(apkPath) { - const reader = await ApkReader.open(apkPath); - const manifest = await reader.readManifest(); - - info = { - packageName: manifest.package, - versionCode: manifest.versionCode, - versionName: manifest.versionName, - }; - - return info; -} - -async function getInstalledApps(obj = false) { - let apps = await adbShell(`pm list packages -3 --show-versioncode`); - apps = apps.split('\n'); - // apps.pop(); - appinfo = {}; - - for (const appLine of apps) { - const [packageName, versionCode] = appLine.slice(8).split(' versionCode:'); - - const info = []; - info['simpleName'] = KMETAS[packageName] && KMETAS[packageName].simpleName || packageName; - info['packageName'] = packageName; - info['versionCode'] = versionCode; - info['imagePath'] = QUEST_ICONS.includes(packageName + '.jpg') - ? `https://raw.githubusercontent.com/vKolerts/quest_icons/master/250/${packageName}.jpg` - : `http://cdn.apk-cloud.com/detail/image/${packageName}-w130.png`//'unknown.png'; - - appinfo[packageName] = info; - } - - - const sortAppMode = global.currentConfiguration.sortApps || "simplename"; - const sortByName = sortAppMode.startsWith("simplename"); - const asc = !sortAppMode.endsWith("-desc"); - global.installedApps = Object.values(appinfo).sort((a, b) => { - const valA = (sortByName ? a.simpleName : a.packageName).toLowerCase(); - const valB = (sortByName ? b.simpleName : b.packageName).toLowerCase(); - if (valA < valB) { - return asc ? -1 : 1; - } - if (valA > valB) { - return asc ? 1 : -1; - } - return 0; - }); - - return obj ? appinfo : global.installedApps; -} - -async function getInstalledAppsWithUpdates() { - const remotePath = path.join(global.mountFolder, global.currentConfiguration.mntGamePath); // TODO: folder path to config - const list = await getDir(remotePath); - let remotePackages = {}; - let remoteList = {}; - - if (list) for (const app of list) { - const { name, packageName, versionCode, simpleName, filePath, size } = app; - if (!packageName) continue; - - if (!remotePackages[packageName]) remotePackages[packageName] = []; - remotePackages[packageName].push(name); - - remoteList[name] = { - versionCode, - simpleName, - filePath, - size, - }; - }; - - const remoteKeys = Object.keys(remotePackages); - - const apps = global.installedApps || await getInstalledApps(); - let updates = []; - for (const app of apps) { - const packageName = app['packageName']; - // console.log(packageName, 'checking'); - - if (!remoteKeys.includes(packageName)) continue; - - for (name of remotePackages[packageName]) { - const pkg = remoteList[name]; - const installedVersion = app['versionCode']; - const remoteversion = pkg.versionCode; - - // console.log({ packageName, installedVersion, remoteversion }); - // console.log({ pkg }); - - if (remoteversion <= installedVersion) continue; - - app['simpleName'] = pkg.simpleName; - app['update'] = []; - app['update']['path'] = pkg.filePath; - app['update']['size'] = pkg.size; - app['update']['versionCode'] = remoteversion; - updates.push(app); - - console.log(packageName, 'UPDATE AVAILABLE'); - } - } - - global.installedApps = false; - return updates; -} - - -async function detectNoteTxt(files, folder) { - // TODO: check .meta/notes - - if (typeof files == 'string') { - folder = files; - files = false; - } - - if (!files) { - files = await fsp.readdir(folder); - } - - if (files.includes('notes.txt')) { - return fsp.readFile(path.join(folder, 'notes.txt'), 'utf8'); - } - - return false; -} - -async function detectInstallTxt(files, folder) { - if (typeof files == 'string') { - folder = files; - files = false; - } - - if (!files) { - files = await fsp.readdir(folder); - } - - const installTxNames = [ - 'install.txt', - 'Install.txt', - ]; - - for (const name of installTxNames) { - if (files.includes(name)) { - return fsp.readFile(path.join(folder, name), 'utf8'); - } - } - - return false; -} - - -async function getApkFromFolder(folder){ - let res = { - path: false, - install_desc: false, - } - - const files = await fsp.readdir(folder); - res.install_desc = await detectInstallTxt(files, folder); - res.notes = await detectNoteTxt(files, folder); - console.log({ files }); - - for (file of files) { - if (file.endsWith('.apk')) { - res.path = path.join(folder, file).replace(/\\/g, "/"); - return res; - } - } - - returnError('No apk found in ' + folder); - return res; -} - -async function uninstall(packageName){ - resp = await adb.getDevice(global.adbDevice).uninstall(packageName); -} - - -let rcloneProgress = false; -async function updateRcloneProgress() { - try { - const response = await fetch('http://127.0.0.1:5572/core/stats', { method: 'POST' }) - const data = await response.json(); - if (!data.transferring || !data.transferring[0]) throw 'no data'; - const transferring = data.transferring[0]; - rcloneProgress = { - cmd: 'download', - bytes: transferring.bytes, - size: transferring.size, - percentage: transferring.percentage, - speedAvg: transferring.speedAvg, - eta: transferring.eta, - name: transferring.name, - }; - //console.log('sending rclone data'); - win.webContents.send('process_data', rcloneProgress); - } - catch (error) { - //console.error('Fetch-Error:', error); - if (rcloneProgress) { - rcloneProgress = false; - win.webContents.send('process_data', rcloneProgress); - } - } - - setTimeout(updateRcloneProgress, 2000); -} - -async function init() { - fsp.exists = (p) => fsp.access(p).then(() => true).catch(e => false); - - await initLogs(); - - console.log({ platform, arch, version, sidenoderHome }, process.platform, process.arch, process.argv); - - await loadMeta(); -} - -async function loadMeta() { - try { - const res = await fetch('https://raw.githubusercontent.com/vKolerts/quest_icons/master/version?' + Date.now()); - version = await res.text(); - if (version == META_VERSION) return setTimeout(loadMeta, CHECK_META_PERIOD); - - META_VERSION = version; - console.log('Meta version', META_VERSION); - } - catch (err) { - console.error('can`t get meta version', err); - } - - try { - const res = await fetch('https://raw.githubusercontent.com/vKolerts/quest_icons/master/list.json?' + Date.now()); - QUEST_ICONS = await res.json(); - console.log('icons list loaded'); - } - catch (err) { - console.error('can`t get quest_icons', err); - } - - try { - const res = await fetch('https://raw.githubusercontent.com/vKolerts/quest_icons/master/.e?' + Date.now()); - const text = await res.text(); - const iv = Buffer.from(text.substring(0, l), 'hex'); - const secret = crypto.createHash(hash_alg).update(pkg.author.split(' ')[0].repeat(2)).digest('base64').substr(0, l); - const decipher = crypto.createDecipheriv('aes-256-cbc', secret, iv); - const encrypted = text.substring(l); - KMETAS = JSON.parse(decipher.update(encrypted, 'base64', 'utf8') + decipher.final('utf8')); - for (const pkg of Object.keys(KMETAS)) { - KMETAS2[escString(KMETAS[pkg].simpleName)] = pkg; - } - - console.log('kmetas loaded'); - } - catch (err) { - console.error('can`t get kmetas', err); - } - - setTimeout(loadMeta, CHECK_META_PERIOD); -} - -function escString(val) { - let res = val.toLowerCase(); - res = res.replace(/[-\_:.,!?\"'&™®| ]/g, ''); - return res; -} - -async function initLogs() { - const log_path = path.join(sidenoderHome, 'debug_last.log'); - if (await fsp.exists(log_path)) { - await fsp.unlink(log_path); - } - else { - await fsp.mkdir(sidenoderHome, { recursive: true }); - } - - const log_file = fs.createWriteStream(log_path, { flags: 'w' }); - const log_stdout = process.stdout; - - function dateF() { - const d = new Date(); - return `[${d.toLocaleString()}.${d.getMilliseconds()}] `; - } - - console.log = function(...d) { - let line = ''; - let line_color = ''; - for (const l of d) { - if (typeof l == 'string') { - line += l + ' '; - line_color += l + ' '; - continue; - } - - const formated = util.format(l); - line += formated + ' '; - line_color += '\x1b[32m' + formated + '\x1b[0m '; - } - - log_stdout.write(dateF() + line_color + '\n'); - log_file.write(dateF() + line + '\n'); - }; - - console.error = function(...d) { - let line = ''; - for (const l of d) { - line += util.format(l) + ' '; - } - - log_stdout.write(`\x1b[31m${dateF()}ERROR: ` + line + '\x1b[0m\n'); - log_file.write(dateF()+ 'ERROR: ' + line + '\n'); - }; - - console.warning = function(...d) { - let line = ''; - for (const l of d) { - line += util.format(l) + ' '; - } - - log_stdout.write(`\x1b[33m${dateF()}WARN: ` + line + '\x1b[0m\n'); - log_file.write(dateF() + 'WARN: ' + line + '\n'); - }; -} - -async function fetchTimeout(url = '', options = {}, timeout = 20 * 1000) { - const controller = new AbortController(); - options.signal = controller.signal; - setTimeout(() => { - controller.abort() - }, timeout); - - return fetch(url, options); -} - - - -async function saveConfig(config = global.currentConfiguration) { - await fsp.writeFile(configLocation, JSON.stringify(config, null, ' ')); -} - -async function reloadConfig() { - const defaultConfig = { - allowOtherDevices: false, - cacheOculusGames: true, - autoMount: false, - adbPath: '', - rclonePath: '', - rcloneConf: '', - mountCmd: 'mount', - cfgSection: '', - snapshotsDelete: true, - mntGamePath: 'Quest Games', - scrcpyBitrate: '5', - scrcpyCrop: '1600:900:2017:510', - lastIp: '', - userHide: false, - dirBookmarks: [ - { name: 'Sidenoder folder', path: global.sidenoderHome }, - ], - - proxyUrl: '', - proxyOculus: false, - proxySteam: false, - proxySQ: false, - }; - - if (await fsp.exists(configLocationOld)) { - await fsp.rename(configLocationOld, configLocation); - } - - - if (await fsp.exists(configLocation)) { - console.log('Config exist, using ' + configLocation); - global.currentConfiguration = Object.assign(defaultConfig, require(configLocation)); - } - else { - console.log('Config doesnt exist, creating ') + configLocation; - await saveConfig(defaultConfig); - global.currentConfiguration = defaultConfig; - } - - if (global.currentConfiguration.tmpdir) { - global.tmpdir = global.currentConfiguration.tmpdir; - } - - if (!global.currentConfiguration.dirBookmarks) { - global.currentConfiguration.dirBookmarks = defaultConfig.dirBookmarks; - } - - proxySettings() - - await parseRcloneSections(); -} - -function proxySettings(proxyUrl = global.currentConfiguration.proxyUrl) { - const { proxyOculus, proxySteam, proxySQ } = global.currentConfiguration; - - agentOculus = proxyUrl && proxyOculus ? new SocksProxyAgent(proxyUrl) : undefined; - agentSteam = proxyUrl && proxySteam ? new SocksProxyAgent(proxyUrl) : undefined; - agentSQ = proxyUrl && proxySQ ? new SocksProxyAgent(proxyUrl) : undefined; -} - -async function changeConfig(key, value) { - console.log('cfg.update', key, value); - if (key == 'proxyUrl') proxySettings(value); - if (['proxyOculus', 'proxySteam', 'proxySQ'].includes(key)) proxySettings(); - - global.currentConfiguration[key] = value; - await saveConfig(); - - if (key == 'rcloneConf') await parseRcloneSections(true); - if (key == 'tmpdir') global.tmpdir = value || require('os').tmpdir().replace(/\\/g, '/'); - - return value; -} +const exec = require("child_process").exec; +// const fs = require('fs'); +const fs = require("fs"); +const fsp = fs.promises; +const util = require("util"); +const path = require("path"); +const crypto = require("crypto"); +const commandExists = require("command-exists"); +const { dialog } = require("electron"); +const ApkReader = require("adbkit-apkreader"); +const adbkit = require("@devicefarmer/adbkit").default; +const adb = adbkit.createClient(); +const fetch = (...args) => + import("node-fetch").then(({ default: fetch }) => fetch(...args)); +const WAE = require("web-auto-extractor").default; +// const HttpProxyAgent = require('https-proxy-agent'); // TODO add https proxy support +const { SocksProxyAgent } = require("socks-proxy-agent"); +const url = require("url"); +// const ApkReader = require('node-apk-parser'); +const fixPath = (...args) => + import("fix-path").then(({ default: fixPath }) => fixPath(...args)); +// adb.kill(); + +const pkg = require("./package.json"); +const _sec = 1000; +const _min = 60 * _sec; + +let CHECK_META_PERIOD = 2 * _min; +const l = 32; +const configLocationOld = path.join(global.homedir, "sidenoder-config.json"); +const configLocation = path.join(global.sidenoderHome, "config.json"); + +let agentOculus, agentSteam, agentSQ; + +init(); + +const GAME_LIST_NAMES = global.currentConfiguration.gameListNames || [ + "FFA.txt", + "GameList.txt", + "VRP-GameList.txt/VRP-GameList.txt", + "VRP-GameList.txt", + "Dynamic.txt", +]; +let META_VERSION = []; +let QUEST_ICONS = []; +let cacheOculusGames = false; +let KMETAS = {}; +let KMETAS2 = {}; + +let adbCmd = "adb"; +let grep_cmd = "| grep "; +if (platform == "win") { + grep_cmd = "| findstr "; +} + +let RCLONE_ID = 0; + +module.exports = { + //properties + resetCache, + //methods + getDeviceSync, + trackDevices, + checkDeps, + checkMount, + mount, + killRClone, + getDir, + returnError, + sideloadFolder, + getInstalledApps, + getInstalledAppsWithUpdates, + getApkFromFolder, + uninstall, + getDirListing, + getPackageInfo, + wifiGetStat, + wifiEnable, + connectWireless, + disconnectWireless, + enableMTP, + startSCRCPY, + rebootDevice, + rebootRecovery, + rebootBootloader, + sideloadFile, + getLaunchActivity, + getActivities, + startActivity, + devOpenUrl, + checkAppTools, + changeAppConfig, + backupApp, + backupAppData, + restoreAppData, + getDeviceInfo, + getStorageInfo, + getUserInfo, + getFwInfo, + getBatteryInfo, + changeConfig, + reloadConfig, + execShellCommand, + updateRcloneProgress, + deviceTweaksGet, + deviceTweaksSet, + appInfo, + appInfoEvents, + isIdle, + wakeUp, + detectInstallTxt, + detectNoteTxt, + // ... +}; + +async function getDeviceInfo() { + if (!global.adbDevice) { + return { + success: false, + }; + } + // console.log('getDeviceInfo()'); + + const storage = await getStorageInfo(); + const user = await getUserInfo(); + const fw = await getFwInfo(); + const battery = await getBatteryInfo(); + const ip = await getDeviceIp(); + const wifi = await wifiGetStat(); + + const res = { + success: !!storage, + storage, + user, + fw, + battery, + ip, + wifi, + }; + + // console.log('getDeviceInfo', res); + return res; +} + +async function getFwInfo() { + console.log("getFwInfo()"); + const res = await adbShell("getprop ro.build.branch"); + if (!res) return false; + + return { + version: res.replace("releases-oculus-", ""), + }; +} + +async function getBatteryInfo() { + console.log("getBatteryInfo()"); + const res = await adbShell("dumpsys battery"); + if (!res) return false; + + return parceOutOptions(res); +} + +async function getUserInfo() { + if (global.currentConfiguration.userHide) + return { + name: "<i>hidden</i>", + }; + + console.log("getUserInfo()"); + const res = await adbShell("dumpsys user | grep UserInfo"); + if (!res) return false; + + return { + name: res.split(":")[1], + }; +} + +async function deviceTweaksGet(arg) { + console.log("deviceTweaksGet()", arg); + let res = { + cmd: "get", + // mp_name: '', + // guardian_pause: '0', + // frc: '0', + // gRR: '72', + // gCA: '-1', + // gFFR: '2', + // CPU: '2', + // GPU: '2', + // vres: '1024', + // cres: '640x480', + // gSSO: '1440x1584', + }; + + if (arg.key === "mp_name") + res.mp_name = await adbShell("settings get global username"); + if (arg.key === "guardian_pause") + res.guardian_pause = await adbShell("getprop debug.oculus.guardian_pause"); + if (arg.key === "frc") + res.frc = await adbShell("getprop debug.oculus.fullRateCapture"); + if (arg.key === "gRR") + res.gRR = await adbShell("getprop debug.oculus.refreshRate"); + if (arg.key === "gCA") + res.gCA = await adbShell("getprop debug.oculus.forceChroma"); + if (arg.key === "gFFR") + res.gFFR = await adbShell("getprop debug.oculus.foveation.level"); + if (arg.key === "CPU") + res.CPU = await adbShell("getprop debug.oculus.cpuLevel"); + if (arg.key === "GPU") + res.GPU = await adbShell("getprop debug.oculus.gpuLevel"); + if (arg.key === "vres") + res.vres = await adbShell("getprop debug.oculus.videoResolution"); + if (arg.key === "cres") { + let captureDims = + (await adbShell("getprop debug.oculus.capture.width")) + + "x" + + (await adbShell("getprop debug.oculus.capture.height")); + + // Default when not set + if (captureDims === "x") { + captureDims = "3840x1920"; + } + res.cres = captureDims; + } + if (arg.key === "gSSO") + res.gSSO = + (await adbShell("getprop debug.oculus.textureWidth")) + + "x" + + (await adbShell("getprop debug.oculus.textureHeight")); + //oculus.capture.bitrate + + return res; +} + +async function deviceTweaksSet(arg) { + console.log("deviceTweaksSet()", arg); + let res = { cmd: "set" }; + if (typeof arg.mp_name != "undefined") { + res.mp_name = await adbShell("settings put global username " + arg.mp_name); + } + + if (typeof arg.guardian_pause != "undefined") { + res.guardian_pause = await adbShell( + "setprop debug.oculus.guardian_pause " + (arg.guardian_pause ? "1" : "0"), + ); + } + if (typeof arg.frc != "undefined") { + res.frc = await adbShell( + "setprop debug.oculus.fullRateCapture " + (arg.frc ? "1" : "0"), + ); + } + + if (typeof arg.gRR != "undefined") { + res.gRR = await adbShell("setprop debug.oculus.refreshRate " + arg.gRR); + } + + if (typeof arg.gCA != "undefined") { + res.gCA = await adbShell("setprop debug.oculus.forceChroma " + arg.gCA); + } + + if (typeof arg.gFFR != "undefined") { + res.gFFR = await adbShell( + "setprop debug.oculus.foveation.level " + arg.gFFR, + ); + } + + if (typeof arg.CPU != "undefined") { + res.CPU = await adbShell("setprop debug.oculus.cpuLevel " + arg.CPU); + } + + if (typeof arg.GPU != "undefined") { + res.GPU = await adbShell("setprop debug.oculus.gpuLevel " + arg.GPU); + } + + if (typeof arg.vres != "undefined") { + res.vres = await adbShell( + "setprop debug.oculus.videoResolution " + arg.vres, + ); + } + + if (typeof arg.cres != "undefined") { + const [width, height] = arg.cres.split("x"); + await adbShell("setprop debug.oculus.capture.width " + width); + res.cres = await adbShell("setprop debug.oculus.capture.height " + height); + } + + if (typeof arg.gSSO != "undefined") { + const [width, height] = arg.gSSO.split("x"); + await adbShell("setprop debug.oculus.textureWidth " + width); + await adbShell("setprop debug.oculus.textureHeight " + height); + res.gSSO = await adbShell( + "settings put system font_scale 0.85 && settings put system font_scale 1.0", + ); + } + + return res; +} + +async function getStorageInfo() { + console.log("getStorageInfo()"); + + const linematch = await adbShell('df -h | grep "/storage/emulated"'); + if (!linematch) return false; + + const refree = new RegExp("([0-9(.{1})]+[a-zA-Z%])", "g"); + const storage = linematch.match(refree); + console.log(storage); + + if (storage.length == 3) { + return { + size: storage[0], + used: storage[1], + free: 0, + percent: storage[2], + }; + } + + return { + size: storage[0], + used: storage[1], + free: storage[2], + percent: storage[3], + }; +} + +async function getLaunchActivity(pkg) { + console.log("startApp()", pkg); + const activity = await adbShell( + `dumpsys package ${pkg} | grep -A 1 'filter' | head -n 1 | cut -d ' ' -f 10`, + ); + return startActivity(activity); +} + +async function getActivities(pkg, activity = false) { + console.log("getActivities()", pkg); + + let activities = await adbShell( + `dumpsys package | grep -Eo '^[[:space:]]+[0-9a-f]+[[:space:]]+${pkg}/[^[:space:]]+' | grep -oE '[^[:space:]]+$'`, + ); + if (!activities) return false; + + activities = activities.split("\n"); + // activities.pop(); + console.log({ pkg, activities }); + // TODO: check manifest.application.launcherActivities + + return activities; +} + +async function startActivity(activity) { + console.log("startActivity()", activity); + wakeUp(); + const result = await adbShell(`am start ${activity}`); // TODO activity selection + + console.log("startActivity", activity, result); + return result; +} + +async function devOpenUrl(url) { + console.log("devOpenUrl", url); + wakeUp(); + const result = await adbShell( + `am start -a android.intent.action.VIEW -d "${url}"`, + ); // TODO activity selection + + console.log("devOpenUrl", url, result); + return result; +} + +async function readAppCfg(pkg) { + let config = await adbShell( + `cat /sdcard/Android/data/${pkg}/private/config.json 1>&1 2> /dev/null`, + ); + try { + config = config && JSON.parse(config); + } catch (e) { + console.error("readAppCfg", e); + config = false; + } + + return config; +} + +async function checkAppTools(pkg) { + const backupPath = path.join(global.sidenoderHome, "backup_data", pkg); + const availableBackup = await adbFileExists(`/sdcard/Android/data/${pkg}`); + let availableRestore = false; + let availableConfig = false; + if (await fsp.exists(backupPath)) { + try { + availableRestore = await fsp.readFile(`${backupPath}/time.txt`, "utf8"); + } catch (err) { + availableRestore = 1; + } + } + + if (availableBackup) { + availableConfig = await readAppCfg(pkg); + } + + return { + success: true, + pkg, + availableRestore, + availableConfig, + }; +} + +async function changeAppConfig(pkg, key, val) { + console.log("changeAppConfig()", { pkg, key, val }); + const res = { + pkg, + key, + val, + success: false, + }; + + let config = await readAppCfg(pkg); + try { + config = Object.assign(config, { [key]: val }); + adbShell( + `echo '${JSON.stringify(config)}' > "/sdcard/Android/data/${pkg}/private/config.json"`, + ); + config = await readAppCfg(pkg); + res.val = config && config[key]; + res.success = !!config; + } catch (e) { + console.error("changeAppConfig", res, e); + } + + return res; +} + +// Implementation ---------------------------------- + +async function getDeviceIp() { + // let ip = await adb.getDHCPIpAddress(global.adbDevice); + // if (ip) return ip; + if (!global.adbDevice && global.currentConfiguration.lastIp) { + return global.currentConfiguration.lastIp; + } + + let ip = await adbShell( + `ip -o route get to 8.8.8.8 | sed -n 's/.*src \\([0-9.]\\+\\).*/\\1/p'`, + ); + console.log({ ip }); + if (ip) return ip; + + ip = await adbShell( + `ip addr show wlan0 | grep 'inet ' | cut -d ' ' -f 6 | cut -d / -f 1`, + ); + console.log({ ip }); + if (ip) return ip; + return false; +} + +async function wifiGetStat() { + const on = await adbShell("settings get global wifi_on"); + return on && +on; +} + +async function wifiEnable(enable) { + return adbShell(`svc wifi ${enable ? "enable" : "disable"}`); +} + +async function connectWireless() { + const on = await adbShell("settings get global wifi_on"); + if (!(await wifiGetStat())) { + console.error("connectWireless", "wifi disabled"); + await wifiEnable(true); + return false; + } + + // await adbShell(`setprop service.adb.tcp.port 5555`); + // TODO: save ip & try use it + const ip = await getDeviceIp(); + console.log({ ip }); + if (!ip) return false; + + try { + if (global.adbDevice) { + const device = adb.getDevice(global.adbDevice); + const port = await device.tcpip(); + await device.waitForDevice(); + console.log("set tcpip", port); + await changeConfig("lastIp", ip); + } + + const deviceTCP = await adb.connect(ip, 5555); + // await deviceTCP.waitForDevice(); + console.log("connectWireless", { ip, res: deviceTCP }); + + return ip; + } catch (err) { + console.error("connectWireless", err); + await changeConfig("lastIp", ""); + return false; + } +} + +async function disconnectWireless() { + const ip = await getDeviceIp(); + if (!ip) return false; + + try { + const res = await adb.disconnect(ip, 5555); + // const res = await adb.usb(global.adbDevice); + console.log("disconnectWireless", { ip, res }); + // await changeConfig('lastIp', ''); + // await getDeviceSync(); + return res; + } catch (err) { + console.error("disconnectWireless.error", err); + return !(await isWireless()); + } +} + +async function isWireless() { + try { + const devices = await adb.listDevices(); + for (const device of devices) { + if (!device.id.includes(":5555")) continue; + if (["offline", "authorizing"].includes(device.type)) continue; + if (["unauthorized"].includes(device.type)) { + win.webContents.send( + "alert", + "Please authorize adb access on your device", + ); + continue; + } + + console.log("device.id", device.type); + return device.id; + } + + return false; + } catch (err) { + console.error("Something went wrong:", err.stack); + return false; + } +} + +async function enableMTP() { + const res = await adbShell(`svc usb setFunctions mtp`); + console.log("enableMTP", { res }); + return res; +} + +async function isIdle() { + const res = await adbShell(`dumpsys deviceidle | grep mScreenOn`); + console.log(res, res.includes("true")); + return !res.includes("true"); +} + +async function wakeUp() { + if (!(await isIdle())) return; + return adbShell(`input keyevent KEYCODE_POWER`); +} + +async function startSCRCPY() { + console.log("startSCRCPY()"); + if ( + !global.currentConfiguration.scrcpyPath && + !(await commandExists("scrcpy")) + ) { + returnError("Can`t find scrcpy binary"); + return; + } + + const scrcpyCmd = + `"${global.currentConfiguration.scrcpyPath || "scrcpy"}" ` + + (global.currentConfiguration.scrcpyCrop + ? `--crop ${global.currentConfiguration.scrcpyCrop} ` + : "") + + `-b ${global.currentConfiguration.scrcpyBitrate || 1}M ` + + (global.currentConfiguration.scrcpyFps + ? `--max-fps ${global.currentConfiguration.scrcpyFps} ` + : "") + + (global.currentConfiguration.scrcpySize + ? `--max-size ${global.currentConfiguration.scrcpySize} ` + : "") + + (!global.currentConfiguration.scrcpyWindow ? "-f " : "") + + (global.currentConfiguration.scrcpyOnTop ? "--always-on-top " : "") + + (!global.currentConfiguration.scrcpyControl ? "-n " : "") + + '--window-title "SideNoder Stream" ' + + `-s ${global.adbDevice} `; + console.log({ scrcpyCmd }); + wakeUp(); + exec(scrcpyCmd, (error, stdout, stderr) => { + if (error) { + console.error("scrcpy error:", error); + win.webContents.send("cmd_sended", { success: error }); + return; + } + + if (stderr) { + console.error("scrcpy stderr:", stderr); + // win.webContents.send('cmd_sended', { success: stderr }); + return; + } + + console.log("scrcpy stdout:", stdout); + }); + + return scrcpyCmd; +} + +async function rebootDevice() { + const res = await adbShell(`reboot`); + console.log("rebootDevice", { res }); + return res; +} +async function rebootRecovery() { + const res = await adbShell(`reboot recovery`); + console.log("rebootRecovery", { res }); + return res; +} +async function rebootBootloader() { + const res = await adbShell(`reboot bootloader`); + console.log("rebootBootloader", { res }); + return res; +} +async function sideloadFile(path) { + const res = await execShellCommand(`"${adbCmd}" sideload "${path}"`); + console.log("sideloadFile", { res }); + return res; +} + +async function getDeviceSync(attempt = 0) { + try { + // const lastDevice = global.adbDevice; + const devices = await adb.listDevices(); + console.log({ devices }); + global.adbDevice = false; + for (const device of devices) { + if (["offline", "authorizing"].includes(device.type)) continue; + if (["unauthorized"].includes(device.type)) { + win.webContents.send( + "alert", + "Please authorize adb access on your device", + ); + continue; + } + + if ( + !global.currentConfiguration.allowOtherDevices && + (await adbShell("getprop ro.product.brand", device.id)) != "oculus" + ) + continue; + + global.adbDevice = device.id; + } + + /*if (!global.adbDevice && devices.length > 0 && attempt < 1) { + return setTimeout(()=> getDeviceSync(attempt + 1), 1000); + }*/ + // if (lastDevice == global.adbDevice) return; + + win.webContents.send("check_device", { success: global.adbDevice }); + + return global.adbDevice; + } catch (err) { + console.error("Something went wrong:", err.stack); + } +} + +/** + * Executes a shell command and return it as a Promise. + * @param cmd {string} + * @return {Promise<string>} + */ +async function adbShell(cmd, deviceId = global.adbDevice, skipRead = false) { + try { + if (!deviceId) { + throw "device not defined"; + } + + global.adbError = null; + const r = await adb.getDevice(deviceId).shell(cmd); + // console.timeLog(cmd); + if (skipRead) { + console.log(`adbShell[${deviceId}]`, { cmd, skipRead }); + return true; + } + + let output = await adbkit.util.readAll(r); + output = await output.toString(); + // output = output.split('\n'); + // const end = output.pop(); + // if (end != '') output.push(); + console.log(`adbShell[${deviceId}]`, { cmd, output }); + if (output.substr(-1) == "\n") return output.slice(0, -1); + return output; + } catch (err) { + console.error(`adbShell[${deviceId}]: err`, { cmd }, err); + global.adbError = err; + if (err.toString() == `FailError: Failure: 'device offline'`) { + getDeviceSync(); + } + + return false; + } +} + +function parceOutOptions(line) { + let opts = {}; + for (let l of line.split("\n")) { + l = l.split(" ").join(""); + let [k, v] = l.split(":"); + + if (v == "true") v = true; + if (v == "false") v = false; + if (!isNaN(+v)) v = +v; + + opts[k] = v; + } + + return opts; +} + +// on empty dirrectory return false +async function adbFileExists(path) { + const r = await adbShell(`ls "${path}" 1>&1 2> /dev/null`); + return r; +} + +async function adbPull(orig, dest, sync = false) { + console.log("adbPull", orig, dest); + const transfer = sync + ? await sync.pull(orig) + : await adb.getDevice(global.adbDevice).pull(orig); + return new Promise(function (resolve, reject) { + let c = 0; + transfer.on("progress", (stats) => { + c++; + if (c % 40 != 1) return; // skip 20 events + + // console.log(orig + ' pulled', stats); + const res = { + cmd: "pull", + bytes: stats.bytesTransferred, + size: 0, + percentage: 0, + speedAvg: 0, + eta: 0, + name: orig, + }; + win.webContents.send("process_data", res); + }); + transfer.on("end", () => { + console.log(orig, "pull complete"); + win.webContents.send("process_data", false); + resolve(true); + }); + transfer.on("error", (err) => { + console.error("adb_pull_stderr", err); + win.webContents.send("process_data", false); + reject(err); + }); + transfer.pipe(fs.createWriteStream(dest)); + }); +} + +async function adbPullFolder(orig, dest, sync = false) { + console.log("pullFolder", orig, dest); + /*let need_close = false; + if (!sync) { + need_close = true; + sync = await adb.getDevice(global.adbDevice).syncService(); + }*/ + + let actions = []; + await fsp.mkdir(dest, { recursive: true }); + const files = sync + ? await sync.readdir(orig) + : await adb.getDevice(global.adbDevice).readdir(orig); + + for (const file of files) { + const new_orig = `${orig}/${file.name}`; + const new_dest = path.join(dest, file.name); + if (file.isFile()) { + actions.push(adbPull(new_orig, new_dest, sync)); // file.size + continue; + } + + actions.push(adbPullFolder(new_orig, new_dest, sync)); + } + + await Promise.all(actions); + + // if (need_close) sync.end(); + + return true; +} + +async function adbPush(orig, dest, sync = false) { + console.log("adbPush", orig, dest); + const transfer = sync + ? await sync.pushFile(orig, dest) + : await adb.getDevice(global.adbDevice).push(orig, dest); + const stats = await fsp.lstat(orig); + const size = stats.size; + + return new Promise(function (resolve, reject) { + let c = 0; + transfer.on("progress", (stats) => { + c++; + if (c % 40 != 1) return; // skip 20 events + + // console.log(orig + ' pushed', stats); + const res = { + cmd: "push", + bytes: stats.bytesTransferred, + size, + percentage: ((stats.bytesTransferred * 100) / size).toFixed(2), + speedAvg: 0, + eta: 0, + name: orig, + }; + win.webContents.send("process_data", res); + }); + transfer.on("end", () => { + console.log(orig, "push complete"); + win.webContents.send("process_data", false); + resolve(true); + }); + transfer.on("error", (err) => { + console.error("adb_push_stderr", err); + win.webContents.send("process_data", false); + reject(err); + }); + }); +} + +async function adbPushFolder(orig, dest, sync = false) { + console.log("pushFolder", orig, dest); + + const stat = await fsp.lstat(orig); + console.log({ orig, stat }, stat.isFile()); + if (stat.isFile()) return adbPush(orig, dest); + + /*let need_close = false; + if (!sync) { + need_close = true; + sync = await adb.getDevice(global.adbDevice).syncService(); + }*/ + + let actions = []; + await adbShell(`mkdir -p ${dest}`, global.adbDevice, true); + const files = await fsp.readdir(orig, { withFileTypes: true }); + for (const file of files) { + const new_orig = path.join(orig, file.name); + const new_dest = `${dest}/${file.name}`; + if (file.isFile()) { + actions.push(adbPush(new_orig, new_dest, sync)); + continue; + } + + actions.push(adbPushFolder(new_orig, new_dest, sync)); + } + + await Promise.all(actions); + + // if (need_close) sync.end(); + + return true; +} + +async function adbInstall(apk) { + console.log("adbInstall", apk); + const temp_path = "/data/local/tmp/install.apk"; + + await adbPush(apk, temp_path, false, false); + try { + await adb.getDevice(global.adbDevice).installRemote(temp_path); + } catch (err) { + adbShell(`rm ${temp_path}`); + throw err; + } + + return true; +} + +function execShellCommand(cmd, ignoreError = false, buffer = 100) { + console.log({ cmd }); + return new Promise((resolve, reject) => { + exec(cmd, { maxBuffer: 1024 * buffer }, (error, stdout, stderr) => { + if (error) { + if (ignoreError) return resolve(false); + console.error("exec_error", cmd, error); + return reject(error); + } + + if (stdout || !stderr) { + console.log("exec_stdout", cmd, stdout); + return resolve(stdout); + } else { + if (ignoreError) return resolve(false); + console.error("exec_stderr", cmd, stderr); + return reject(stderr); + } + }); + }); +} + +async function trackDevices() { + console.log("trackDevices()"); + await getDeviceSync(); + + try { + const tracker = await adb.trackDevices(); + tracker.on("add", async (device) => { + console.log("Device was plugged in", device.id); + // await getDeviceSync(); + }); + + tracker.on("remove", async (device) => { + console.log("Device was unplugged", device.id); + // await getDeviceSync(); + }); + + tracker.on("change", async (device) => { + // TODO: // need fix double run + console.log("Device was changed", device.id); + await getDeviceSync(); + }); + + tracker.on("end", () => { + console.error("Tracking stopped"); + trackDevices(); + }); + } catch (err) { + console.error("Something went wrong:", err.stack); + returnError(err); + } +} + +async function appInfo(args) { + const { res, pkg } = args; + const app = KMETAS[pkg]; + + let data = { + res, + pkg, + id: 0, + name: app.simpleName, + short_description: "", + detailed_description: "", + about_the_game: "", + supported_languages: "", + genres: [], + header_image: "", + screenshots: [], + url: "", + }; + + try { + if (res == "steam") { + const steam = app && app.steam; + if (!steam || !steam.id) throw "incorrect args"; + + data.id = steam.id; + data.url = `https://store.steampowered.com/app/${data.id}/`; + + const resp = await fetchTimeout( + `https://store.steampowered.com/api/appdetails?appids=${data.id}`, + { + headers: { + "Accept-Language": global.locale + ",en-US;q=0.5,en;q=0.3", + }, + agent: agentSteam, + }, + ); + const json = await resp.json(); + // console.log({ json }); + + Object.assign(data, json[data.id].data); + } + + if (res == "oculus") { + const oculus = app && app.oculus; + if (!oculus || !oculus.id) throw "incorrect args"; + // console.log({ oculus }); + + data.id = oculus.id; + data.url = `https://www.oculus.com/experiences/quest/${data.id}`; + // data.genres = oculus.genres && oculus.genres.split(', '); + + //https://computerelite.github.io + let resp = await fetchTimeout( + `https://graph.oculus.com/graphql?forced_locale=${global.locale}`, + { + method: "POST", + body: `access_token=OC|1317831034909742|&variables={"itemId":"${oculus.id}","first":1}&doc_id=5373392672732392`, + headers: { + "Accept-Language": global.locale + ",en-US;q=0.5,en;q=0.3", + "Content-Type": "application/x-www-form-urlencoded", + Origin: "https://www.oculus.com", + }, + agent: agentOculus, + }, + ); + try { + let json = await resp.json(); + // console.log('json', json); + if (json.error) throw json.error; + + const meta = json.data.node; + if (!meta) throw "empty json.data.node"; + + data.name = meta.appName; + data.detailed_description = + meta.display_long_description && + meta.display_long_description.split("\n").join("<br/>"); + data.genres = meta.genre_names; + + if (meta.supported_in_app_languages) { + data.supported_languages = meta.supported_in_app_languages + .map(({ name }) => name) + .join(", "); + } + //meta.internet_connection_name, + //meta.quality_rating_aggregate, + //meta.release_date, + + if (meta.website_page_meta) { + data.header_image = meta.website_page_meta.image_url; + data.short_description = + meta.website_page_meta.description && + meta.website_page_meta.description.split("\n").join("<br/>"); + data.url = meta.website_page_meta.page_url; + } + + if (meta.screenshots) { + for (const { uri } of meta.screenshots) { + data.screenshots.push({ + // id, + path_thumbnail: uri, + }); + } + } + + if (meta.trailer && meta.trailer.uri) { + data.movies = [{ mp4: { 480: meta.trailer.uri } }]; + } + } catch (err) { + console.error(res, "fetch error", err); + + resp = await fetchTimeout(`${data.url}?locale=${global.locale}`, { + agent: agentOculus, + }); + const meta = await WAE().parse(await resp.text()); + const { metatags } = meta; + // console.log('meta', meta); + + data.name = metatags["og:title"][0].replace(" on Oculus Quest", ""); + data.header_image = metatags["og:image"][0]; + data.short_description = + metatags["og:description"][0] && + metatags["og:description"][0].split("\n").join("<br/>"); + data.url = metatags["al:web:url"][0]; + + const jsonld = + (meta.jsonld.Product && meta.jsonld.Product[0]) || + JSON.parse(metatags["json-ld"][0]); + // console.log(jsonld); + + if (jsonld) { + if (jsonld.name) data.name = jsonld.name; + data.detailed_description = + jsonld.description && jsonld.description.split("\n").join("<br/>"); + + if (jsonld.image) { + for (const id in jsonld.image) { + if (["0", "1", "2"].includes(id)) continue; // skip resizes of header + + data.screenshots.push({ + id, + path_thumbnail: jsonld.image[id], + }); + } + } + } + } + } + + if (res == "sq") { + const sq = app && app.sq; + if (!sq || !sq.id) throw "incorrect args"; + // console.log({ sq }); + + data.id = sq.id; + data.url = `https://sidequestvr.com/app/${data.id}/`; + + const resp = await fetchTimeout(`https://api.sidequestvr.com/get-app`, { + method: "POST", + body: JSON.stringify({ apps_id: data.id }), + headers: { + "Accept-Language": global.locale + ",en-US;q=0.5,en;q=0.3", + "Content-Type": "application/json", + Origin: "https://sidequestvr.com", + Cookie: + " __stripe_mid=829427af-c8dd-47d1-a857-1dc73c95b947201218; cf_clearance=LkOSetFAXEs255r2rAMVK_hm_I0lawkUfJAedj1nkD0-1633288577-0-250; __stripe_sid=6e94bd6b-19a4-4c34-98d5-1dc46423dd2e2f3688", + "User-Agent": + "Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0", + }, + agent: agentSQ, + }); + const json = await resp.json(); + const meta = json.data[0]; + data.name = meta.name; + data.header_image = meta.image_url; + data.short_description = meta.summary; + data.detailed_description = meta.description.split("\n").join("<br/>"); + if (meta.video_url) + data.youtube = [ + meta.video_url + .replace("youtube.com", "youtube.com/embed") + .replace("youtu.be", "youtube.com/embed") + .replace("/embed/embed", "/embed") + .replace("/watch?v=", "/"), + ]; + + const resp_img = await fetchTimeout( + `https://api.sidequestvr.com/get-app-screenshots`, + { + method: "POST", + body: JSON.stringify({ apps_id: data.id }), + headers: { + "Content-Type": "application/json", + Origin: "https://sidequestvr.com", + Cookie: + " __stripe_mid=829427af-c8dd-47d1-a857-1dc73c95b947201218; cf_clearance=LkOSetFAXEs255r2rAMVK_hm_I0lawkUfJAedj1nkD0-1633288577-0-250; __stripe_sid=6e94bd6b-19a4-4c34-98d5-1dc46423dd2e2f3688", + "User-Agent": + "Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0", + }, + agent: agentSQ, + }, + ); + const json_img = await resp_img.json(); + for (const id in json_img.data) { + data.screenshots.push({ + id, + path_thumbnail: json_img.data[id].image_url, + }); + } + } + } catch (err) { + console.error("appInfo", { args, data }, err); + } + + return { success: true, data }; +} + +async function appInfoEvents(args) { + const { res, pkg } = args; + const app = KMETAS[pkg]; + let data = { + res, + pkg, + events: [], + }; + + try { + if (res == "steam") { + const steam = app && app.steam; + if (!steam || !steam.id) throw "incorrect args"; + + data.url = `https://store.steampowered.com/news/app/${steam.id}/`; + + const resp = await fetchTimeout( + `http://api.steampowered.com/ISteamNews/GetNewsForApp/v0002?appid=${steam.id}`, + { + headers: { + "Accept-Language": global.locale + ",en-US;q=0.5,en;q=0.3", + }, + agent: agentSteam, + }, + ); + const json = await resp.json(); + // console.log({ json }); + + const events = json.appnews.newsitems; + for (const e of events) { + const event = { + title: e.title, + url: e.url, + date: new Date(e.date * _sec).toLocaleString(), + // author: e.author, + }; + + event.contents = e.contents + .split("\n") + .join("<br/>") + .split("[img]") + .join('<center><img src="') + .split("[/img]") + .join('" /></center>') + .split("{STEAM_CLAN_IMAGE}") + .join( + "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/clans", + ) + .split("[list]") + .join("<ul>") + .split("[/list]") + .join("</ul>") + .split("[*]") + .join("</li><li>") + // .split('[b]').join('<b>') + // .split('[/b]').join('</b>') + // .split('[i]').join('<i>') + // .split('[/i]').join('</i>') + .split("[") + .join("<") + .split("]") + .join(">"); + data.events.push(event); + } + } + + if (res == "oculus") { + const oculus = app && app.oculus; + if (!oculus || !oculus.id) throw "incorrect args"; + + // data.url = `https://store.steampowered.com/news/app/${steam.id}/`; + + let resp = await fetchTimeout( + `https://graph.oculus.com/graphql?forced_locale=${global.locale}`, + { + method: "POST", + body: `access_token=OC|1317831034909742|&variables={"id":"${oculus.id}"}&doc_id=1586217024733717`, + headers: { + "Accept-Language": global.locale + ",en-US;q=0.5,en;q=0.3", + "Content-Type": "application/x-www-form-urlencoded", + Origin: "https://www.oculus.com", + }, + agent: agentOculus, + }, + ); + try { + let json = await resp.json(); + if (json.error) throw json.error; + + // console.log({ json }); + const events = json.data.node.supportedBinaries.edges; + for (const { node } of events) { + const e = node; + const event = { + id: e.id, + title: `${e.version} (versionCode: ${e.versionCode})`, + contents: e.changeLog && e.changeLog.split("\n").join("<br/>"), + // richChangeLog: e.richChangeLog, + // url: '', + // date: '', + // author: '', + }; + + if (e.richChangeLog) console.log("RICHCHANGELOG", e.richChangeLog); + + data.events.push(event); + } + } catch (err) { + console.error(res, "fetch error", err); + } + + resp = await fetch( + `https://computerelite.github.io/tools/Oculus/OlderAppVersions/${oculus.id}.json`, + ); + json = await resp.json(); + // console.log({ json }); + const events = json.data.node.binaries.edges; + for (const { node } of events) { + const e = node; + let found = false; + for (const i in data.events) { + if (data.events[i].id != e.id) continue; + + data.events[i].date = new Date( + e.created_date * _sec, + ).toLocaleString(); + found = true; + break; + } + if (found) continue; + + const event = { + id: e.id, + title: `${e.version} (versionCode: ${e.version_code})`, + date: new Date(e.created_date * _sec).toLocaleString(), + contents: e.change_log.split("\n").join("<br/>"), + // url: '', + // author: '', + }; + + data.events.push(event); + } + } + + if (res == "sq") { + const sq = app && app.sq; + if (!sq || !sq.id) throw "incorrect args"; + // console.log({ sq }); + + for (const is_news of [true, false]) { + const resp = await fetchTimeout( + `https://api.sidequestvr.com/events-list`, + { + method: "POST", + body: JSON.stringify({ apps_id: sq.id, is_news }), + headers: { + "Accept-Language": global.locale + ",en-US;q=0.5,en;q=0.3", + "Content-Type": "application/json", + Origin: "https://sidequestvr.com", + Cookie: + " __stripe_mid=829427af-c8dd-47d1-a857-1dc73c95b947201218; cf_clearance=LkOSetFAXEs255r2rAMVK_hm_I0lawkUfJAedj1nkD0-1633288577-0-250; __stripe_sid=6e94bd6b-19a4-4c34-98d5-1dc46423dd2e2f3688", + "User-Agent": + "Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0", + }, + agent: agentSQ, + }, + ); + const json = await resp.json(); + // console.log({ json }); + for (const e of json.data) { + const event = { + id: e.events_id, + title: e.event_name, + url: e.event_url, + date: new Date(e.start_time * _sec).toLocaleString(), + contents: "", + // author: '', + }; + + if (e.event_image) { + event.contents += `<center><img src="${e.event_image[0] == "/" ? "https://sidequestvr.com" : ""}${e.event_image}" /></center>`; + } + + if (e.event_description) { + event.contents += e.event_description.split("\n").join("<br/>"); + } + + data.events.push(event); + } + } + } + } catch (err) { + console.error("appInfoEvents", { args, data }, err); + } + + return { success: true, data }; +} + +async function checkMount(attempt = 0) { + console.log("checkMount()", attempt); + try { + attempt++; + + if (!(await fsp.readdir(global.mountFolder)).length && attempt < 15) { + return new Promise((res, rej) => { + setTimeout(() => { + checkMount(attempt).then(res).catch(rej); + }, _sec); + }); + } + + const resp = await fetch("http://127.0.0.1:5572/rc/noop", { + method: "post", + }); + + global.mounted = resp.ok; + console.log("checkMount", global.mounted); + return global.mounted; + //setTimeout(updateRcloneProgress, 2000); + } catch (e) { + console.warn("checkMount", e); + global.mounted = false; + return false; + } +} + +async function checkDeps(arg) { + console.log("checkDeps()", arg); + let res = { + [arg]: { + version: false, + cmd: false, + error: false, + }, + }; + + try { + if (arg == "adb") { + let globalAdb = false; + try { + globalAdb = await commandExists("adb"); + } catch (e) {} + + res[arg].cmd = adbCmd = globalAdb ? "adb" : await fetchBinary("adb"); + try { + await execShellCommand(`"${res[arg].cmd}" start-server`); + } catch (err) { + if (!err.toString().includes("daemon started successfully")) throw err; + } + + res[arg].version = + "adbkit v." + + (await adb.version()) + + "\n" + + (await execShellCommand(`"${res[arg].cmd}" version`)); + + await trackDevices(); + } + + if (arg == "rclone") { + // module with autodownload https://github.com/sntran/rclone.js/blob/main/index.js + // res.rclone.cmd = global.currentConfiguration.rclonePath || await commandExists('rclone'); + res[arg].cmd = await fetchBinary("rclone"); + res[arg].version = await execShellCommand(`"${res[arg].cmd}" --version`); + } + + if (arg == "zip") { + res[arg].cmd = await fetchBinary("7za"); + res[arg].version = await execShellCommand( + `"${res[arg].cmd}" --help ${grep_cmd} "7-Zip"`, + ); + console.log(res[arg].version); + } + + if (arg == "scrcpy") { + res[arg].cmd = + global.currentConfiguration.scrcpyPath || + (await commandExists("scrcpy")); + try { + res[arg].version = await execShellCommand( + `"${res[arg].cmd}" --version`, + ); + } catch (err) { + res[arg].version = err; // don`t know why version at std_err(( + } + } + } catch (e) { + console.error("checkDeps", arg, e); + res[arg].error = e && e.toString(); + } + + res.success = true; + return res; +} + +async function fetchBinary(bin) { + const cfgKey = `${bin}Path`; + const cmd = global.currentConfiguration[cfgKey]; + if (cmd) return cmd; + + const file = global.platform == "win" ? `${bin}.exe` : bin; + + const binPath = path.join(sidenoderHome, file); + const branch = /*bin == 'rclone' ? 'new' :*/ "master"; + const binUrl = `https://raw.githubusercontent.com/vKolerts/${bin}-bin/${branch}/${global.platform}/${global.arch}/${file}`; + await fetchFile(binUrl, binPath); + + if (bin == "adb" && global.platform == "win") { + const libFile = "AdbWinApi.dll"; + const libUrl = `https://raw.githubusercontent.com/vKolerts/${bin}-bin/master/${global.platform}/${global.arch}/${libFile}`; + await fetchFile(libUrl, path.join(sidenoderHome, libFile)); + + const usbLibFile = "AdbWinUsbApi.dll"; + const usbLibUrl = `https://raw.githubusercontent.com/vKolerts/${bin}-bin/master/${global.platform}/${global.arch}/${usbLibFile}`; + await fetchFile(usbLibUrl, path.join(sidenoderHome, usbLibFile)); + } + + return changeConfig(cfgKey, binPath); +} + +async function fetchFile(url, dest) { + console.log("fetchFile", { url, dest }); + const resp = await fetch(url); + if (!resp.ok) throw new Error(`Can't download '${url}': ${resp.statusText}`); + + if (await fsp.exists(dest)) await fsp.unlink(dest); + return fsp.writeFile(dest, await resp.buffer(), { mode: 0o755 }); +} + +function returnError(message) { + console.log("returnError()"); + global.win.loadURL(`file://${__dirname}/views/error.twig`); + global.twig.view = { + message: message, + }; +} + +async function killRClone() { + RCLONE_ID++; + const killCmd = + platform == "win" + ? `taskkill.exe /F /T /IM rclone.exe` + : `killall -9 rclone`; + console.log("try kill rclone"); + return new Promise((res, rej) => { + exec(killCmd, (error, stdout, stderr) => { + if (error) { + console.log(killCmd, "error:", error); + return rej(error); + } + + if (stderr) { + console.log(killCmd, "stderr:", stderr); + return rej(stderr); + } + + console.log(killCmd, "stdout:", stdout); + return res(stdout); + }); + }); +} + +async function parseRcloneSections(newCfg = false) { + console.warn("parseRcloneSections", newCfg); + if (!global.currentConfiguration.rclonePath) { + return console.error("rclone binary not defined"); + } + + if (!global.currentConfiguration.rcloneConf) { + return console.error("rclone config not defined"); + } + + try { + const rcloneCmd = global.currentConfiguration.rclonePath; + const out = await execShellCommand( + `"${rcloneCmd}" --config="${global.currentConfiguration.rcloneConf}" listremotes`, + ); + if (!out) { + return console.error( + "rclone config is empty", + global.currentConfiguration.rcloneConf, + out, + ); + } + + const sections = out + .split("\n") + .map((section) => section.replace(/:$/, "")); + if (sections.length) sections.pop(); + if (!sections.length) { + return console.error( + "rclone config sections not found", + global.currentConfiguration.rcloneConf, + { out, sections }, + ); + } + + global.rcloneSections = sections; + } catch (err) { + const cfg = await fsp.readFile( + global.currentConfiguration.rcloneConf, + "utf8", + ); + + if (!cfg) + return console.error( + "rclone config is empty", + global.currentConfiguration.rcloneConf, + ); + + const lines = cfg.split("\n"); + let sections = []; + for (const line of lines) { + if (line[0] != "[") continue; + const section = line.match(/\[(.*?)\]/)[1]; + sections.push(section); + } + + global.rcloneSections = sections; + } + + if (newCfg || !global.currentConfiguration.cfgSection) { + await changeConfig("cfgSection", global.rcloneSections[0]); + } + + // console.log({ sections: global.rcloneSections }); + return global.rcloneSections; +} + +async function umount() { + if (platform == "win") { + if (!(await fsp.exists(global.mountFolder))) return; + + await fsp.rmdir(global.mountFolder, { recursive: true }); + return; + } + + await execShellCommand(`umount "${global.mountFolder}"`, true); + await execShellCommand(`fusermount -uz "${global.mountFolder}"`, true); + await fsp.mkdir(global.mountFolder, { recursive: true }); +} + +async function mount() { + if ( + !global.currentConfiguration.rclonePath || + !global.currentConfiguration.rcloneConf + ) { + win.webContents.send("alert", "Rclone not configured"); + } + + // if (await checkMount(13)) { + // return; + try { + await killRClone(); + } catch (err) { + console.log("rclone not started"); + } + // } + + await umount(); + + if (global.mounted) { + return (global.mounted = false); + } + + const myId = RCLONE_ID; + const mountCmd = global.currentConfiguration.mountCmd; + const rcloneCmd = global.currentConfiguration.rclonePath; + console.log("start rclone"); + exec( + `"${rcloneCmd}" ${mountCmd} --read-only --rc --rc-no-auth --config="${global.currentConfiguration.rcloneConf}" ${global.currentConfiguration.cfgSection}: "${global.mountFolder}"`, + (error, stdout, stderr) => { + if (error) { + console.error("rclone error:", error); + if (RCLONE_ID != myId) error = false; + console.log({ RCLONE_ID, myId }); + win.webContents.send("check_mount", { success: false, error }); + // checkMount(); + /*if (error.message.search('transport endpoint is not connected')) { + console.log('GEVONDE'); + }*/ + + return; + } + + if (stderr) { + console.log("rclone stderr:", stderr); + return; + } + + console.log("rclone stdout:", stdout); + }, + ); +} + +function resetCache(folder) { + console.log("resetCache", folder); + const oculusGamesDir = path + .join(global.mountFolder, global.currentConfiguration.mntGamePath) + .replace(/\\/g, "/"); + + if (folder == oculusGamesDir) { + cacheOculusGames = false; + return true; + } + + return false; +} + +async function getDir(folder) { + const oculusGamesDir = path + .join(global.mountFolder, global.currentConfiguration.mntGamePath) + .replace(/\\/g, "/"); + //console.log(folder, oculusGamesDir); + if ( + folder == oculusGamesDir && + global.currentConfiguration.cacheOculusGames && + cacheOculusGames + ) { + console.log("getDir return from cache", folder); + return cacheOculusGames; + } + + try { + const files = await fsp.readdir(folder /*, { withFileTypes: true }*/); + let gameList = {}; + let gameListName2Package = {}; + let installedApps = {}; + let gameListName = false; + try { + // throw 'test'; + for (const name of GAME_LIST_NAMES) { + if (!fs.existsSync(path.join(folder, name))) continue; + // if (!files.includes(name)) continue; + gameListName = name; + break; + } + + if (gameListName) { + const list = ( + await fsp.readFile(path.join(folder, gameListName), "utf8") + ).split("\n"); + let listVer; + if (!list.length) throw gameListName + " is empty"; + + for (const line of list) { + const meta = line.split(";"); + if (!listVer) { + listVer = meta[2] == "Release APK Path" ? 1 : 2; + console.log({ gameListName, listVer }); + continue; + } + + if (listVer == 1) { + gameList[meta[1]] = { + simpleName: meta[0], + releaseName: meta[1], + packageName: meta[3], + versionCode: meta[4], + versionName: meta[5], + imagePath: `file://${global.tmpdir}/mnt/${global.currentConfiguration.mntGamePath}/.meta/thumbnails/${meta[3]}.jpg`, + }; + } else if (listVer == 2) { + gameList[meta[1]] = { + simpleName: meta[0], + releaseName: meta[1], + packageName: meta[2], + versionCode: meta[3], + imagePath: `file://${global.tmpdir}/mnt/${global.currentConfiguration.mntGamePath}/.meta/thumbnails/${meta[2]}.jpg`, + size: meta[5], + }; + } + } + } + } catch (err) { + console.error(`${gameListName} failed`, err); + gameListName = { + err: `Can't parse GameList.txt + <br/>Maybe issue of server - please attempt to switch mirror at settings. + <br/>Actual mirrors posted there <a class="btn btn-sm btn-info" onclick=shell.openExternal('http://t.me/sidenoder')> http://t.me/sidenoder</a>`, + }; + } + + // console.log(gameList); + + try { + if (global.adbDevice) { + installedApps = await getInstalledApps(true); + } + } catch (err) { + console.error("Can`t get installed apps", err); + } + + let fileNames = await Promise.all( + files.map(async (fileName) => { + // console.log(fileName); + + const info = await fsp.lstat(path.join(folder, fileName)); + let steamId = false, + sqId = false, + oculusId = false, + imagePath = false, + versionCode = "", + versionName = "", + simpleName = fileName, + packageName = false, + note = "", + kmeta = false, + mp = false, + installed = 0, + size = false, + newItem = false; + + let isGameFolder = false; + + if (info.isDirectory()) { + const dirCont = await fsp.readdir(path.join(folder, fileName)); + + isGameFolder = + dirCont.filter((file) => { + return /.*\.apk/.test(file); + }).length > 0; + } + + let gameMeta = false; + + if (isGameFolder) { + gameMeta = gameList[fileName]; + + if (!gameMeta) { + // If gameMeta is still not defined then there is no game with a + // matching version number. We now query gameList using the game name + // without the version number. + let regex = /^([\w -.,!?&+™®'"]+) v\d+\+/; + if (regex.test(fileName)) { + // Only do this if this is a folder containing an apk file. + if (info.isDirectory()) { + const dirCont = await fsp.readdir(path.join(folder, fileName)); + const isGameFolder = + dirCont.filter((file) => { + return /.*\.apk/.test(file); + }).length > 0; + + if (isGameFolder) { + const match = fileName.match(regex)[1]; + gameMeta = gameList[match]; + } + } + } + } + } + + if (gameMeta) { + simpleName = gameMeta.simpleName; + packageName = gameMeta.packageName; + versionCode = gameMeta.versionCode; + versionName = gameMeta.versionName; + simpleName = gameMeta.simpleName; + size = gameMeta.size; + imagePath = gameMeta.imagePath; + + let regex = /\((.*?)\)/; + if (regex.test(gameMeta.releaseName)) { + const match = gameMeta.releaseName.match(regex)[0]; + note += match.replace(", only autoinstalls with Rookie", ""); + } + } + + // Include local notes. This allows users to add notes in brackets to + // their filenames to override the original note. These notes are rendered + // in the game's browse card. + let regex = /\((.*?)\)/; + if (regex.test(fileName)) { + const match = fileName.match(/\((.*?)\)/)[0]; + if (match !== note) { + // This note differs from the original so it has been overriden in the + // filename, so we replace it. + note = match.replace(", only autoinstalls with Rookie", ""); + } + } + + regex = /^([\w -.,!?&+™®'"]+) v\d+\+/; + if (gameListName && !packageName && regex.test(fileName)) { + simpleName = fileName.match(regex)[1]; + packageName = KMETAS2[escString(simpleName)]; + } + + regex = /v(\d+)\+/; + if (!versionCode && regex.test(fileName)) { + versionCode = fileName.match(regex)[1]; + } + + regex = /v\d+\+([\w.]*) /; + if (!versionName && regex.test(fileName)) { + versionName = fileName.match(regex)[1]; + } + + if (!versionCode && new RegExp(".* -versionCode-").test(fileName)) { + versionCode = fileName.match(/-versionCode-([0-9]*)/)[1]; + } + + if (!packageName && new RegExp(".* -packageName-").test(fileName)) { + packageName = fileName.match(/-packageName-([a-zA-Z0-9.]*)/)[1]; + } + + // obbs path the same =( + if (gameListName && !packageName && KMETAS[fileName]) { + packageName = fileName; + } + + if (packageName) { + if (!imagePath) { + if (QUEST_ICONS.includes(packageName + ".jpg")) { + imagePath = `https://raw.githubusercontent.com/vKolerts/quest_icons/master/250/${packageName}.jpg`; + } else if (!imagePath) { + imagePath = "unknown.png"; + } + } + + kmeta = KMETAS[packageName]; + installedApp = installedApps[packageName]; + if (installedApp) { + installed = 1; + if (versionCode && versionCode > installedApp.versionCode) { + installed++; + } + } + } + + if (gameListName && !packageName && versionCode) { + packageName = "can`t parse package name"; + imagePath = "unknown.png"; + } + + if (kmeta) { + steamId = !!(kmeta.steam && kmeta.steam.id); + oculusId = !!(kmeta.oculus && kmeta.oculus.id); + sqId = !!(kmeta.sq && kmeta.sq.id); + simpleName = simpleName || kmeta.simpleName; + mp = kmeta.mp || !!kmeta.mp; + } else { + newItem = true; + } + + simpleName = await cleanUpFoldername(simpleName); + const isFile = + info.isFile() || (info.isSymbolicLink() && fileName.includes(".")); // not well + + return { + name: fileName, + simpleName, + isFile, + isLink: info.isSymbolicLink(), + steamId, + sqId, + oculusId, + imagePath, + versionCode, + versionName, + packageName, + size, + note, + newItem, + info, + mp, + installed, + createdAt: new Date(info.mtimeMs), + filePath: path.join(folder, fileName).replace(/\\/g, "/"), + }; + }), + ); + // console.log({ fileNames }); + + const sortFileMode = global.currentConfiguration.sortFiles || "name"; + const sortByName = sortFileMode.startsWith("name"); + const asc = !sortFileMode.endsWith("-desc"); + fileNames + .sort((a, b) => { + const valA = sortByName ? a.simpleName.toLowerCase() : a.info.mtimeMs; + const valB = sortByName ? b.simpleName.toLowerCase() : b.info.mtimeMs; + + if (valA < valB) { + return asc ? -1 : 1; + } + if (valA > valB) { + return asc ? 1 : -1; + } + return 0; + }) + .sort((a, b) => { + if (a.isFile && !b.isFile) { + return 1; + } + if (!a.isFile && b.isFile) { + return -1; + } + return 0; + }); + // console.log(fileNames) + + if ( + folder == oculusGamesDir && + global.currentConfiguration.cacheOculusGames + ) { + console.log("getDir cached", folder); + cacheOculusGames = fileNames; + } + + if (gameListName && gameListName.err) { + fileNames.unshift({ name: gameListName.err }); + } + + return fileNames; + } catch (error) { + console.error("Can`t open folder " + folder, error); + //returnError(e.message) + return false; + } +} + +async function cleanUpFoldername(simpleName) { + // simpleName = simpleName.split('-packageName-')[0]; + simpleName = simpleName.split("-versionCode-")[0]; + simpleName = simpleName.split(/ v[0-9]/)[0]; + return simpleName; +} + +async function getDirListing(folder) { + const files = await fsp.readdir(folder); + let fileNames = await Promise.all( + files.map(async (file) => { + return path.join(folder, file).replace(/\\/g, "/"); + }), + ); + + return fileNames; +} + +async function backupApp({ location, pkg }) { + console.log("backupApp()", pkg, location); + let apk = await adbShell(`pm path ${pkg}`); + apk = apk.replace("package:", ""); + + let folderName = pkg; + + for (const app of global.installedApps) { + if (app["packageName"] != pkg) continue; + folderName = `${app["simpleName"]} -versionCode-${app["versionCode"]} -packageName-${pkg}`; + break; + } + + location = path.join(location, folderName); + console.log({ location, apk }); + + await fsp.mkdir(location, { recursive: true }); + await adbPull(apk, path.join(location, "base.apk")); + const obbsPath = `/sdcard/Android/obb/${pkg}`; + if (!(await adbFileExists(obbsPath))) return true; + + await adbPullFolder(obbsPath, path.join(location, pkg)); + + return true; +} + +const backupPrefsPath = "/sdcard/Download/backup/data/data"; +async function backupAppData( + packageName, + backupPath = path.join(global.sidenoderHome, "backup_data"), +) { + console.log("backupAppData()", packageName); + backupPath = path.join(backupPath, packageName); + if (await adbFileExists(`/sdcard/Android/data/${packageName}`)) { + await adbPullFolder( + `/sdcard/Android/data/${packageName}`, + path.join(backupPath, "Android", packageName), + ); + } else { + console.log(`skip backup Android/data/${packageName}`); + } + + await copyAppPrefs(packageName); + await adbPullFolder( + `${backupPrefsPath}/${packageName}`, + path.join(backupPath, "data", packageName), + ); + await adbShell(`rm -r "${backupPrefsPath}/${packageName}"`); + + fsp.writeFile(`${backupPath}/time.txt`, Date.now()); + return true; +} + +async function restoreAppData( + packageName, + backupPath = path.join(global.sidenoderHome, "backup_data"), +) { + console.log("restoreAppData()", packageName); + backupPath = path.join(backupPath, packageName); + if (!(await fsp.exists(backupPath))) throw `Backup not found ${backupPath}`; + + await adbPushFolder( + path.join(backupPath, "Android", packageName), + `/sdcard/Android/data/${packageName}`, + ); + await adbPushFolder( + path.join(backupPath, "data", packageName), + `${backupPrefsPath}/${packageName}`, + ); + await restoreAppPrefs(packageName); + return true; +} + +async function copyAppPrefs(packageName, removeAfter = false) { + const cmd = removeAfter ? "mv -f" : "cp -rf"; + await adbShell(`mkdir -p "${backupPrefsPath}"`); + return adbShell( + `run-as ${packageName} ${cmd} "/data/data/${packageName}" "${backupPrefsPath}/"`, + ); +} + +async function restoreAppPrefs(packageName, removeAfter = true) { + const cmd = removeAfter ? "mv -f" : "cp -rf"; + const backup_path = `${backupPrefsPath}/${packageName}`; + if (!(await adbFileExists(backup_path))) return; + + return adbShell( + `run-as ${packageName} ${cmd} "${backup_path}" "/data/data/"`, + ); +} + +async function sideloadFolder(arg) { + location = arg.path; + console.log("sideloadFolder()", arg); + let res = { + device: "done", + aapt: false, + check: false, + backup: false, + uninstall: false, + restore: false, + download: false, + apk: false, + download_obb: false, + push_obb: false, + done: false, + update: false, + error: "", + location, + }; + + win.webContents.send("sideload_process", res); + + if (location.endsWith(".apk")) { + apkfile = location; + location = path.dirname(location); + } else { + returnError("not an apk file"); + return; + } + + console.log("start sideload: " + apkfile); + + fromremote = false; + if (location.includes(global.mountFolder)) { + fromremote = true; + } + + console.log("fromremote:", fromremote); + + packageName = ""; + let apktmp = ""; + try { + if (!fromremote) { + res.download = "skip"; + } else { + res.download = "processing"; + win.webContents.send("sideload_process", res); + + apktmp = path.join(global.tmpdir, path.basename(apkfile)); + console.log("is remote, copying to " + apktmp); + + if (await fsp.exists(apktmp)) { + console.log("is remote, " + apktmp + "already exists, using"); + res.download = "skip"; + } else { + const tmpname = `${apktmp}.part`; + if (await fsp.exists(tmpname)) await fsp.unlink(tmpname); + await fsp.copyFile(apkfile, tmpname); + await fsp.rename(tmpname, apktmp); + res.download = "done"; + } + + apkfile = apktmp; + } + } catch (e) { + // returnError(e); + console.error(e); + res.download = "fail"; + res.done = "fail"; + res.error = e; + return win.webContents.send("sideload_process", res); + } + + res.aapt = "processing"; + win.webContents.send("sideload_process", res); + + try { + packageinfo = await getPackageInfo(apkfile); + + packageName = packageinfo.packageName; + console.log({ apkfile, packageinfo, packageName }); + } catch (e) { + // returnError(e); + console.error(e); + res.aapt = "fail"; + res.done = "fail"; + res.error = e; + return win.webContents.send("sideload_process", res); + } + + if (!packageName) { + const e = `Can't parse packageName of ${apkfile}`; + // returnError(new Error(e)); + console.error(e); + res.aapt = "fail"; + res.done = "fail"; + res.error = e; + return win.webContents.send("sideload_process", res); + } + + res.aapt = "done"; + res.check = "processing"; + win.webContents.send("sideload_process", res); + + console.log("checking if installed"); + let installed = false; + try { + installed = await adb.getDevice(global.adbDevice).isInstalled(packageName); + res.check = "done"; + } catch (err) { + console.error("check", e); + res.check = "fail"; + res.error = e; + // TODO: maybe return; + } + + res.backup = "processing"; + win.webContents.send("sideload_process", res); + // const backup_path = `${global.tmpdir}/sidenoder_restore_backup/`; + const backup_path = "/sdcard/Download/backup/Android/data/"; + + // TODO: if adbExist + if (installed) { + console.log("doing adb pull appdata (ignore error)"); + try { + await adbShell(`mkdir -p "${backup_path}"`); + await adbShell( + `mv "/sdcard/Android/data/${packageName}" "${backup_path}"`, + ); + await copyAppPrefs(packageName); + // await backupAppData(packageName, backup_path); + res.backup = "done"; + } catch (e) { + console.error("backup", e); + res.backup = "fail"; + res.error = e; + // TODO: maybe return; + } + } else { + res.backup = "skip"; + } + + res.uninstall = "processing"; + win.webContents.send("sideload_process", res); + + if (installed) { + console.log("doing adb uninstall (ignore error)"); + try { + await adb.getDevice(global.adbDevice).uninstall(packageName); + res.uninstall = "done"; + console.log("uninstall done", packageName); + } catch (e) { + console.error("uninstall", e); + res.uninstall = "fail"; + res.error = e; + } + } else { + res.uninstall = "skip"; + } + + console.log("doing adb install"); + res.apk = "processing"; + win.webContents.send("sideload_process", res); + + try { + await adbInstall(apkfile); + console.log("apk done", packageName); + res.apk = "done"; + } catch (e) { + // returnError(e); + console.error(e); + res.apk = "fail"; + res.done = "fail"; + res.error = e; + return win.webContents.send("sideload_process", res); + } + + res.restore = "processing"; + win.webContents.send("sideload_process", res); + + if (/*installed || */ await adbFileExists(`${backup_path}${packageName}`)) { + console.log("doing adb push appdata (ignore error)"); + try { + // await restoreAppData(packageName, backup_path); + await adbShell( + `mv "${backup_path}${packageName}" "/sdcard/Android/data/"`, + ); + await restoreAppPrefs(packageName); + res.restore = "done"; + console.log("restore done", packageName); + } catch (e) { + console.error("restore", e); + res.restore = "fail"; + res.error = e; + // TODO: maybe return; + } + } else { + res.restore = "skip"; + } + + res.remove_obb = "processing"; + win.webContents.send("sideload_process", res); + + const obbFolderOrig = path.join(location, packageName); + console.log({ obbFolderOrig }); + try { + if (!(await fsp.exists(obbFolderOrig))) throw "Can`t find obbs folder"; + obbFolderDest = `/sdcard/Android/obb/${packageName}`; + console.log("DATAFOLDER to copy:" + obbFolderDest); + } catch (error) { + console.log(error); + obbFolderDest = false; + res.remove_obb = "skip"; + res.download_obb = "skip"; + res.push_obb = "skip"; + win.webContents.send("sideload_process", res); + } + + let obbFiles = []; + if (!obbFolderDest) { + res.download_obb = "skip"; + res.push_obb = "skip"; + } else { + console.log("doing obb rm"); + try { + await adbShell(`rm -r "${obbFolderDest}"`); + await adbShell(`mkdir -p ${obbFolderDest}`, global.adbDevice, true); + res.remove_obb = "done"; + console.log("remove_obb done", packageName); + } catch (e) { + res.remove_obb = "skip"; + console.log(e); + } + + res.download_obb = "processing"; + win.webContents.send("sideload_process", res); + + try { + obbFiles = await fsp.readdir(obbFolderOrig); + console.log("obbFiles: ", obbFiles.length); + + res.download_obb = + (fromremote ? "0" : obbFiles.length) + "/" + obbFiles.length; + res.push_obb = "0/" + obbFiles.length; + win.webContents.send("sideload_process", res); + + const tmpFolder = path.join(global.tmpdir, packageName); + if (fromremote) { + await fsp.mkdir(tmpFolder, { recursive: true }); + } + + for (const obbName of obbFiles) { + const obb = path.join(obbFolderOrig, obbName); + console.log("obb File: " + obbName); + console.log("doing obb push"); + const destFile = `${obbFolderDest}/${obbName}`; + + if (fromremote) { + const obbtmp = path.join(tmpFolder, obbName); + console.log("obb is remote, copying to " + obbtmp); + + if (await fsp.exists(obbtmp)) { + console.log(`obb is remote, ${obbtmp} already exists, using`); + } else { + const tmpname = `${obbtmp}.part`; + if (await fsp.exists(tmpname)) await fsp.unlink(tmpname); + await fsp.copyFile(obb, tmpname); + await fsp.rename(tmpname, obbtmp); + } + + res.download_obb = + +res.download_obb.split("/")[0] + 1 + "/" + obbFiles.length; + win.webContents.send("sideload_process", res); + await adbPush(obbtmp, `${destFile}`, false); + } else { + await adbShell(`mkdir -p ${obbFolderDest}`, global.adbDevice, true); + await adbPush(obb, `${destFile}`, false); + } + + res.push_obb = +res.push_obb.split("/")[0] + 1 + "/" + obbFiles.length; + win.webContents.send("sideload_process", res); + } + + if (fromremote) { + //TODO: check settings + await fsp.rm(tmpFolder, { recursive: true }); + } + } catch (e) { + console.error("obbs processing", e); + if (fromremote) { + res.download_obb = "fail"; + } + + res.push_obb = "fail"; + res.done = "fail"; + res.error = e; + return win.webContents.send("sideload_process", res); + } + } + + if (fromremote) { + //TODO: check settings + await fsp.unlink(apktmp); + } + + res.done = "done"; + res.update = arg.update; + win.webContents.send("sideload_process", res); + console.log("DONE"); + return; +} + +async function getPackageInfo(apkPath) { + const reader = await ApkReader.open(apkPath); + const manifest = await reader.readManifest(); + + info = { + packageName: manifest.package, + versionCode: manifest.versionCode, + versionName: manifest.versionName, + }; + + return info; +} + +async function getInstalledApps(obj = false) { + let apps = await adbShell(`pm list packages -3 --show-versioncode`); + apps = apps.split("\n"); + // apps.pop(); + appinfo = {}; + + for (const appLine of apps) { + const [packageName, versionCode] = appLine.slice(8).split(" versionCode:"); + + const info = []; + info["simpleName"] = + (KMETAS[packageName] && KMETAS[packageName].simpleName) || packageName; + info["packageName"] = packageName; + info["versionCode"] = versionCode; + info["imagePath"] = QUEST_ICONS.includes(packageName + ".jpg") + ? `https://raw.githubusercontent.com/vKolerts/quest_icons/master/250/${packageName}.jpg` + : `http://cdn.apk-cloud.com/detail/image/${packageName}-w130.png`; //'unknown.png'; + + appinfo[packageName] = info; + } + + const sortAppMode = global.currentConfiguration.sortApps || "simplename"; + const sortByName = sortAppMode.startsWith("simplename"); + const asc = !sortAppMode.endsWith("-desc"); + global.installedApps = Object.values(appinfo).sort((a, b) => { + const valA = (sortByName ? a.simpleName : a.packageName).toLowerCase(); + const valB = (sortByName ? b.simpleName : b.packageName).toLowerCase(); + if (valA < valB) { + return asc ? -1 : 1; + } + if (valA > valB) { + return asc ? 1 : -1; + } + return 0; + }); + + return obj ? appinfo : global.installedApps; +} + +async function getInstalledAppsWithUpdates() { + const remotePath = path.join( + global.mountFolder, + global.currentConfiguration.mntGamePath, + ); // TODO: folder path to config + const list = await getDir(remotePath); + let remotePackages = {}; + let remoteList = {}; + + if (list) + for (const app of list) { + const { name, packageName, versionCode, simpleName, filePath, size } = + app; + if (!packageName) continue; + + if (!remotePackages[packageName]) remotePackages[packageName] = []; + remotePackages[packageName].push(name); + + remoteList[name] = { + versionCode, + simpleName, + filePath, + size, + }; + } + + const remoteKeys = Object.keys(remotePackages); + + const apps = global.installedApps || (await getInstalledApps()); + let updates = []; + for (const app of apps) { + const packageName = app["packageName"]; + // console.log(packageName, 'checking'); + + if (!remoteKeys.includes(packageName)) continue; + + for (name of remotePackages[packageName]) { + const pkg = remoteList[name]; + const installedVersion = app["versionCode"]; + const remoteversion = pkg.versionCode; + + // console.log({ packageName, installedVersion, remoteversion }); + // console.log({ pkg }); + + if (remoteversion <= installedVersion) continue; + + app["simpleName"] = pkg.simpleName; + app["update"] = []; + app["update"]["path"] = pkg.filePath; + app["update"]["size"] = pkg.size; + app["update"]["versionCode"] = remoteversion; + updates.push(app); + + console.log(packageName, "UPDATE AVAILABLE"); + } + } + + global.installedApps = false; + return updates; +} + +async function detectNoteTxt(files, folder) { + // TODO: check .meta/notes + + if (typeof files == "string") { + folder = files; + files = false; + } + + if (!files) { + files = await fsp.readdir(folder); + } + + if (files.includes("notes.txt")) { + return fsp.readFile(path.join(folder, "notes.txt"), "utf8"); + } + + return false; +} + +async function detectInstallTxt(files, folder) { + if (typeof files == "string") { + folder = files; + files = false; + } + + if (!files) { + files = await fsp.readdir(folder); + } + + const installTxNames = ["install.txt", "Install.txt"]; + + for (const name of installTxNames) { + if (files.includes(name)) { + return fsp.readFile(path.join(folder, name), "utf8"); + } + } + + return false; +} + +async function getApkFromFolder(folder) { + let res = { + path: false, + install_desc: false, + }; + + const files = await fsp.readdir(folder); + res.install_desc = await detectInstallTxt(files, folder); + res.notes = await detectNoteTxt(files, folder); + console.log({ files }); + + for (file of files) { + if (file.endsWith(".apk")) { + res.path = path.join(folder, file).replace(/\\/g, "/"); + return res; + } + } + + returnError("No apk found in " + folder); + return res; +} + +async function uninstall(packageName) { + resp = await adb.getDevice(global.adbDevice).uninstall(packageName); +} + +let rcloneProgress = false; +async function updateRcloneProgress() { + try { + const response = await fetch("http://127.0.0.1:5572/core/stats", { + method: "POST", + }); + const data = await response.json(); + if (!data.transferring || !data.transferring[0]) throw "no data"; + const transferring = data.transferring[0]; + rcloneProgress = { + cmd: "download", + bytes: transferring.bytes, + size: transferring.size, + percentage: transferring.percentage, + speedAvg: transferring.speedAvg, + eta: transferring.eta, + name: transferring.name, + }; + //console.log('sending rclone data'); + win.webContents.send("process_data", rcloneProgress); + } catch (error) { + //console.error('Fetch-Error:', error); + if (rcloneProgress) { + rcloneProgress = false; + win.webContents.send("process_data", rcloneProgress); + } + } + + setTimeout(updateRcloneProgress, 2000); +} + +async function init() { + fsp.exists = (p) => + fsp + .access(p) + .then(() => true) + .catch((e) => false); + + await initLogs(); + + console.log( + { platform, arch, version, sidenoderHome }, + process.platform, + process.arch, + process.argv, + ); + + await loadMeta(); +} + +async function loadMeta() { + try { + const res = await fetch( + "https://raw.githubusercontent.com/vKolerts/quest_icons/master/version?" + + Date.now(), + ); + version = await res.text(); + if (version == META_VERSION) return setTimeout(loadMeta, CHECK_META_PERIOD); + + META_VERSION = version; + console.log("Meta version", META_VERSION); + } catch (err) { + console.error("can`t get meta version", err); + } + + try { + const res = await fetch( + "https://raw.githubusercontent.com/vKolerts/quest_icons/master/list.json?" + + Date.now(), + ); + QUEST_ICONS = await res.json(); + console.log("icons list loaded"); + } catch (err) { + console.error("can`t get quest_icons", err); + } + + try { + const res = await fetch( + "https://raw.githubusercontent.com/vKolerts/quest_icons/master/.e?" + + Date.now(), + ); + const text = await res.text(); + const iv = Buffer.from(text.substring(0, l), "hex"); + const secret = crypto + .createHash(hash_alg) + .update(pkg.author.split(" ")[0].repeat(2)) + .digest("base64") + .substr(0, l); + const decipher = crypto.createDecipheriv("aes-256-cbc", secret, iv); + const encrypted = text.substring(l); + KMETAS = JSON.parse( + decipher.update(encrypted, "base64", "utf8") + decipher.final("utf8"), + ); + for (const pkg of Object.keys(KMETAS)) { + KMETAS2[escString(KMETAS[pkg].simpleName)] = pkg; + } + + console.log("kmetas loaded"); + } catch (err) { + console.error("can`t get kmetas", err); + } + + setTimeout(loadMeta, CHECK_META_PERIOD); +} + +function escString(val) { + let res = val.toLowerCase(); + res = res.replace(/[-\_:.,!?\"'&™®| ]/g, ""); + return res; +} + +async function initLogs() { + const log_path = path.join(sidenoderHome, "debug_last.log"); + if (await fsp.exists(log_path)) { + await fsp.unlink(log_path); + } else { + await fsp.mkdir(sidenoderHome, { recursive: true }); + } + + const log_file = fs.createWriteStream(log_path, { flags: "w" }); + const log_stdout = process.stdout; + + function dateF() { + const d = new Date(); + return `[${d.toLocaleString()}.${d.getMilliseconds()}] `; + } + + console.log = function (...d) { + let line = ""; + let line_color = ""; + for (const l of d) { + if (typeof l == "string") { + line += l + " "; + line_color += l + " "; + continue; + } + + const formated = util.format(l); + line += formated + " "; + line_color += "\x1b[32m" + formated + "\x1b[0m "; + } + + log_stdout.write(dateF() + line_color + "\n"); + log_file.write(dateF() + line + "\n"); + }; + + console.error = function (...d) { + let line = ""; + for (const l of d) { + line += util.format(l) + " "; + } + + log_stdout.write(`\x1b[31m${dateF()}ERROR: ` + line + "\x1b[0m\n"); + log_file.write(dateF() + "ERROR: " + line + "\n"); + }; + + console.warning = function (...d) { + let line = ""; + for (const l of d) { + line += util.format(l) + " "; + } + + log_stdout.write(`\x1b[33m${dateF()}WARN: ` + line + "\x1b[0m\n"); + log_file.write(dateF() + "WARN: " + line + "\n"); + }; +} + +async function fetchTimeout(url = "", options = {}, timeout = 20 * 1000) { + const controller = new AbortController(); + options.signal = controller.signal; + setTimeout(() => { + controller.abort(); + }, timeout); + + return fetch(url, options); +} + +async function saveConfig(config = global.currentConfiguration) { + await fsp.writeFile(configLocation, JSON.stringify(config, null, " ")); +} + +async function reloadConfig() { + const defaultConfig = { + allowOtherDevices: false, + cacheOculusGames: true, + autoMount: false, + adbPath: "", + rclonePath: "", + rcloneConf: "", + mountCmd: "mount", + cfgSection: "", + snapshotsDelete: true, + mntGamePath: "Quest Games", + scrcpyBitrate: "5", + scrcpyCrop: "1600:900:2017:510", + lastIp: "", + userHide: false, + dirBookmarks: [{ name: "Sidenoder folder", path: global.sidenoderHome }], + + proxyUrl: "", + proxyOculus: false, + proxySteam: false, + proxySQ: false, + }; + + if (await fsp.exists(configLocationOld)) { + await fsp.rename(configLocationOld, configLocation); + } + + if (await fsp.exists(configLocation)) { + console.log("Config exist, using " + configLocation); + global.currentConfiguration = Object.assign( + defaultConfig, + require(configLocation), + ); + } else { + console.log("Config doesnt exist, creating ") + configLocation; + await saveConfig(defaultConfig); + global.currentConfiguration = defaultConfig; + } + + if (global.currentConfiguration.tmpdir) { + global.tmpdir = global.currentConfiguration.tmpdir; + } + + if (!global.currentConfiguration.dirBookmarks) { + global.currentConfiguration.dirBookmarks = defaultConfig.dirBookmarks; + } + + proxySettings(); + + await parseRcloneSections(); +} + +function proxySettings(proxyUrl = global.currentConfiguration.proxyUrl) { + const { proxyOculus, proxySteam, proxySQ } = global.currentConfiguration; + + agentOculus = + proxyUrl && proxyOculus ? new SocksProxyAgent(proxyUrl) : undefined; + agentSteam = + proxyUrl && proxySteam ? new SocksProxyAgent(proxyUrl) : undefined; + agentSQ = proxyUrl && proxySQ ? new SocksProxyAgent(proxyUrl) : undefined; +} + +async function changeConfig(key, value) { + console.log("cfg.update", key, value); + if (key == "proxyUrl") proxySettings(value); + if (["proxyOculus", "proxySteam", "proxySQ"].includes(key)) proxySettings(); + + global.currentConfiguration[key] = value; + await saveConfig(); + + if (key == "rcloneConf") await parseRcloneSections(true); + if (key == "tmpdir") + global.tmpdir = value || require("os").tmpdir().replace(/\\/g, "/"); + + return value; +} + diff --git a/versioncheck.js b/versioncheck.js index 1d625e7..147faab 100644 --- a/versioncheck.js +++ b/versioncheck.js @@ -1,24 +1,26 @@ -const pkg = require('./package.json'); -const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); -const compareVersions = require('compare-versions'); +const pkg = require("./package.json"); +const fetch = (...args) => + import("node-fetch").then(({ default: fetch }) => fetch(...args)); +const compareVersions = require("compare-versions"); global.version = pkg.version; async function checkVersion() { try { - const res = await fetch('https://api.github.com/repos/VRPirates/sidenoder/releases/latest'); - const content = JSON.parse(await res.text()) + const res = await fetch( + "https://api.github.com/repos/VRPirates/sidenoder/releases/latest", + ); + const content = JSON.parse(await res.text()); const remoteversion = content.name; - console.log('Current version: ' + pkg.version); - console.log('Github version: ' + remoteversion); + console.log("Current version: " + pkg.version); + console.log("Github version: " + remoteversion); if (!remoteversion) return; - if (compareVersions.compare(remoteversion, pkg.version, '<=')) { - console.log('Using latest version'); - } - else { - console.log('requires update'); - win.webContents.send('notify_update', { + if (compareVersions.compare(remoteversion, pkg.version, "<=")) { + console.log("Using latest version"); + } else { + console.log("requires update"); + win.webContents.send("notify_update", { success: true, current: pkg.version, remote: remoteversion, @@ -26,8 +28,7 @@ async function checkVersion() { description: content.body, }); } + } catch (err) { + console.error("checkVersion.Fail", err); } - catch (err) { - console.error('checkVersion.Fail', err); - } -} \ No newline at end of file +} diff --git a/views/browse_include.twig b/views/browse_include.twig index 1450056..64ded21 100644 --- a/views/browse_include.twig +++ b/views/browse_include.twig @@ -4,13 +4,15 @@ <a class="btn btn-md btn-primary" onclick="upDir()" - title="Up dir ../ (hotkey:backspace)"> + title="Up dir ../ (hotkey:backspace)" + > <i class="fa fa-level-up"></i> </a> <a class="btn btn-md btn-primary" id="shellOpenDirPath" - title="Open by filebrowser"> + title="Open by filebrowser" + > <i class="fa fa-folder-open"></i> </a> <a class="btn btn-md btn-primary" id="editDirPath" title="Edit path"> @@ -22,47 +24,54 @@ <button class="btn btn-md btn-primary dropdown-toggle" type="button" - onclick="$id('bookmarksdropdown').toggle()"> + onclick="$id('bookmarksdropdown').toggle()" + > <i class="fa fa-star" title="Bookmarks"></i> </button> <div id="bookmarksdropdown" class="dropdown-menu" - style="left: auto; top: auto"> + style="left: auto; top: auto" + > <div class="input-group"> <input id="bookmarkName" class="ml-3" value="" - placeholder="Bookmark name" /> + placeholder="Bookmark name" + > <a class="btn btn-warning btn-sm mr-3" id="addBookmark"> <i class="fa fa-plus" title="Add current location"></i ></a> </div> - <hr /> + <hr> </div> <a class="btn btn-md btn-primary" onclick="refreshDir()" - title="(hotkey: ctrl+r)"> + title="(hotkey: ctrl+r)" + > <i class="fa fa-refresh"></i> Refresh</a > <a class="btn btn-md btn-primary" onclick="openSearch()" - title="(hotkey: ctrl+f)"> + title="(hotkey: ctrl+f)" + > <i class="fa fa-search"></i> Search</a > <button class="btn btn-md btn-primary dropdown-toggle" type="button" - onclick="$id('searchdropdownmenu').toggle()"> + onclick="$id('searchdropdownmenu').toggle()" + > <i class="fa fa-sort"></i> Sort By </button> <div id="searchdropdownmenu" class="dropdown-menu" - style="right: 0; left: auto; top: auto"> + style="right: 0; left: auto; top: auto" + > <a class="dropdown-item" onclick="sortFiles('name', 1)"> <i class="fa fa-sort-alpha-asc"></i> Name</a > @@ -93,25 +102,25 @@ <script> /* global promptDialog, addBookmark */ (() => { - $id("processingModal").modal("show"); - id("nav-panel").style.top = $id("topbar").height() + "px"; // fix navbar position + $id('processingModal').modal('show'); + id('nav-panel').style.top = $id('topbar').height() + 'px'; // fix navbar position - const { dirBookmarks } = remote.getGlobal("currentConfiguration"); + const { dirBookmarks } = remote.getGlobal('currentConfiguration'); for (const { name, path } of dirBookmarks) { addBookmark(name, path, false); } - id("addBookmark").onclick = () => { - addBookmark(id("bookmarkName").value, id("path").title); + id('addBookmark').onclick = () => { + addBookmark(id('bookmarkName').value, id('path').title); }; - id("shellOpenDirPath").onclick = () => { - shell.openPath(id("path").title); + id('shellOpenDirPath').onclick = () => { + shell.openPath(id('path').title); }; - id("editDirPath").onclick = () => { + id('editDirPath').onclick = () => { promptDialog( - "Change directory location", - "", - id("path").title, + 'Change directory location', + '', + id('path').title, true, ).then((loc) => { if (loc) { @@ -120,4 +129,4 @@ }); }; })(); -</script> \ No newline at end of file +</script> diff --git a/views/css/bootstrap-slate.min.css b/views/css/bootstrap-slate.min.css index 8219ffb..ed859b9 100644 --- a/views/css/bootstrap-slate.min.css +++ b/views/css/bootstrap-slate.min.css @@ -9,4 +9,4 @@ * Copyright 2011-2021 The Bootstrap Authors * Copyright 2011-2021 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#ee5f5b;--orange:#fd7e14;--yellow:#f89406;--green:#62c462;--teal:#20c997;--cyan:#5bc0de;--white:#fff;--gray:#7a8288;--gray-dark:#3a3f44;--primary:#3a3f44;--secondary:#7a8288;--success:#62c462;--info:#5bc0de;--warning:#f89406;--danger:#ee5f5b;--light:#e9ecef;--dark:#272b30;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:.9375rem;font-weight:400;line-height:1.5;color:#aaa;text-align:left;background-color:#272b30}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#fff;text-decoration:none;background-color:transparent}a:hover{color:#d9d9d9;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#7a8288;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.34375rem}.h2,h2{font-size:1.875rem}.h3,h3{font-size:1.640625rem}.h4,h4{font-size:1.40625rem}.h5,h5{font-size:1.171875rem}.h6,h6{font-size:.9375rem}.lead{font-size:1.171875rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.171875rem}.blockquote-footer{display:block;font-size:80%;color:#7a8288}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#272b30;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#7a8288}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#272b30;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:inherit}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:817px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:1066px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1318px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#fff}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid rgba(0,0,0,.6)}.table thead th{vertical-align:bottom;border-bottom:2px solid rgba(0,0,0,.6)}.table tbody+tbody{border-top:2px solid rgba(0,0,0,.6)}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid rgba(0,0,0,.6)}.table-bordered td,.table-bordered th{border:1px solid rgba(0,0,0,.6)}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#c8c9cb}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#999b9e}.table-hover .table-primary:hover{background-color:#bbbcbf}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#bbbcbf}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#dadcde}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#babec1}.table-hover .table-secondary:hover{background-color:#cdcfd2}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cdcfd2}.table-success,.table-success>td,.table-success>th{background-color:#d3eed3}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#ade0ad}.table-hover .table-success:hover{background-color:#c1e7c1}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#c1e7c1}.table-info,.table-info>td,.table-info>th{background-color:#d1edf6}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#aadeee}.table-hover .table-info:hover{background-color:#bce5f2}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#bce5f2}.table-warning,.table-warning>td,.table-warning>th{background-color:#fde1b9}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#fbc77e}.table-hover .table-warning:hover{background-color:#fcd6a0}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fcd6a0}.table-danger,.table-danger>td,.table-danger>th{background-color:#fad2d1}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#f6acaa}.table-hover .table-danger:hover{background-color:#f8bcba}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f8bcba}.table-light,.table-light>td,.table-light>th{background-color:#f9fafb}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#f4f5f7}.table-hover .table-light:hover{background-color:#eaedf1}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#eaedf1}.table-dark,.table-dark>td,.table-dark>th{background-color:#c3c4c5}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#8f9193}.table-hover .table-dark:hover{background-color:#b6b7b8}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b6b7b8}.table-active,.table-active>td,.table-active>th{background-color:rgba(255,255,255,.075)}.table-hover .table-active:hover{background-color:rgba(242,242,242,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(242,242,242,.075)}.table .thead-dark th{color:#fff;background-color:#3a3f44;border-color:rgba(0,0,0,.6)}.table .thead-light th{color:#52575c;background-color:#e9ecef;border-color:rgba(0,0,0,.6)}.table-dark{color:#fff;background-color:#3a3f44}.table-dark td,.table-dark th,.table-dark thead th{border-color:rgba(0,0,0,.6)}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + 1.5rem + 2px);padding:.75rem 1rem;font-size:.9375rem;font-weight:400;line-height:1.5;color:#52575c;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #52575c}.form-control:focus{color:#52575c;background-color:#fff;border-color:#757f89;outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.form-control::-webkit-input-placeholder{color:#7a8288;opacity:1}.form-control::-moz-placeholder{color:#7a8288;opacity:1}.form-control:-ms-input-placeholder{color:#7a8288;opacity:1}.form-control::-ms-input-placeholder{color:#7a8288;opacity:1}.form-control::placeholder{color:#7a8288;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#ccc;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#52575c;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.75rem + 1px);padding-bottom:calc(.75rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.171875rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.825rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.75rem 0;margin-bottom:0;font-size:.9375rem;line-height:1.5;color:#aaa;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.825rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#7a8288}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#62c462}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.825rem;line-height:1.5;color:#fff;background-color:rgba(98,196,98,.9);border-radius:.25rem}.form-row>.col>.valid-tooltip,.form-row>[class*=col-]>.valid-tooltip{left:5px}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#62c462;padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2362c462' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .375rem) center;background-size:calc(.75em + .75rem) calc(.75em + .75rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#62c462;box-shadow:0 0 0 .2rem rgba(98,196,98,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(.375em + .375rem) right calc(.375em + .375rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#62c462;padding-right:calc(.75em + 3.125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%233a3f44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2362c462' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 2rem/calc(.75em + .75rem) calc(.75em + .75rem) no-repeat}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#62c462;box-shadow:0 0 0 .2rem rgba(98,196,98,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#62c462}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#62c462}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#62c462}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#87d287;background-color:#87d287}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(98,196,98,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#62c462}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#62c462}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#62c462;box-shadow:0 0 0 .2rem rgba(98,196,98,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#ee5f5b}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.825rem;line-height:1.5;color:#fff;background-color:rgba(238,95,91,.9);border-radius:.25rem}.form-row>.col>.invalid-tooltip,.form-row>[class*=col-]>.invalid-tooltip{left:5px}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#ee5f5b;padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23ee5f5b' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ee5f5b' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .375rem) center;background-size:calc(.75em + .75rem) calc(.75em + .75rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#ee5f5b;box-shadow:0 0 0 .2rem rgba(238,95,91,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(.375em + .375rem) right calc(.375em + .375rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#ee5f5b;padding-right:calc(.75em + 3.125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%233a3f44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23ee5f5b' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ee5f5b' stroke='none'/%3e%3c/svg%3e") center right 2rem/calc(.75em + .75rem) calc(.75em + .75rem) no-repeat}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#ee5f5b;box-shadow:0 0 0 .2rem rgba(238,95,91,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#ee5f5b}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#ee5f5b}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#ee5f5b}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#f38c89;background-color:#f38c89}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(238,95,91,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#ee5f5b}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#ee5f5b}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#ee5f5b;box-shadow:0 0 0 .2rem rgba(238,95,91,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#aaa;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.75rem 1rem;font-size:.9375rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#aaa;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#3a3f44;border-color:#3a3f44}.btn-primary:hover{color:#fff;background-color:#282c2f;border-color:#232628}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#282c2f;border-color:#232628;box-shadow:0 0 0 .2rem rgba(88,92,96,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#3a3f44;border-color:#3a3f44}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#232628;border-color:#1d1f22}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(88,92,96,.5)}.btn-secondary{color:#fff;background-color:#7a8288;border-color:#7a8288}.btn-secondary:hover{color:#fff;background-color:#686f74;border-color:#62686d}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#686f74;border-color:#62686d;box-shadow:0 0 0 .2rem rgba(142,149,154,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#7a8288;border-color:#7a8288}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#62686d;border-color:#5c6267}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(142,149,154,.5)}.btn-success{color:#fff;background-color:#62c462;border-color:#62c462}.btn-success:hover{color:#fff;background-color:#46ba46;border-color:#42b142}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#46ba46;border-color:#42b142;box-shadow:0 0 0 .2rem rgba(122,205,122,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#62c462;border-color:#62c462}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#42b142;border-color:#3fa73f}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(122,205,122,.5)}.btn-info{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover{color:#fff;background-color:#3bb4d8;border-color:#31b0d5}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#3bb4d8;border-color:#31b0d5;box-shadow:0 0 0 .2rem rgba(116,201,227,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#2aaacf}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(116,201,227,.5)}.btn-warning{color:#fff;background-color:#f89406;border-color:#f89406}.btn-warning:hover{color:#fff;background-color:#d37e05;border-color:#c67605}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#d37e05;border-color:#c67605;box-shadow:0 0 0 .2rem rgba(249,164,43,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;background-color:#f89406;border-color:#f89406}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c67605;border-color:#ba6f04}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(249,164,43,.5)}.btn-danger{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:hover{color:#fff;background-color:#ea3d38;border-color:#e9322d}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#ea3d38;border-color:#e9322d;box-shadow:0 0 0 .2rem rgba(241,119,116,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#e9322d;border-color:#e82721}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(241,119,116,.5)}.btn-light{color:#272b30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:hover{color:#272b30;background-color:#d3d9df;border-color:#cbd3da}.btn-light.focus,.btn-light:focus{color:#272b30;background-color:#d3d9df;border-color:#cbd3da;box-shadow:0 0 0 .2rem rgba(204,207,210,.5)}.btn-light.disabled,.btn-light:disabled{color:#272b30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#272b30;background-color:#cbd3da;border-color:#c4ccd4}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(204,207,210,.5)}.btn-dark{color:#fff;background-color:#272b30;border-color:#272b30}.btn-dark:hover{color:#fff;background-color:#16181b;border-color:#101214}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#16181b;border-color:#101214;box-shadow:0 0 0 .2rem rgba(71,75,79,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#272b30;border-color:#272b30}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#101214;border-color:#0a0b0d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(71,75,79,.5)}.btn-outline-primary{color:#3a3f44;border-color:#3a3f44}.btn-outline-primary:hover{color:#fff;background-color:#3a3f44;border-color:#3a3f44}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(58,63,68,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#3a3f44;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#3a3f44;border-color:#3a3f44}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,63,68,.5)}.btn-outline-secondary{color:#7a8288;border-color:#7a8288}.btn-outline-secondary:hover{color:#fff;background-color:#7a8288;border-color:#7a8288}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(122,130,136,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#7a8288;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#7a8288;border-color:#7a8288}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(122,130,136,.5)}.btn-outline-success{color:#62c462;border-color:#62c462}.btn-outline-success:hover{color:#fff;background-color:#62c462;border-color:#62c462}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(98,196,98,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#62c462;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#62c462;border-color:#62c462}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(98,196,98,.5)}.btn-outline-info{color:#5bc0de;border-color:#5bc0de}.btn-outline-info:hover{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(91,192,222,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#5bc0de;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(91,192,222,.5)}.btn-outline-warning{color:#f89406;border-color:#f89406}.btn-outline-warning:hover{color:#fff;background-color:#f89406;border-color:#f89406}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(248,148,6,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#f89406;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#f89406;border-color:#f89406}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,148,6,.5)}.btn-outline-danger{color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:hover{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(238,95,91,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#ee5f5b;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(238,95,91,.5)}.btn-outline-light{color:#e9ecef;border-color:#e9ecef}.btn-outline-light:hover{color:#272b30;background-color:#e9ecef;border-color:#e9ecef}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(233,236,239,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#e9ecef;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#272b30;background-color:#e9ecef;border-color:#e9ecef}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(233,236,239,.5)}.btn-outline-dark{color:#272b30;border-color:#272b30}.btn-outline-dark:hover{color:#fff;background-color:#272b30;border-color:#272b30}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(39,43,48,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#272b30;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#272b30;border-color:#272b30}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(39,43,48,.5)}.btn-link{font-weight:400;color:#fff;text-decoration:none}.btn-link:hover{color:#d9d9d9;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#7a8288;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.825rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:.9375rem;color:#aaa;text-align:left;list-style:none;background-color:#3a3f44;background-clip:padding-box;border:1px solid rgba(0,0,0,.6);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#aaa;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#fff;text-decoration:none;background-color:#272b30}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#272b30}.dropdown-item.disabled,.dropdown-item:disabled{color:#999;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.825rem;color:#7a8288;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#aaa}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:first-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label::after,.input-group:not(.has-validation)>.custom-select:not(:last-child),.input-group:not(.has-validation)>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label::after,.input-group.has-validation>.custom-select:nth-last-child(n+3),.input-group.has-validation>.form-control:nth-last-child(n+3){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.75rem 1rem;margin-bottom:0;font-size:.9375rem;font-weight:400;line-height:1.5;color:#52575c;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.825rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:2rem}.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.btn,.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.input-group-text,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.btn,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.40625rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.203125rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#3a3f44;background-color:#3a3f44}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#757f89}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#9098a0;border-color:#9098a0}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#7a8288}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#ccc}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#999 solid 1px}.custom-control-label::after{position:absolute;top:.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:50%/50% 50% no-repeat}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#3a3f44;background-color:#3a3f44}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(58,63,68,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(58,63,68,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(58,63,68,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.203125rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#999;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(58,63,68,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + 1.5rem + 2px);padding:.75rem 2rem .75rem 1rem;font-size:.9375rem;font-weight:400;line-height:1.5;color:#52575c;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%233a3f44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1rem center/8px 10px no-repeat;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#757f89;outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.custom-select:focus::-ms-value{color:#52575c;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:1rem;background-image:none}.custom-select:disabled{color:#7a8288;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #52575c}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.825rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.171875rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + 1.5rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + 1.5rem + 2px);margin:0;overflow:hidden;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#757f89;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#ccc}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + 1.5rem + 2px);padding:.75rem 1rem;overflow:hidden;font-weight:400;line-height:1.5;color:#52575c;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + 1.5rem);padding:.75rem 1rem;line-height:1.5;color:#52575c;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #272b30,0 0 0 .2rem rgba(58,63,68,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #272b30,0 0 0 .2rem rgba(58,63,68,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #272b30,0 0 0 .2rem rgba(58,63,68,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#3a3f44;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#9098a0}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#3a3f44;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#9098a0}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#3a3f44;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#9098a0}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#999}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#999}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#999}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#7a8288;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid rgba(0,0,0,.6)}.nav-tabs .nav-link{margin-bottom:-1px;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:rgba(0,0,0,.6)}.nav-tabs .nav-link.disabled{color:#7a8288;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#fff;background-color:#272b30;border-color:rgba(0,0,0,.6)}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#3a3f44}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:0 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.324219rem;padding-bottom:.324219rem;margin-right:1rem;font-size:1.171875rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.171875rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:50%/100% 100% no-repeat}.navbar-nav-scroll{max-height:75vh;overflow-y:auto}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:#3a3f44}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:#3a3f44}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:#3a3f44}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:#3a3f44}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:#3a3f44}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:#3a3f44}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:#fff}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#32383e;background-clip:border-box;border:1px solid rgba(0,0,0,.6);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:#515960;border-bottom:1px solid rgba(0,0,0,.6)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:#515960;border-top:1px solid rgba(0,0,0,.6)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#7a8288;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#999}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#fff;background-color:transparent;border:1px solid rgba(0,0,0,.6)}.page-link:hover{z-index:2;color:#fff;text-decoration:none;background-color:transparent;border-color:rgba(0,0,0,.6)}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:transparent;border-color:rgba(0,0,0,.6)}.page-item.disabled .page-link{color:#7a8288;pointer-events:none;cursor:auto;background-color:transparent;border-color:rgba(0,0,0,.6)}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.171875rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.825rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#3a3f44}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#232628}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.5)}.badge-secondary{color:#fff;background-color:#7a8288}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#62686d}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(122,130,136,.5)}.badge-success{color:#fff;background-color:#62c462}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#42b142}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(98,196,98,.5)}.badge-info{color:#fff;background-color:#5bc0de}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#31b0d5}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(91,192,222,.5)}.badge-warning{color:#fff;background-color:#f89406}a.badge-warning:focus,a.badge-warning:hover{color:#fff;background-color:#c67605}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,148,6,.5)}.badge-danger{color:#fff;background-color:#ee5f5b}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#e9322d}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(238,95,91,.5)}.badge-light{color:#272b30;background-color:#e9ecef}a.badge-light:focus,a.badge-light:hover{color:#272b30;background-color:#cbd3da}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(233,236,239,.5)}.badge-dark{color:#fff;background-color:#272b30}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#101214}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(39,43,48,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#1c1e22;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#1e2123;background-color:#d8d9da;border-color:#c8c9cb}.alert-primary hr{border-top-color:#bbbcbf}.alert-primary .alert-link{color:#060708}.alert-secondary{color:#3f4447;background-color:#e4e6e7;border-color:#dadcde}.alert-secondary hr{border-top-color:#cdcfd2}.alert-secondary .alert-link{color:#272a2c}.alert-success{color:#363;background-color:#e0f3e0;border-color:#d3eed3}.alert-success hr{border-top-color:#c1e7c1}.alert-success .alert-link{color:#242}.alert-info{color:#2f6473;background-color:#def2f8;border-color:#d1edf6}.alert-info hr{border-top-color:#bce5f2}.alert-info .alert-link{color:#20454f}.alert-warning{color:#814d03;background-color:#feeacd;border-color:#fde1b9}.alert-warning hr{border-top-color:#fcd6a0}.alert-warning .alert-link{color:#4f2f02}.alert-danger{color:#7c312f;background-color:#fcdfde;border-color:#fad2d1}.alert-danger hr{border-top-color:#f8bcba}.alert-danger .alert-link{color:#572221}.alert-light{color:#797b7c;background-color:#fbfbfc;border-color:#f9fafb}.alert-light hr{border-top-color:#eaedf1}.alert-light .alert-link{color:#606162}.alert-dark{color:#141619;background-color:#d4d5d6;border-color:#c3c4c5}.alert-dark hr{border-top-color:#b6b7b8}.alert-dark .alert-link{color:#000}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.703125rem;background-color:#1c1e22;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#7a8288;text-align:center;white-space:nowrap;background-color:#3a3f44;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#fff;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#fff;text-decoration:none;background-color:#3e444c}.list-group-item-action:active{color:#aaa;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#32383e;border:1px solid rgba(0,0,0,.6)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#52575c;pointer-events:none;background-color:#32383e}.list-group-item.active{z-index:2;color:#fff;background-color:#3e444c;border-color:rgba(0,0,0,.6)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#1e2123;background-color:#c8c9cb}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#1e2123;background-color:#bbbcbf}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#1e2123;border-color:#1e2123}.list-group-item-secondary{color:#3f4447;background-color:#dadcde}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#3f4447;background-color:#cdcfd2}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#3f4447;border-color:#3f4447}.list-group-item-success{color:#363;background-color:#d3eed3}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#363;background-color:#c1e7c1}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#363;border-color:#363}.list-group-item-info{color:#2f6473;background-color:#d1edf6}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#2f6473;background-color:#bce5f2}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#2f6473;border-color:#2f6473}.list-group-item-warning{color:#814d03;background-color:#fde1b9}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#814d03;background-color:#fcd6a0}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#814d03;border-color:#814d03}.list-group-item-danger{color:#7c312f;background-color:#fad2d1}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#7c312f;background-color:#f8bcba}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#7c312f;border-color:#7c312f}.list-group-item-light{color:#797b7c;background-color:#f9fafb}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#797b7c;background-color:#eaedf1}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#797b7c;border-color:#797b7c}.list-group-item-dark{color:#141619;background-color:#c3c4c5}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#b6b7b8}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.close{float:right;font-size:1.40625rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#aaa;background-color:#32383e;background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid rgba(0,0,0,.2);border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.825rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.825rem;word-wrap:break-word;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#32383e}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#32383e}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#32383e}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #2c3036}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#32383e}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:.9375rem;background-color:#2c3036;border-bottom:1px solid #202328;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#aaa}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:50%/100% 100% no-repeat}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#3a3f44!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#232628!important}.bg-secondary{background-color:#7a8288!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#62686d!important}.bg-success{background-color:#62c462!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#42b142!important}.bg-info{background-color:#5bc0de!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#31b0d5!important}.bg-warning{background-color:#f89406!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#c67605!important}.bg-danger{background-color:#ee5f5b!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#e9322d!important}.bg-light{background-color:#e9ecef!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#cbd3da!important}.bg-dark{background-color:#272b30!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#101214!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#3a3f44!important}.border-secondary{border-color:#7a8288!important}.border-success{border-color:#62c462!important}.border-info{border-color:#5bc0de!important}.border-warning{border-color:#f89406!important}.border-danger{border-color:#ee5f5b!important}.border-light{border-color:#e9ecef!important}.border-dark{border-color:#272b30!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#3a3f44!important}a.text-primary:focus,a.text-primary:hover{color:#17191b!important}.text-secondary{color:#7a8288!important}a.text-secondary:focus,a.text-secondary:hover{color:#565b60!important}.text-success{color:#62c462!important}a.text-success:focus,a.text-success:hover{color:#3b9e3b!important}.text-info{color:#5bc0de!important}a.text-info:focus,a.text-info:hover{color:#28a1c5!important}.text-warning{color:#f89406!important}a.text-warning:focus,a.text-warning:hover{color:#ad6704!important}.text-danger{color:#ee5f5b!important}a.text-danger:focus,a.text-danger:hover{color:#e51d18!important}.text-light{color:#e9ecef!important}a.text-light:focus,a.text-light:hover{color:#bdc6cf!important}.text-dark{color:#272b30!important}a.text-dark:focus,a.text-dark:hover{color:#050506!important}.text-body{color:#aaa!important}.text-muted{color:#7a8288!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:rgba(0,0,0,.6)}.table .thead-dark th{color:inherit;border-color:rgba(0,0,0,.6)}}.navbar{border:1px solid rgba(0,0,0,.6);text-shadow:1px 1px 1px rgba(0,0,0,.3)}.navbar .container{padding:0}.navbar .navbar-toggler{border-color:rgba(0,0,0,.6)}.navbar-fixed-top{border-width:0 0 1px}.navbar-fixed-bottom{border-width:1px 0 0}.navbar .nav-link{padding:1rem;border-left:1px solid rgba(255,255,255,.1);border-right:1px solid rgba(0,0,0,.2)}.navbar .nav-link:focus,.navbar .nav-link:hover{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,.2)}.navbar-brand{padding:.75rem 1rem calc(24px - .75rem);margin-right:0;border-right:1px solid rgba(0,0,0,.2)}.navbar .nav-item.active .nav-link{background-color:rgba(0,0,0,.3);border-left:1px solid rgba(0,0,0,.2)}.navbar-nav .nav-item+.nav-item{margin-left:0}.navbar.bg-light{text-shadow:1px 1px 1px rgba(0,0,0,.1)}.navbar.bg-light .nav-link:focus,.navbar.bg-light .nav-link:hover{background-image:linear-gradient(#4e5458,#565b60 40%,#5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,.2)}@media (max-width:576px){.navbar-expand-sm .nav-link,.navbar-expand-sm .navbar-brand{border:none!important}}@media (max-width:768px){.navbar-expand-md .nav-link,.navbar-expand-md .navbar-brand{border:none!important}}@media (max-width:992px){.navbar-expand-lg .nav-link,.navbar-expand-lg .navbar-brand{border:none!important}}.btn{border-color:rgba(0,0,0,.6);text-shadow:1px 1px 1px rgba(0,0,0,.3)}.btn.disabled,.btn:not([disabled]):not(.disabled).active{border-color:rgba(0,0,0,.6);box-shadow:none}.btn:focus,.btn:hover,.btn:not([disabled]):not(.disabled).active:hover,.btn:not([disabled]):not(.disabled):active,.btn:not([disabled]):not(.disabled):active:hover{border-color:rgba(0,0,0,.6)}.btn-primary{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-primary:not([disabled]):not(.disabled).active:hover,.btn-primary:not([disabled]):not(.disabled):active:hover,.btn-primary:not([disabled]):not(.disabled):focus,.btn-primary:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary{background-image:linear-gradient(#8a9196,#7a8288 60%,#70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary:not([disabled]):not(.disabled).active,.btn-secondary:not([disabled]):not(.disabled):active,.btn-secondary:not([disabled]):not(.disabled):focus,.btn-secondary:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#4e5458,#565b60 40%,#5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-success{background-image:linear-gradient(#78cc78,#62c462 60%,#53be53);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-success:not([disabled]):not(.disabled).active,.btn-success:not([disabled]):not(.disabled):active,.btn-success:not([disabled]):not(.disabled):focus,.btn-success:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#379337,#3b9e3b 40%,#3ea63e);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-info{background-image:linear-gradient(#74cae3,#5bc0de 60%,#4ab9db);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-info:not([disabled]):not(.disabled).active,.btn-info:not([disabled]):not(.disabled):active,.btn-info:not([disabled]):not(.disabled):focus,.btn-info:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#2596b8,#28a1c5 40%,#29a8cd);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-warning{background-image:linear-gradient(#faa123,#f89406 60%,#e48806);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-warning:not([disabled]):not(.disabled).active,.btn-warning:not([disabled]):not(.disabled):active,.btn-warning:not([disabled]):not(.disabled):focus,.btn-warning:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#9e5f04,#ad6704 40%,#b76d04);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger{background-image:linear-gradient(#f17a77,#ee5f5b 60%,#ec4d49);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger:not([disabled]):not(.disabled).active,.btn-danger:not([disabled]):not(.disabled):active,.btn-danger:not([disabled]):not(.disabled):focus,.btn-danger:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#d71c16,#e51d18 40%,#e8241f);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-outline-primary{color:#fff}.btn-link,.btn-link:hover{border-color:transparent}.btn-group .btn.active,.btn-group-vertical .btn.active{border-color:rgba(0,0,0,.6)}h1,h2,h3,h4,h5,h6{text-shadow:-1px -1px 0 rgba(0,0,0,.3)}.table-danger,.table-info,.table-primary,.table-secondary,.table-success,.table-warning{color:#fff}.table-primary,.table-primary>td,.table-primary>th{background-color:#3a3f44}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#7a8288}.table-light,.table-light>td,.table-light>th{background-color:#e9ecef}.table-dark,.table-dark>td,.table-dark>th{background-color:#272b30}.table-success,.table-success>td,.table-success>th{background-color:#62c462}.table-info,.table-info>td,.table-info>th{background-color:#5bc0de}.table-danger,.table-danger>td,.table-danger>th{background-color:#ee5f5b}.table-warning,.table-warning>td,.table-warning>th{background-color:#f89406}.table-active,.table-active>td,.table-active>th{background-color:rgba(255,255,255,.075)}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#2e3236}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#6e757b}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#dadfe4}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#1c1e22}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#4fbd4f}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#46b8da}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#ec4844}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#df8505}.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(255,255,255,.075)}legend{color:#fff}.input-group-addon{background-image:linear-gradient(#8a9196,#7a8288 60%,#70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-shadow:1px 1px 1px rgba(0,0,0,.3);color:#fff}.nav-tabs .nav-link{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,.6)}.nav-tabs .nav-link:not([disabled]):not(.disabled).active,.nav-tabs .nav-link:not([disabled]):not(.disabled):active,.nav-tabs .nav-link:not([disabled]):not(.disabled):focus,.nav-tabs .nav-link:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.nav-tabs .nav-link.disabled{border:1px solid rgba(0,0,0,.6)}.nav-tabs .nav-link,.nav-tabs .nav-link:hover{color:#fff}.nav-pills .nav-link{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,.6);text-shadow:1px 1px 1px rgba(0,0,0,.3);color:#fff}.nav-pills .nav-link:hover{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,.6)}.nav-pills .nav-link.active,.nav-pills .nav-link:hover{background-color:transparent;background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,.6)}.nav-pills .nav-link.disabled,.nav-pills .nav-link.disabled:hover{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#7a8288}.pagination .page-link{text-shadow:1px 1px 1px rgba(0,0,0,.3);background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-link:hover{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-decoration:none}.pagination .page-item.active .page-link{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-item.disabled .page-link{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb{border:1px solid rgba(0,0,0,.6);text-shadow:1px 1px 1px rgba(0,0,0,.3);background-color:transparent;background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb a,.breadcrumb a:hover{color:#fff}.alert{border:none;color:#fff}.alert .alert-link,.alert a{color:#fff;text-decoration:underline}.alert-primary{background-color:#3a3f44}.alert-secondary{background-color:#7a8288}.alert-success{background-color:#62c462}.alert-info{background-color:#5bc0de}.alert-warning{background-color:#f89406}.alert-danger{background-color:#ee5f5b}.alert-light{background-color:#e9ecef}.alert-dark{background-color:#272b30}.alert-light,.alert-light .alert-link,.alert-light a:not(.btn){color:#272b30}.alert .close{color:#000;text-decoration:none}.badge-info,.badge-success,.badge-warning{color:#fff}.jumbotron{border:1px solid rgba(0,0,0,.6)}.list-group-item-action:hover{background-color:#1c1e22} \ No newline at end of file + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#ee5f5b;--orange:#fd7e14;--yellow:#f89406;--green:#62c462;--teal:#20c997;--cyan:#5bc0de;--white:#fff;--gray:#7a8288;--gray-dark:#3a3f44;--primary:#3a3f44;--secondary:#7a8288;--success:#62c462;--info:#5bc0de;--warning:#f89406;--danger:#ee5f5b;--light:#e9ecef;--dark:#272b30;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:.9375rem;font-weight:400;line-height:1.5;color:#aaa;text-align:left;background-color:#272b30}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#fff;text-decoration:none;background-color:transparent}a:hover{color:#d9d9d9;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#7a8288;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.34375rem}.h2,h2{font-size:1.875rem}.h3,h3{font-size:1.640625rem}.h4,h4{font-size:1.40625rem}.h5,h5{font-size:1.171875rem}.h6,h6{font-size:.9375rem}.lead{font-size:1.171875rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.171875rem}.blockquote-footer{display:block;font-size:80%;color:#7a8288}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#272b30;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#7a8288}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#272b30;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:inherit}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:817px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:1066px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1318px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#fff}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid rgba(0,0,0,.6)}.table thead th{vertical-align:bottom;border-bottom:2px solid rgba(0,0,0,.6)}.table tbody+tbody{border-top:2px solid rgba(0,0,0,.6)}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid rgba(0,0,0,.6)}.table-bordered td,.table-bordered th{border:1px solid rgba(0,0,0,.6)}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#c8c9cb}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#999b9e}.table-hover .table-primary:hover{background-color:#bbbcbf}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#bbbcbf}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#dadcde}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#babec1}.table-hover .table-secondary:hover{background-color:#cdcfd2}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cdcfd2}.table-success,.table-success>td,.table-success>th{background-color:#d3eed3}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#ade0ad}.table-hover .table-success:hover{background-color:#c1e7c1}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#c1e7c1}.table-info,.table-info>td,.table-info>th{background-color:#d1edf6}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#aadeee}.table-hover .table-info:hover{background-color:#bce5f2}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#bce5f2}.table-warning,.table-warning>td,.table-warning>th{background-color:#fde1b9}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#fbc77e}.table-hover .table-warning:hover{background-color:#fcd6a0}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fcd6a0}.table-danger,.table-danger>td,.table-danger>th{background-color:#fad2d1}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#f6acaa}.table-hover .table-danger:hover{background-color:#f8bcba}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f8bcba}.table-light,.table-light>td,.table-light>th{background-color:#f9fafb}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#f4f5f7}.table-hover .table-light:hover{background-color:#eaedf1}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#eaedf1}.table-dark,.table-dark>td,.table-dark>th{background-color:#c3c4c5}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#8f9193}.table-hover .table-dark:hover{background-color:#b6b7b8}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b6b7b8}.table-active,.table-active>td,.table-active>th{background-color:rgba(255,255,255,.075)}.table-hover .table-active:hover{background-color:rgba(242,242,242,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(242,242,242,.075)}.table .thead-dark th{color:#fff;background-color:#3a3f44;border-color:rgba(0,0,0,.6)}.table .thead-light th{color:#52575c;background-color:#e9ecef;border-color:rgba(0,0,0,.6)}.table-dark{color:#fff;background-color:#3a3f44}.table-dark td,.table-dark th,.table-dark thead th{border-color:rgba(0,0,0,.6)}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + 1.5rem + 2px);padding:.75rem 1rem;font-size:.9375rem;font-weight:400;line-height:1.5;color:#52575c;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #52575c}.form-control:focus{color:#52575c;background-color:#fff;border-color:#757f89;outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.form-control::-webkit-input-placeholder{color:#7a8288;opacity:1}.form-control::-moz-placeholder{color:#7a8288;opacity:1}.form-control:-ms-input-placeholder{color:#7a8288;opacity:1}.form-control::-ms-input-placeholder{color:#7a8288;opacity:1}.form-control::placeholder{color:#7a8288;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#ccc;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#52575c;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.75rem + 1px);padding-bottom:calc(.75rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.171875rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.825rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.75rem 0;margin-bottom:0;font-size:.9375rem;line-height:1.5;color:#aaa;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.825rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#7a8288}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#62c462}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.825rem;line-height:1.5;color:#fff;background-color:rgba(98,196,98,.9);border-radius:.25rem}.form-row>.col>.valid-tooltip,.form-row>[class*=col-]>.valid-tooltip{left:5px}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#62c462;padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2362c462' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .375rem) center;background-size:calc(.75em + .75rem) calc(.75em + .75rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#62c462;box-shadow:0 0 0 .2rem rgba(98,196,98,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(.375em + .375rem) right calc(.375em + .375rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#62c462;padding-right:calc(.75em + 3.125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%233a3f44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2362c462' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 2rem/calc(.75em + .75rem) calc(.75em + .75rem) no-repeat}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#62c462;box-shadow:0 0 0 .2rem rgba(98,196,98,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#62c462}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#62c462}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#62c462}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#87d287;background-color:#87d287}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(98,196,98,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#62c462}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#62c462}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#62c462;box-shadow:0 0 0 .2rem rgba(98,196,98,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#ee5f5b}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.825rem;line-height:1.5;color:#fff;background-color:rgba(238,95,91,.9);border-radius:.25rem}.form-row>.col>.invalid-tooltip,.form-row>[class*=col-]>.invalid-tooltip{left:5px}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#ee5f5b;padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23ee5f5b' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ee5f5b' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .375rem) center;background-size:calc(.75em + .75rem) calc(.75em + .75rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#ee5f5b;box-shadow:0 0 0 .2rem rgba(238,95,91,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(.375em + .375rem) right calc(.375em + .375rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#ee5f5b;padding-right:calc(.75em + 3.125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%233a3f44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23ee5f5b' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ee5f5b' stroke='none'/%3e%3c/svg%3e") center right 2rem/calc(.75em + .75rem) calc(.75em + .75rem) no-repeat}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#ee5f5b;box-shadow:0 0 0 .2rem rgba(238,95,91,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#ee5f5b}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#ee5f5b}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#ee5f5b}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#f38c89;background-color:#f38c89}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(238,95,91,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#ee5f5b}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#ee5f5b}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#ee5f5b;box-shadow:0 0 0 .2rem rgba(238,95,91,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#aaa;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.75rem 1rem;font-size:.9375rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#aaa;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#3a3f44;border-color:#3a3f44}.btn-primary:hover{color:#fff;background-color:#282c2f;border-color:#232628}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#282c2f;border-color:#232628;box-shadow:0 0 0 .2rem rgba(88,92,96,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#3a3f44;border-color:#3a3f44}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#232628;border-color:#1d1f22}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(88,92,96,.5)}.btn-secondary{color:#fff;background-color:#7a8288;border-color:#7a8288}.btn-secondary:hover{color:#fff;background-color:#686f74;border-color:#62686d}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#686f74;border-color:#62686d;box-shadow:0 0 0 .2rem rgba(142,149,154,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#7a8288;border-color:#7a8288}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#62686d;border-color:#5c6267}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(142,149,154,.5)}.btn-success{color:#fff;background-color:#62c462;border-color:#62c462}.btn-success:hover{color:#fff;background-color:#46ba46;border-color:#42b142}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#46ba46;border-color:#42b142;box-shadow:0 0 0 .2rem rgba(122,205,122,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#62c462;border-color:#62c462}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#42b142;border-color:#3fa73f}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(122,205,122,.5)}.btn-info{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover{color:#fff;background-color:#3bb4d8;border-color:#31b0d5}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#3bb4d8;border-color:#31b0d5;box-shadow:0 0 0 .2rem rgba(116,201,227,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#2aaacf}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(116,201,227,.5)}.btn-warning{color:#fff;background-color:#f89406;border-color:#f89406}.btn-warning:hover{color:#fff;background-color:#d37e05;border-color:#c67605}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#d37e05;border-color:#c67605;box-shadow:0 0 0 .2rem rgba(249,164,43,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;background-color:#f89406;border-color:#f89406}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c67605;border-color:#ba6f04}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(249,164,43,.5)}.btn-danger{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:hover{color:#fff;background-color:#ea3d38;border-color:#e9322d}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#ea3d38;border-color:#e9322d;box-shadow:0 0 0 .2rem rgba(241,119,116,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#e9322d;border-color:#e82721}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(241,119,116,.5)}.btn-light{color:#272b30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:hover{color:#272b30;background-color:#d3d9df;border-color:#cbd3da}.btn-light.focus,.btn-light:focus{color:#272b30;background-color:#d3d9df;border-color:#cbd3da;box-shadow:0 0 0 .2rem rgba(204,207,210,.5)}.btn-light.disabled,.btn-light:disabled{color:#272b30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#272b30;background-color:#cbd3da;border-color:#c4ccd4}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(204,207,210,.5)}.btn-dark{color:#fff;background-color:#272b30;border-color:#272b30}.btn-dark:hover{color:#fff;background-color:#16181b;border-color:#101214}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#16181b;border-color:#101214;box-shadow:0 0 0 .2rem rgba(71,75,79,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#272b30;border-color:#272b30}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#101214;border-color:#0a0b0d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(71,75,79,.5)}.btn-outline-primary{color:#3a3f44;border-color:#3a3f44}.btn-outline-primary:hover{color:#fff;background-color:#3a3f44;border-color:#3a3f44}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(58,63,68,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#3a3f44;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#3a3f44;border-color:#3a3f44}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,63,68,.5)}.btn-outline-secondary{color:#7a8288;border-color:#7a8288}.btn-outline-secondary:hover{color:#fff;background-color:#7a8288;border-color:#7a8288}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(122,130,136,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#7a8288;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#7a8288;border-color:#7a8288}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(122,130,136,.5)}.btn-outline-success{color:#62c462;border-color:#62c462}.btn-outline-success:hover{color:#fff;background-color:#62c462;border-color:#62c462}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(98,196,98,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#62c462;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#62c462;border-color:#62c462}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(98,196,98,.5)}.btn-outline-info{color:#5bc0de;border-color:#5bc0de}.btn-outline-info:hover{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(91,192,222,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#5bc0de;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(91,192,222,.5)}.btn-outline-warning{color:#f89406;border-color:#f89406}.btn-outline-warning:hover{color:#fff;background-color:#f89406;border-color:#f89406}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(248,148,6,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#f89406;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#f89406;border-color:#f89406}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,148,6,.5)}.btn-outline-danger{color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:hover{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(238,95,91,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#ee5f5b;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(238,95,91,.5)}.btn-outline-light{color:#e9ecef;border-color:#e9ecef}.btn-outline-light:hover{color:#272b30;background-color:#e9ecef;border-color:#e9ecef}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(233,236,239,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#e9ecef;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#272b30;background-color:#e9ecef;border-color:#e9ecef}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(233,236,239,.5)}.btn-outline-dark{color:#272b30;border-color:#272b30}.btn-outline-dark:hover{color:#fff;background-color:#272b30;border-color:#272b30}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(39,43,48,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#272b30;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#272b30;border-color:#272b30}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(39,43,48,.5)}.btn-link{font-weight:400;color:#fff;text-decoration:none}.btn-link:hover{color:#d9d9d9;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#7a8288;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.825rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:.9375rem;color:#aaa;text-align:left;list-style:none;background-color:#3a3f44;background-clip:padding-box;border:1px solid rgba(0,0,0,.6);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#aaa;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#fff;text-decoration:none;background-color:#272b30}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#272b30}.dropdown-item.disabled,.dropdown-item:disabled{color:#999;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.825rem;color:#7a8288;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#aaa}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:first-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label::after,.input-group:not(.has-validation)>.custom-select:not(:last-child),.input-group:not(.has-validation)>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label::after,.input-group.has-validation>.custom-select:nth-last-child(n+3),.input-group.has-validation>.form-control:nth-last-child(n+3){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.75rem 1rem;margin-bottom:0;font-size:.9375rem;font-weight:400;line-height:1.5;color:#52575c;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.825rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:2rem}.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.btn,.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.input-group-text,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.btn,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.40625rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.203125rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#3a3f44;background-color:#3a3f44}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#757f89}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#9098a0;border-color:#9098a0}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#7a8288}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#ccc}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#999 solid 1px}.custom-control-label::after{position:absolute;top:.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:50%/50% 50% no-repeat}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#3a3f44;background-color:#3a3f44}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(58,63,68,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(58,63,68,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(58,63,68,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.203125rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#999;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(58,63,68,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + 1.5rem + 2px);padding:.75rem 2rem .75rem 1rem;font-size:.9375rem;font-weight:400;line-height:1.5;color:#52575c;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%233a3f44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 1rem center/8px 10px no-repeat;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#757f89;outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.custom-select:focus::-ms-value{color:#52575c;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:1rem;background-image:none}.custom-select:disabled{color:#7a8288;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #52575c}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.825rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.171875rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + 1.5rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + 1.5rem + 2px);margin:0;overflow:hidden;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#757f89;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#ccc}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + 1.5rem + 2px);padding:.75rem 1rem;overflow:hidden;font-weight:400;line-height:1.5;color:#52575c;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + 1.5rem);padding:.75rem 1rem;line-height:1.5;color:#52575c;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #272b30,0 0 0 .2rem rgba(58,63,68,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #272b30,0 0 0 .2rem rgba(58,63,68,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #272b30,0 0 0 .2rem rgba(58,63,68,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#3a3f44;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#9098a0}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#3a3f44;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#9098a0}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#3a3f44;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#9098a0}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#999}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#999}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#999}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#7a8288;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid rgba(0,0,0,.6)}.nav-tabs .nav-link{margin-bottom:-1px;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:rgba(0,0,0,.6)}.nav-tabs .nav-link.disabled{color:#7a8288;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#fff;background-color:#272b30;border-color:rgba(0,0,0,.6)}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#3a3f44}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:0 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.324219rem;padding-bottom:.324219rem;margin-right:1rem;font-size:1.171875rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.171875rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:50%/100% 100% no-repeat}.navbar-nav-scroll{max-height:75vh;overflow-y:auto}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:#3a3f44}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:#3a3f44}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:#3a3f44}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:#3a3f44}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:#3a3f44}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:#3a3f44}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:#fff}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#32383e;background-clip:border-box;border:1px solid rgba(0,0,0,.6);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:#515960;border-bottom:1px solid rgba(0,0,0,.6)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:#515960;border-top:1px solid rgba(0,0,0,.6)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#7a8288;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#999}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#fff;background-color:transparent;border:1px solid rgba(0,0,0,.6)}.page-link:hover{z-index:2;color:#fff;text-decoration:none;background-color:transparent;border-color:rgba(0,0,0,.6)}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:transparent;border-color:rgba(0,0,0,.6)}.page-item.disabled .page-link{color:#7a8288;pointer-events:none;cursor:auto;background-color:transparent;border-color:rgba(0,0,0,.6)}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.171875rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.825rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#3a3f44}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#232628}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(58,63,68,.5)}.badge-secondary{color:#fff;background-color:#7a8288}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#62686d}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(122,130,136,.5)}.badge-success{color:#fff;background-color:#62c462}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#42b142}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(98,196,98,.5)}.badge-info{color:#fff;background-color:#5bc0de}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#31b0d5}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(91,192,222,.5)}.badge-warning{color:#fff;background-color:#f89406}a.badge-warning:focus,a.badge-warning:hover{color:#fff;background-color:#c67605}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,148,6,.5)}.badge-danger{color:#fff;background-color:#ee5f5b}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#e9322d}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(238,95,91,.5)}.badge-light{color:#272b30;background-color:#e9ecef}a.badge-light:focus,a.badge-light:hover{color:#272b30;background-color:#cbd3da}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(233,236,239,.5)}.badge-dark{color:#fff;background-color:#272b30}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#101214}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(39,43,48,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#1c1e22;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#1e2123;background-color:#d8d9da;border-color:#c8c9cb}.alert-primary hr{border-top-color:#bbbcbf}.alert-primary .alert-link{color:#060708}.alert-secondary{color:#3f4447;background-color:#e4e6e7;border-color:#dadcde}.alert-secondary hr{border-top-color:#cdcfd2}.alert-secondary .alert-link{color:#272a2c}.alert-success{color:#363;background-color:#e0f3e0;border-color:#d3eed3}.alert-success hr{border-top-color:#c1e7c1}.alert-success .alert-link{color:#242}.alert-info{color:#2f6473;background-color:#def2f8;border-color:#d1edf6}.alert-info hr{border-top-color:#bce5f2}.alert-info .alert-link{color:#20454f}.alert-warning{color:#814d03;background-color:#feeacd;border-color:#fde1b9}.alert-warning hr{border-top-color:#fcd6a0}.alert-warning .alert-link{color:#4f2f02}.alert-danger{color:#7c312f;background-color:#fcdfde;border-color:#fad2d1}.alert-danger hr{border-top-color:#f8bcba}.alert-danger .alert-link{color:#572221}.alert-light{color:#797b7c;background-color:#fbfbfc;border-color:#f9fafb}.alert-light hr{border-top-color:#eaedf1}.alert-light .alert-link{color:#606162}.alert-dark{color:#141619;background-color:#d4d5d6;border-color:#c3c4c5}.alert-dark hr{border-top-color:#b6b7b8}.alert-dark .alert-link{color:#000}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.703125rem;background-color:#1c1e22;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#7a8288;text-align:center;white-space:nowrap;background-color:#3a3f44;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#fff;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#fff;text-decoration:none;background-color:#3e444c}.list-group-item-action:active{color:#aaa;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#32383e;border:1px solid rgba(0,0,0,.6)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#52575c;pointer-events:none;background-color:#32383e}.list-group-item.active{z-index:2;color:#fff;background-color:#3e444c;border-color:rgba(0,0,0,.6)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#1e2123;background-color:#c8c9cb}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#1e2123;background-color:#bbbcbf}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#1e2123;border-color:#1e2123}.list-group-item-secondary{color:#3f4447;background-color:#dadcde}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#3f4447;background-color:#cdcfd2}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#3f4447;border-color:#3f4447}.list-group-item-success{color:#363;background-color:#d3eed3}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#363;background-color:#c1e7c1}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#363;border-color:#363}.list-group-item-info{color:#2f6473;background-color:#d1edf6}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#2f6473;background-color:#bce5f2}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#2f6473;border-color:#2f6473}.list-group-item-warning{color:#814d03;background-color:#fde1b9}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#814d03;background-color:#fcd6a0}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#814d03;border-color:#814d03}.list-group-item-danger{color:#7c312f;background-color:#fad2d1}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#7c312f;background-color:#f8bcba}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#7c312f;border-color:#7c312f}.list-group-item-light{color:#797b7c;background-color:#f9fafb}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#797b7c;background-color:#eaedf1}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#797b7c;border-color:#797b7c}.list-group-item-dark{color:#141619;background-color:#c3c4c5}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#b6b7b8}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.close{float:right;font-size:1.40625rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#aaa;background-color:#32383e;background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid rgba(0,0,0,.2);border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.825rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.825rem;word-wrap:break-word;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#32383e}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#32383e}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#32383e}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #2c3036}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#32383e}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:.9375rem;background-color:#2c3036;border-bottom:1px solid #202328;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#aaa}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:50%/100% 100% no-repeat}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#3a3f44!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#232628!important}.bg-secondary{background-color:#7a8288!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#62686d!important}.bg-success{background-color:#62c462!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#42b142!important}.bg-info{background-color:#5bc0de!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#31b0d5!important}.bg-warning{background-color:#f89406!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#c67605!important}.bg-danger{background-color:#ee5f5b!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#e9322d!important}.bg-light{background-color:#e9ecef!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#cbd3da!important}.bg-dark{background-color:#272b30!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#101214!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#3a3f44!important}.border-secondary{border-color:#7a8288!important}.border-success{border-color:#62c462!important}.border-info{border-color:#5bc0de!important}.border-warning{border-color:#f89406!important}.border-danger{border-color:#ee5f5b!important}.border-light{border-color:#e9ecef!important}.border-dark{border-color:#272b30!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#3a3f44!important}a.text-primary:focus,a.text-primary:hover{color:#17191b!important}.text-secondary{color:#7a8288!important}a.text-secondary:focus,a.text-secondary:hover{color:#565b60!important}.text-success{color:#62c462!important}a.text-success:focus,a.text-success:hover{color:#3b9e3b!important}.text-info{color:#5bc0de!important}a.text-info:focus,a.text-info:hover{color:#28a1c5!important}.text-warning{color:#f89406!important}a.text-warning:focus,a.text-warning:hover{color:#ad6704!important}.text-danger{color:#ee5f5b!important}a.text-danger:focus,a.text-danger:hover{color:#e51d18!important}.text-light{color:#e9ecef!important}a.text-light:focus,a.text-light:hover{color:#bdc6cf!important}.text-dark{color:#272b30!important}a.text-dark:focus,a.text-dark:hover{color:#050506!important}.text-body{color:#aaa!important}.text-muted{color:#7a8288!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:rgba(0,0,0,.6)}.table .thead-dark th{color:inherit;border-color:rgba(0,0,0,.6)}}.navbar{border:1px solid rgba(0,0,0,.6);text-shadow:1px 1px 1px rgba(0,0,0,.3)}.navbar .container{padding:0}.navbar .navbar-toggler{border-color:rgba(0,0,0,.6)}.navbar-fixed-top{border-width:0 0 1px}.navbar-fixed-bottom{border-width:1px 0 0}.navbar .nav-link{padding:1rem;border-left:1px solid rgba(255,255,255,.1);border-right:1px solid rgba(0,0,0,.2)}.navbar .nav-link:focus,.navbar .nav-link:hover{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,.2)}.navbar-brand{padding:.75rem 1rem calc(24px - .75rem);margin-right:0;border-right:1px solid rgba(0,0,0,.2)}.navbar .nav-item.active .nav-link{background-color:rgba(0,0,0,.3);border-left:1px solid rgba(0,0,0,.2)}.navbar-nav .nav-item+.nav-item{margin-left:0}.navbar.bg-light{text-shadow:1px 1px 1px rgba(0,0,0,.1)}.navbar.bg-light .nav-link:focus,.navbar.bg-light .nav-link:hover{background-image:linear-gradient(#4e5458,#565b60 40%,#5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,.2)}@media (max-width:576px){.navbar-expand-sm .nav-link,.navbar-expand-sm .navbar-brand{border:none!important}}@media (max-width:768px){.navbar-expand-md .nav-link,.navbar-expand-md .navbar-brand{border:none!important}}@media (max-width:992px){.navbar-expand-lg .nav-link,.navbar-expand-lg .navbar-brand{border:none!important}}.btn{border-color:rgba(0,0,0,.6);text-shadow:1px 1px 1px rgba(0,0,0,.3)}.btn.disabled,.btn:not([disabled]):not(.disabled).active{border-color:rgba(0,0,0,.6);box-shadow:none}.btn:focus,.btn:hover,.btn:not([disabled]):not(.disabled).active:hover,.btn:not([disabled]):not(.disabled):active,.btn:not([disabled]):not(.disabled):active:hover{border-color:rgba(0,0,0,.6)}.btn-primary{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-primary:not([disabled]):not(.disabled).active:hover,.btn-primary:not([disabled]):not(.disabled):active:hover,.btn-primary:not([disabled]):not(.disabled):focus,.btn-primary:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary{background-image:linear-gradient(#8a9196,#7a8288 60%,#70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary:not([disabled]):not(.disabled).active,.btn-secondary:not([disabled]):not(.disabled):active,.btn-secondary:not([disabled]):not(.disabled):focus,.btn-secondary:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#4e5458,#565b60 40%,#5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-success{background-image:linear-gradient(#78cc78,#62c462 60%,#53be53);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-success:not([disabled]):not(.disabled).active,.btn-success:not([disabled]):not(.disabled):active,.btn-success:not([disabled]):not(.disabled):focus,.btn-success:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#379337,#3b9e3b 40%,#3ea63e);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-info{background-image:linear-gradient(#74cae3,#5bc0de 60%,#4ab9db);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-info:not([disabled]):not(.disabled).active,.btn-info:not([disabled]):not(.disabled):active,.btn-info:not([disabled]):not(.disabled):focus,.btn-info:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#2596b8,#28a1c5 40%,#29a8cd);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-warning{background-image:linear-gradient(#faa123,#f89406 60%,#e48806);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-warning:not([disabled]):not(.disabled).active,.btn-warning:not([disabled]):not(.disabled):active,.btn-warning:not([disabled]):not(.disabled):focus,.btn-warning:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#9e5f04,#ad6704 40%,#b76d04);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger{background-image:linear-gradient(#f17a77,#ee5f5b 60%,#ec4d49);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger:not([disabled]):not(.disabled).active,.btn-danger:not([disabled]):not(.disabled):active,.btn-danger:not([disabled]):not(.disabled):focus,.btn-danger:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#d71c16,#e51d18 40%,#e8241f);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-outline-primary{color:#fff}.btn-link,.btn-link:hover{border-color:transparent}.btn-group .btn.active,.btn-group-vertical .btn.active{border-color:rgba(0,0,0,.6)}h1,h2,h3,h4,h5,h6{text-shadow:-1px -1px 0 rgba(0,0,0,.3)}.table-danger,.table-info,.table-primary,.table-secondary,.table-success,.table-warning{color:#fff}.table-primary,.table-primary>td,.table-primary>th{background-color:#3a3f44}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#7a8288}.table-light,.table-light>td,.table-light>th{background-color:#e9ecef}.table-dark,.table-dark>td,.table-dark>th{background-color:#272b30}.table-success,.table-success>td,.table-success>th{background-color:#62c462}.table-info,.table-info>td,.table-info>th{background-color:#5bc0de}.table-danger,.table-danger>td,.table-danger>th{background-color:#ee5f5b}.table-warning,.table-warning>td,.table-warning>th{background-color:#f89406}.table-active,.table-active>td,.table-active>th{background-color:rgba(255,255,255,.075)}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#2e3236}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#6e757b}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#dadfe4}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#1c1e22}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#4fbd4f}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#46b8da}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#ec4844}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#df8505}.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(255,255,255,.075)}legend{color:#fff}.input-group-addon{background-image:linear-gradient(#8a9196,#7a8288 60%,#70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-shadow:1px 1px 1px rgba(0,0,0,.3);color:#fff}.nav-tabs .nav-link{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,.6)}.nav-tabs .nav-link:not([disabled]):not(.disabled).active,.nav-tabs .nav-link:not([disabled]):not(.disabled):active,.nav-tabs .nav-link:not([disabled]):not(.disabled):focus,.nav-tabs .nav-link:not([disabled]):not(.disabled):hover{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.nav-tabs .nav-link.disabled{border:1px solid rgba(0,0,0,.6)}.nav-tabs .nav-link,.nav-tabs .nav-link:hover{color:#fff}.nav-pills .nav-link{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,.6);text-shadow:1px 1px 1px rgba(0,0,0,.3);color:#fff}.nav-pills .nav-link:hover{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,.6)}.nav-pills .nav-link.active,.nav-pills .nav-link:hover{background-color:transparent;background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,.6)}.nav-pills .nav-link.disabled,.nav-pills .nav-link.disabled:hover{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#7a8288}.pagination .page-link{text-shadow:1px 1px 1px rgba(0,0,0,.3);background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-link:hover{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-decoration:none}.pagination .page-item.active .page-link{background-image:linear-gradient(#101112,#17191b 40%,#1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-item.disabled .page-link{background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb{border:1px solid rgba(0,0,0,.6);text-shadow:1px 1px 1px rgba(0,0,0,.3);background-color:transparent;background-image:linear-gradient(#484e55,#3a3f44 60%,#313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb a,.breadcrumb a:hover{color:#fff}.alert{border:none;color:#fff}.alert .alert-link,.alert a{color:#fff;text-decoration:underline}.alert-primary{background-color:#3a3f44}.alert-secondary{background-color:#7a8288}.alert-success{background-color:#62c462}.alert-info{background-color:#5bc0de}.alert-warning{background-color:#f89406}.alert-danger{background-color:#ee5f5b}.alert-light{background-color:#e9ecef}.alert-dark{background-color:#272b30}.alert-light,.alert-light .alert-link,.alert-light a:not(.btn){color:#272b30}.alert .close{color:#000;text-decoration:none}.badge-info,.badge-success,.badge-warning{color:#fff}.jumbotron{border:1px solid rgba(0,0,0,.6)}.list-group-item-action:hover{background-color:#1c1e22} diff --git a/views/css/component.css b/views/css/component.css index b0e5d30..d96769f 100755 --- a/views/css/component.css +++ b/views/css/component.css @@ -1,94 +1,94 @@ .cbp-fwslider { - position: relative; - margin: 0 0 5px; - overflow: hidden; - padding: 5px 0 26px; + position: relative; + margin: 0 0 5px; + overflow: hidden; + padding: 5px 0 26px; } .cbp-fwslider ul { - margin: 0; - padding: 0; - white-space: nowrap; - list-style-type: none; + margin: 0; + padding: 0; + white-space: nowrap; + list-style-type: none; } .cbp-fwslider ul li { - -webkit-transform: translateZ(0); - -moz-transform: translateZ(0); - transform: translateZ(0); - float: left; - display: block; - margin: 0; - padding: 0; + -webkit-transform: translateZ(0); + -moz-transform: translateZ(0); + transform: translateZ(0); + float: left; + display: block; + margin: 0; + padding: 0; } .cbp-fwslider ul li > a, .cbp-fwslider ul li > div { - display: block; - text-align: center; - outline: none; + display: block; + text-align: center; + outline: none; } .cbp-fwslider ul li > a img { - border: none; - display: block; - margin: 0 auto; - max-width: 75%; + border: none; + display: block; + margin: 0 auto; + max-width: 75%; } .cbp-fwslider nav span { - position: absolute; - top: 50%; - width: 50px; - height: 100px; - background: #47a3da; - color: #fff; - font-size: 50px; - text-align: center; - margin-top: -50px; - line-height: 100px; - cursor: pointer; - font-weight: normal; + position: absolute; + top: 50%; + width: 50px; + height: 100px; + background: #47a3da; + color: #fff; + font-size: 50px; + text-align: center; + margin-top: -50px; + line-height: 100px; + cursor: pointer; + font-weight: normal; } .cbp-fwslider nav span:hover { - background: #378fc3; + background: #378fc3; } .cbp-fwslider nav span.cbp-fwnext { - right: 0px; + right: 0px; } .cbp-fwslider nav span.cbp-fwprev { - left: 0px; + left: 0px; } .cbp-fwdots { - position: absolute; - bottom: 0px; - white-space: nowrap; - text-align: center; - width: 100%; + position: absolute; + bottom: 0px; + white-space: nowrap; + text-align: center; + width: 100%; } .cbp-fwdots span { - display: inline-block; - width: 18px; - height: 18px; - background: #ddd; - margin: 4px; - border-radius: 50%; - cursor: pointer; + display: inline-block; + width: 18px; + height: 18px; + background: #ddd; + margin: 4px; + border-radius: 50%; + cursor: pointer; } .cbp-fwdots span:hover { - background: #999; + background: #999; } .cbp-fwdots span.cbp-fwcurrent { - background: #47a3da; - box-shadow: 0 0 0 2px #47a3da; - -webkit-transition: box-shadow 0.2s ease-in-out; - -moz-transition: box-shadow 0.2s ease-in-out; - transition: box-shadow 0.2s ease-in-out; -} \ No newline at end of file + background: #47a3da; + box-shadow: 0 0 0 2px #47a3da; + -webkit-transition: box-shadow 0.2s ease-in-out; + -moz-transition: box-shadow 0.2s ease-in-out; + transition: box-shadow 0.2s ease-in-out; +} diff --git a/views/css/main.css b/views/css/main.css index 16ab0e2..ed5de98 100644 --- a/views/css/main.css +++ b/views/css/main.css @@ -1,11 +1,14 @@ .browse-file, .browse-folder, -a { cursor: pointer; } +a { + cursor: pointer; +} .card-header { - padding: .4rem 0.75rem; + padding: 0.4rem 0.75rem; } -.btn-floating, [class*="btn-outline-"].btn-floating { +.btn-floating, +[class*="btn-outline-"].btn-floating { border-radius: 50px; position: relative; } @@ -14,10 +17,10 @@ a { cursor: pointer; } font-weight: bold; } .btn-md { - padding: .4rem .75rem; + padding: 0.4rem 0.75rem; } .btn-success { - background: #7C4; + background: #7c4; } .btn-success:hover { background: #46ba46; @@ -67,7 +70,9 @@ pre { overflow: visible; } -.modal { overflow: auto !important; } +.modal { + overflow: auto !important; +} .modal-content { box-shadow: 0px 0px 20pt black; @@ -78,7 +83,7 @@ pre { } .table-hover tbody tr:hover { - background-color: rgba(255,255,255,0.25); + background-color: rgba(255, 255, 255, 0.25); } .ribbon-img-container { @@ -146,7 +151,7 @@ pre { }*/ .content_container { - display:flex; + display: flex; flex-direction: column; height: 100%; width: 100%; @@ -171,18 +176,18 @@ pre { #mainbody { /*margin-top: 110px;*/ padding: 0 20px; - flex:1; + flex: 1; } .listitem.badge { - max-width:100%; - width:100%; - flex:auto; + max-width: 100%; + width: 100%; + flex: auto; padding: 1rem; margin: 0 1rem 1rem 1rem; - white-space:normal; - font-size:100%; - line-height:18pt; + white-space: normal; + font-size: 100%; + line-height: 18pt; } #sideloadQueueModal.fade .modal-dialog { @@ -201,7 +206,6 @@ pre { margin: 0; } - #showQueue { position: fixed; width: 52px; @@ -225,11 +229,11 @@ pre { padding: 0; height: 300pt; overflow: auto; - border-top: 1px solid rgba(0,0,0,0.4); + border-top: 1px solid rgba(0, 0, 0, 0.4); } #sideloadQueueModal th { - background: #32383E; + background: #32383e; position: sticky; top: -1px; } @@ -238,15 +242,15 @@ pre { .table th, .table thead th { vertical-align: middle; - border-bottom: 1px solid rgba(0,0,0,.4); + border-bottom: 1px solid rgba(0, 0, 0, 0.4); border-top: none; } html { - overflow-y: scroll; + overflow-y: scroll; } - /* width */ +/* width */ ::-webkit-scrollbar { width: 6pt; height: 6pt; @@ -258,7 +262,7 @@ html { border-radius: 4px; } ::-webkit-scrollbar-corner { - background: rgba(0,0,0,0); + background: rgba(0, 0, 0, 0); } /* Handle */ ::-webkit-scrollbar-thumb { diff --git a/views/device_info.twig b/views/device_info.twig index c17fefa..b436acd 100644 --- a/views/device_info.twig +++ b/views/device_info.twig @@ -6,20 +6,13 @@ clearTimeout(timer); if (!remote.getGlobal('adbDevice')) { id('deviceBatteryCharge').innerHTML = 'unknown'; - return ; + return; } timer = setTimeout(() => ipcRenderer.send('get_device_info', ''), 30000); console.log('get_device_info ! ', arg); - const { - storage, - user, - fw, - battery, - wifi, - ip, - } = arg; + const { storage, user, fw, battery, wifi, ip } = arg; if (storage) { const percent = +storage.percent.replace('%', ''); const success = Math.min(80, percent); @@ -34,35 +27,37 @@ <div class="progress-bar progress-bar-striped bg-warning" role="progressbar" style="width: ${warning}%" aria-valuenow="${warning}" aria-valuemin="0" aria-valuemax="100"></div> <div class="progress-bar progress-bar-striped bg-danger" role="progressbar" style="width: ${danger}%" aria-valuenow="${danger}" aria-valuemin="0" aria-valuemax="100"></div> </div>`; - } - else { + } else { id('storageDiv').innerHTML = 'Can`t get storage status'; } - let bat_charge = 'none'; let bat_note = ''; let bat_level = 'XX'; if (battery) { bat_level = battery.level; - if (battery.temperature) bat_note = `Temperature: ${battery.temperature / 10}°C`; + if (battery.temperature) + bat_note = `Temperature: ${battery.temperature / 10}°C`; if (battery.ACpowered) bat_charge = 'AC'; if (battery.USBpowered) bat_charge = 'USB'; if (battery.Wirelesspowered) bat_charge = 'AIR'; if (battery.Maxchargingcurrent && battery.Maxchargingvoltage) { const current = battery.Maxchargingcurrent / 1000000; const voltage = battery.Maxchargingvoltage / 1000000; - bat_note+= `\nMax Current: ${current}A\nMax Voltage: ${voltage}V\nMax Power: ${current * voltage}W`; + bat_note += `\nMax Current: ${current}A\nMax Voltage: ${voltage}V\nMax Power: ${current * voltage}W`; } } - id('deviceUserName').innerHTML = user && user.name || '<i>Unknown</i>'; - id('deviceFwVersion').innerText = fw && fw.version || 'v.XX'; + id('deviceUserName').innerHTML = (user && user.name) || '<i>Unknown</i>'; + id('deviceFwVersion').innerText = (fw && fw.version) || 'v.XX'; id('deviceBatteryLevel').innerText = bat_level; id('deviceBatteryLevel').parentNode.title = bat_note; id('deviceBatteryCharge').innerText = bat_charge; id('deviceWifi').innerText = wifi ? 'On' : 'Off'; - id('deviceIp').innerText = (wifi && ip) || remote.getGlobal('currentConfiguration').lastIp || 'X.X.X.X'; + id('deviceIp').innerText = + (wifi && ip) || + remote.getGlobal('currentConfiguration').lastIp || + 'X.X.X.X'; }); </script> @@ -70,32 +65,32 @@ <div class="row col-8"> <div class="col-3"> <div> - <i class="fa fa-user-circle-o"></i> User: - <br/><small id="deviceUserName">Unknown</small> + <i class="fa fa-user-circle-o"></i> User: <br> + <small + id="deviceUserName" + >Unknown</small + > </div> </div> <div class="col-3"> <div> - <i class="fa fa-tag"></i> FW: - <br/><span id="deviceFwVersion">v.XX</span> + <i class="fa fa-tag"></i> FW: <br> + <span id="deviceFwVersion">v.XX</span> </div> </div> <div class="col-3"> <div> <i class="fa fa-battery-3"></i> - <span id="deviceBatteryLevel">XX</span>% - <br/><small> - Charge: <span id="deviceBatteryCharge"></span> - - </small> + <span id="deviceBatteryLevel">XX</span>% <br> + <small> Charge: <span id="deviceBatteryCharge"></span> </small> </div> </div> <div class="col-3"> <div> <i class="fa fa-wifi"></i> <span id="deviceWifi">Off</span> - <br/><small>IP: <span id="deviceIp">X.X.X.X</span> - </small> + <br> + <small>IP: <span id="deviceIp">X.X.X.X</span> </small> </div> </div> </div> diff --git a/views/error.twig b/views/error.twig index 18f1ba0..e6ea603 100644 --- a/views/error.twig +++ b/views/error.twig @@ -1,32 +1,22 @@ {% extends 'layout.twig' %} -{% block onload %} -{% endblock %} +{% block onload %}{% endblock %} -{% block navbar %} -{% endblock %} +{% block navbar %}{% endblock %} {% block body %} - - - <div class="card text-white bg-danger"> - <div class="card-header">Error</div> - <div class="card-body"> - <div class="card-text"> - - {#<h1>message</h1>#} - <h2>{{error.status}}</h2> - <pre>{{error.stack}}</pre> - - <pre>{{message}}</pre> - - <a href="index.twig" class="btn btn-info">Try again</a> - - </div> - </div> + <div class="card text-white bg-danger"> + <div class="card-header">Error</div> + <div class="card-body"> + <div class="card-text"> + {# <h1>message</h1> #} + <h2>{{ error.status }}</h2> + <pre>{{error.stack}}</pre> + + <pre>{{message}}</pre> + + <a href="index.twig" class="btn btn-info">Try again</a> + </div> </div> - - - - + </div> {% endblock %} diff --git a/views/index.twig b/views/index.twig index d7b260b..59255ae 100644 --- a/views/index.twig +++ b/views/index.twig @@ -1,136 +1,141 @@ {% extends 'layout.twig' %} {% block body %} - -<div id="mainbody"> - - <div class="card text-white bg-primary"> - <div class="card-header"> - <h4 class="mb-0"> - <i class="fa fa-list-ul"></i> System check - </h4> - </div> - <div class="card-body"> - <h4> - Platform: {{ platform }} {{ arch }} - </h4> - <h4> - Temp Dir: {{ tmpdir }} - </h4> - <h4> - Mount Dir: {{ mountFolder }} - </h4> - <h4> - <a class="btn btn-sm" onclick="shell.openPath(remote.getGlobal('sidenoderHome'))"><i class="fa fa-home"></i></a> Sidenoder Dir: {{ sidenoderHome }} - </h4> - <h4> - <div class="badge badge-warning text-left" id="adbCheckIndicator">Android Debug Bridge - checking..</div> - </h4> - <h4> - <div class="badge badge-warning text-left" id="rcloneCheckIndicator">RCLONE - checking..</div> - </h4> - <h4> - <div class="badge badge-warning text-left" id="zipCheckIndicator">7zip Archiver - checking..</div> - </h4> - <h4> - <div class="badge badge-warning text-left" id="scrcpyCheckIndicator">SCRCPY - checking..</div> - </h4> + <div id="mainbody"> + <div class="card text-white bg-primary"> + <div class="card-header"> + <h4 class="mb-0"><i class="fa fa-list-ul"></i> System check</h4> + </div> + <div class="card-body"> + <h4> + Platform: {{ platform }} + {{ arch }} + </h4> + <h4>Temp Dir: {{ tmpdir }}</h4> + <h4>Mount Dir: {{ mountFolder }}</h4> + <h4> + <a + class="btn btn-sm" + onclick="shell.openPath(remote.getGlobal('sidenoderHome'))" + ><i class="fa fa-home"></i + ></a> + Sidenoder Dir: {{ sidenoderHome }} + </h4> + <h4> + <div class="badge badge-warning text-left" id="adbCheckIndicator"> + Android Debug Bridge - checking.. + </div> + </h4> + <h4> + <div class="badge badge-warning text-left" id="rcloneCheckIndicator"> + RCLONE - checking.. + </div> + </h4> + <h4> + <div class="badge badge-warning text-left" id="zipCheckIndicator"> + 7zip Archiver - checking.. + </div> + </h4> + <h4> + <div class="badge badge-warning text-left" id="scrcpyCheckIndicator"> + SCRCPY - checking.. + </div> + </h4> + </div> </div> </div> -</div> -<script> - console.log('ONLOAD INDEX'); - console.log('sending check_deps'); + <script> + console.log('ONLOAD INDEX'); + console.log('sending check_deps'); - ipcRenderer.send('check_deps', 'adb'); - ipcRenderer.send('check_deps', 'rclone'); - ipcRenderer.send('check_deps', 'zip'); - ipcRenderer.send('check_deps', 'scrcpy'); + ipcRenderer.send('check_deps', 'adb'); + ipcRenderer.send('check_deps', 'rclone'); + ipcRenderer.send('check_deps', 'zip'); + ipcRenderer.send('check_deps', 'scrcpy'); - ipcRenderer.removeAllListeners('check_deps'); - ipcRenderer.on('check_deps', (event, arg) => { - console.log('check_deps msg came: ', arg); // prints "ping" - const { adb, rclone, zip, scrcpy } = arg; - if (adb) { - const el = $id('adbCheckIndicator'); - if (adb.version) { - el - .removeClass('badge-warning') - .addClass('badge-success') - .html(`<i class="fa fa-check-circle-o"></i> ADB Instaled (${adb.cmd}) + ipcRenderer.removeAllListeners('check_deps'); + ipcRenderer.on('check_deps', (event, arg) => { + console.log('check_deps msg came: ', arg); // prints "ping" + const { adb, rclone, zip, scrcpy } = arg; + if (adb) { + const el = $id('adbCheckIndicator'); + if (adb.version) { + el.removeClass('badge-warning').addClass('badge-success') + .html(`<i class="fa fa-check-circle-o"></i> ADB Instaled (${adb.cmd}) <br/><pre style="font-size: x-small;">${adb.version}</pre>`); + } else { + el.removeClass('badge-warning') + .addClass('badge-danger') + .html( + `<i class="fa fa-times-circle"></i> ADB Failed. Maybe global installation will be fixed this issue`, + ); + if (adb.error) + el.append( + `<br/><pre style="font-size: x-small;">error: \n${adb.error}</pre>`, + ); + } } - else { - el - .removeClass('badge-warning') - .addClass('badge-danger') - .html(`<i class="fa fa-times-circle"></i> ADB Failed. Maybe global installation will be fixed this issue`); - if (adb.error) el.append(`<br/><pre style="font-size: x-small;">error: \n${adb.error}</pre>`); - } - } - if (rclone) { - const el = $id('rcloneCheckIndicator'); - if (rclone.version) { - el - .removeClass('badge-warning') - .addClass('badge-success') - .html(`<i class="fa fa-check-circle-o"></i> RCLONE Installed (${rclone.cmd}) + if (rclone) { + const el = $id('rcloneCheckIndicator'); + if (rclone.version) { + el.removeClass('badge-warning').addClass('badge-success') + .html(`<i class="fa fa-check-circle-o"></i> RCLONE Installed (${rclone.cmd}) <br/><pre style="font-size: x-small;">${rclone.version}</pre>`); - if (remote.getGlobal('currentConfiguration').autoMount) checkMount(); - } - else { - el - .removeClass('badge-warning') - .addClass('badge-danger') - .html(`<i class="fa fa-times-circle-o"></i> + if (remote.getGlobal('currentConfiguration').autoMount) checkMount(); + } else { + el.removeClass('badge-warning').addClass('badge-danger') + .html(`<i class="fa fa-times-circle-o"></i> Can't find RCLONE - try to manualy download latest <a class="btn btn-sm btn-info" onclick="shell.openExternal('https://downloads.rclone.org/')">RClone</a> and set custom location at settings`); - if (rclone.error) el.append(`<br/><pre style="font-size: x-small;">error: \n${rclone.error}</pre>`); + if (rclone.error) + el.append( + `<br/><pre style="font-size: x-small;">error: \n${rclone.error}</pre>`, + ); + } } - } - if (zip) { - const el = $id('zipCheckIndicator'); - if (zip.version) { - el - .removeClass('badge-warning') - .addClass('badge-success') - .html(`<i class="fa fa-check-circle-o"></i> 7zip Archiver installed (${zip.cmd}) + if (zip) { + const el = $id('zipCheckIndicator'); + if (zip.version) { + el.removeClass('badge-warning').addClass('badge-success') + .html(`<i class="fa fa-check-circle-o"></i> 7zip Archiver installed (${zip.cmd}) <br/><pre style="font-size: x-small;">${zip.version}</pre>`); - } - else { - el - .removeClass('badge-warning') - .addClass('badge-danger') - .html(`<i class="fa fa-times-circle-o"></i> + } else { + el.removeClass('badge-warning').addClass('badge-danger') + .html(`<i class="fa fa-times-circle-o"></i> Can't find 7zip Archiver - try to manualy download latest <a class="btn btn-sm btn-info" onclick="shell.openExternal('https://www.7-zip.org/download.html')">7ZIP</a> and set custom location at settings`); - if (zip.error) el.append(`<br/><pre style="font-size: x-small;">error: \n${zip.error}</pre>`); + if (zip.error) + el.append( + `<br/><pre style="font-size: x-small;">error: \n${zip.error}</pre>`, + ); + } } - } - if (scrcpy) { - const el = $id('scrcpyCheckIndicator'); - if (scrcpy.version) { - el - .removeClass('badge-warning') - .addClass('badge-success') - .html(`<i class="fa fa-check-circle-o"></i> SCRCPY Installed (${scrcpy.cmd})<br/><pre style="font-size: x-small;">${scrcpy.version}</pre>`); - } - else { - const tag = remote.getGlobal('platform') - .replace('win', 'windows') - .replace('mac', 'macos'); - el - .removeClass('badge-warning') - .addClass('badge-danger') - .html(`<i class="fa fa-times-circle-o"></i> + if (scrcpy) { + const el = $id('scrcpyCheckIndicator'); + if (scrcpy.version) { + el.removeClass('badge-warning') + .addClass('badge-success') + .html( + `<i class="fa fa-check-circle-o"></i> SCRCPY Installed (${scrcpy.cmd})<br/><pre style="font-size: x-small;">${scrcpy.version}</pre>`, + ); + } else { + const tag = remote + .getGlobal('platform') + .replace('win', 'windows') + .replace('mac', 'macos'); + el.removeClass('badge-warning').addClass('badge-danger') + .html(`<i class="fa fa-times-circle-o"></i> SCRCPY global installation not found, please read the <a class="btn btn-sm btn-info" onclick="shell.openExternal('https://github.com/Genymobile/scrcpy#${tag}')">MANUAL</a>`); - if (scrcpy.error) el.append(`<br/><pre style="font-size: x-small;">error: \n${scrcpy.error}</pre>`); + if (scrcpy.error) + el.append( + `<br/><pre style="font-size: x-small;">error: \n${scrcpy.error}</pre>`, + ); + } } - } - }); + }); - console.log('ONLOAD'); -</script> + console.log('ONLOAD'); + </script> {% endblock %} diff --git a/views/js/browse.js b/views/js/browse.js index 1a32574..50017e1 100644 --- a/views/js/browse.js +++ b/views/js/browse.js @@ -62,10 +62,10 @@ document.addEventListener("keydown", (e) => { } if ( - e.code == "Backspace" && - !$(".form-control").is(":focus") && - !$(".find-input").is(":focus") && - !$("#bookmarkName").is(":focus") + e.code == "Backspace" && + !$(".form-control").is(":focus") && + !$(".find-input").is(":focus") && + !$("#bookmarkName").is(":focus") ) { return upDir(); } @@ -91,7 +91,7 @@ function resizeLoc() { const width = window.innerWidth / 10 - 60; if (dir_path.title.length > width) { dir_path.innerText = - dir_path.title.substr(0, 8) + "..." + dir_path.title.slice(-(width - 10)); + dir_path.title.substr(0, 8) + "..." + dir_path.title.slice(-(width - 10)); } else { dir_path.innerText = dir_path.title; } @@ -121,14 +121,14 @@ function scrollByHistory() { function fixIcons() { $(".browse-folder").hover( - (e) => { - $(e.target).find("i").removeClass("fa-folder-o"); - $(e.target).find("i").addClass("fa-folder-open-o"); - }, - (e) => { - $(e.target).find("i").addClass("fa-folder-o"); - $(e.target).find("i").removeClass("fa-folder-open-o"); - }, + (e) => { + $(e.target).find("i").removeClass("fa-folder-o"); + $(e.target).find("i").addClass("fa-folder-open-o"); + }, + (e) => { + $(e.target).find("i").addClass("fa-folder-o"); + $(e.target).find("i").removeClass("fa-folder-open-o"); + }, ); } @@ -198,20 +198,20 @@ function loadDir(list) { // console.log(item); if (!item.createdAt) { cards_first.unshift( - `<div class="listitem badge badge-danger"><i class="fa fa-times-circle-o"></i> ${item.name}</div>`, + `<div class="listitem badge badge-danger"><i class="fa fa-times-circle-o"></i> ${item.name}</div>`, ); continue; } const modified = item.info.mtime.getTime(); const fullPath = item.filePath - .replace("\\", "/") - .replace("", ":") - .split("'") - .join("\\'"); + .replace("\\", "/") + .replace("", ":") + .split("'") + .join("\\'"); const symblink = item.isLink - ? `<small style="font-family: FontAwesome" class="text-secondary fa-link"></small> ` - : ""; + ? `<small style="font-family: FontAwesome" class="text-secondary fa-link"></small> ` + : ""; const name = symblink + item.name; if (item.isFile) { @@ -241,11 +241,11 @@ function loadDir(list) { } let newribbon = item.newItem - ? `<div class="ribbon-wrapper"><div class="ribbon ribbon-yellow">NEW!</div></div>` - : ""; + ? `<div class="ribbon-wrapper"><div class="ribbon ribbon-yellow">NEW!</div></div>` + : ""; if (item.mp) { let color = - item.mp.mp == "yes" ? "green" : item.mp.mp == "no" ? "red" : "yellow"; + item.mp.mp == "yes" ? "green" : item.mp.mp == "no" ? "red" : "yellow"; newribbon = `<div class="ribbon-wrapper"><div class="ribbon ribbon-${color}" title="${item.mp.note}">MP: ${item.mp.mp}</div></div>`; } @@ -275,14 +275,14 @@ function loadDir(list) { } const youtubeUrl = - "https://www.youtube.com/results?search_query=oculus+quest+" + - escape(item.simpleName); + "https://www.youtube.com/results?search_query=oculus+quest+" + + escape(item.simpleName); selectBtn += `<a onclick="shell.openExternal('${youtubeUrl}')" title="Search at Youtube" class="btn btn-sm btn-danger"> <i class="fa-brands fa-youtube"></i></a> `; const size = item.size - ? `${item.size} Mb` - : `<a onclick="getDirSize(this, '${fullPath}')"> + ? `${item.size} Mb` + : `<a onclick="getDirSize(this, '${fullPath}')"> <i class="fa fa-calculator" title="Calculate folder size"></i> get size </a>`; @@ -301,8 +301,8 @@ function loadDir(list) { </div> <div style="color:#ccc;" class="card-footer pb-1 pt-1"><small> versionCode: ${item.versionCode || "Unknown"} ${ - (item.versionName && `(v.${item.versionName})`) || "" - } + (item.versionName && `(v.${item.versionName})`) || "" + } <br/> <span class="package-name">${item.packageName}</span><br/> Updated: ${item.info.mtime.toLocaleString()} @@ -346,31 +346,31 @@ function sortFileElements(el, key, asc) { const sortByName = key.startsWith("name"); el.html( - el - .find(".listitem") - .sort((a, b) => { - const valA = sortByName - ? a.dataset.name.toLowerCase() - : a.dataset.modified; - const valB = sortByName - ? b.dataset.name.toLowerCase() - : b.dataset.modified; - if (valA < valB) { - return asc ? -1 : 1; - } - if (valA > valB) { - return asc ? 1 : -1; - } - return 0; - }) - .sort((a, b) => { - if (a.dataset.isfile === "true" && b.dataset.isfile !== "true") { - return 1; - } - if (a.dataset.isfile !== "true" && b.dataset.isfile === "true") { - return -1; - } - return 0; - }), + el + .find(".listitem") + .sort((a, b) => { + const valA = sortByName + ? a.dataset.name.toLowerCase() + : a.dataset.modified; + const valB = sortByName + ? b.dataset.name.toLowerCase() + : b.dataset.modified; + if (valA < valB) { + return asc ? -1 : 1; + } + if (valA > valB) { + return asc ? 1 : -1; + } + return 0; + }) + .sort((a, b) => { + if (a.dataset.isfile === "true" && b.dataset.isfile !== "true") { + return 1; + } + if (a.dataset.isfile !== "true" && b.dataset.isfile === "true") { + return -1; + } + return 0; + }), ); } diff --git a/views/js/installed.js b/views/js/installed.js index 59a1b04..28be61d 100644 --- a/views/js/installed.js +++ b/views/js/installed.js @@ -1,62 +1,62 @@ -console.log('ONLOAD INSTALLED'); +console.log("ONLOAD INSTALLED"); -ipcRenderer.on('get_installed', (event, arg) => { - console.log('get_installed msg came ! ', arg.success); +ipcRenderer.on("get_installed", (event, arg) => { + console.log("get_installed msg came ! ", arg.success); if (arg.success) { drawInstalledApps(arg.apps, true); - $id('updateBadge').show(); + $id("updateBadge").show(); } - $id('processingModal').modal('hide'); - $id('installedModal').modal('show'); + $id("processingModal").modal("hide"); + $id("installedModal").modal("show"); }); -ipcRenderer.on('get_installed_with_updates', (event, arg) => { - console.log('get_installed msg came ! ', arg.success); +ipcRenderer.on("get_installed_with_updates", (event, arg) => { + console.log("get_installed msg came ! ", arg.success); if (arg.success) { drawInstalledApps(arg.apps); - $id('updateBadge').hide(); + $id("updateBadge").hide(); } - $id('processingModal').modal('hide'); + $id("processingModal").modal("hide"); }); -ipcRenderer.on('uninstall', (event, arg) => { - console.log('uninstall msg came ! '); - $id('installedModal').modal('hide'); - $id('appToolModal').modal('hide'); - loadInclude('modals/installed.twig', 'installedmodaldiv'); +ipcRenderer.on("uninstall", (event, arg) => { + console.log("uninstall msg came ! "); + $id("installedModal").modal("hide"); + $id("appToolModal").modal("hide"); + loadInclude("modals/installed.twig", "installedmodaldiv"); }); function getUpdates() { - $id('processingModal').modal('show'); - ipcRenderer.send('get_installed_with_updates', ''); + $id("processingModal").modal("show"); + ipcRenderer.send("get_installed_with_updates", ""); } function update(elem) { elem.innerHTML = `<i class="fa fa-refresh fa-spin"></i> Please wait`; - ipcRenderer.send('folder_install', { path: elem.dataset.path, update: true }); + ipcRenderer.send("folder_install", { path: elem.dataset.path, update: true }); } function uninstall(elem, packageName) { elem.innerHTML = `<i class="fa fa-refresh fa-spin"></i> Please wait`; - ipcRenderer.send('uninstall', packageName); + ipcRenderer.send("uninstall", packageName); } function startApp(packageName) { - ipcRenderer.send('start_app', packageName); + ipcRenderer.send("start_app", packageName); // ipcRenderer.send('get_activities', packageName); } function appTools(packageName) { - $id('processingModal').modal('show'); - loadInclude('modals/app_tools.twig', 'apptoolsmodaldiv', () => { - ipcRenderer.send('app_tools', packageName); + $id("processingModal").modal("show"); + loadInclude("modals/app_tools.twig", "apptoolsmodaldiv", () => { + ipcRenderer.send("app_tools", packageName); }); } function drawInstalledApps(apps, updates = false) { - console.log('drawInstalledApps', apps.length); + console.log("drawInstalledApps", apps.length); if (updates && apps.length === 0) { const row = `<tr><td class="text-center" style="width: 250px;vertical-align:middle;"><div class="alert alert-info mb-0"><i class="fa fa-info-circle"></i> <b>There are no updates available</b></div></td></tr>`; @@ -64,7 +64,7 @@ function drawInstalledApps(apps, updates = false) { return; } - let rows = ''; + let rows = ""; for (const app of apps) { // console.log('list app', app); let row = `<tr data-simplename="${app.simpleName}" data-packagename="${app.packageName}"><td class="text-center" style="width: 250px;vertical-align:middle;"><img style="max-height:80px" src="${app.imagePath}"/></td> @@ -75,8 +75,7 @@ function drawInstalledApps(apps, updates = false) { row += `<a onclick="startApp('${app.packageName}')" class="adbdev btn btn-md btn-info" title="Launch"><i class="fa fa-play"></i></a> `; row += `<a onclick="uninstall(this, '${app.packageName}')" class="adbdev btn btn-md btn-danger" title="Uninstall"><i class="fa fa-trash-can"></i></a> `; row += `<a onclick="appTools('${app.packageName}')" class="adbdev btn btn-md btn-primary"> <i class="fa fa-cog"></i> Tools</a> `; - } - else { + } else { row += `<a data-path="${app.update.path}" onclick='update(this)' class="btn btn-sm btn-info"> <i class="fa fa-upload"></i> Update to <br/> v.${app.update.versionCode} @@ -87,7 +86,5 @@ function drawInstalledApps(apps, updates = false) { rows += row; } - id('intalledTable').innerHTML = rows; + id("intalledTable").innerHTML = rows; } - - diff --git a/views/js/main.js b/views/js/main.js index 7c05905..fa58fcc 100644 --- a/views/js/main.js +++ b/views/js/main.js @@ -8,24 +8,21 @@ function $id(el_id) { function copyInput(el) { el.select(); - document.execCommand('copy'); - alert('Text copied to clipboard'); + document.execCommand("copy"); + alert("Text copied to clipboard"); } -window.addEventListener('scroll', () => { +window.addEventListener("scroll", () => { // console.log(document.body.scrollTop, document.documentElement.scrollTop); const scroll = document.documentElement.scrollTop; - if ( - scroll > 100 - ) { - $id('backToTop').fadeIn(); - } - else { - $id('backToTop').fadeOut(); + if (scroll > 100) { + $id("backToTop").fadeIn(); + } else { + $id("backToTop").fadeOut(); } }); function backToTop() { // document.body.scrollTop = 0; document.documentElement.scrollTop = 0; -} \ No newline at end of file +} diff --git a/views/js/search.js b/views/js/search.js index 1a444ac..4a8dc9b 100644 --- a/views/js/search.js +++ b/views/js/search.js @@ -320,17 +320,19 @@ function openSearch() { */ function sortBy(key, asc) { return (a, b) => { - var valA = $(a).data(key); - var valB = $(b).data(key); - if (valA < valB) { - return asc ? -1 : 1; - } - - if (valA > valB) { - return asc ? 1 : -1; - } - - return 0; + return (a, b) => { + var valA = $(a).data(key); + var valB = $(b).data(key); + if (valA < valB) { + return asc ? -1 : 1; + } + + if (valA > valB) { + return asc ? 1 : -1; + } + + return 0; + }; }; } @@ -356,3 +358,4 @@ function sortElements($element, key, asc = true) { items.sort(sortBy(key, asc)); $element.html(items); } + diff --git a/views/layout.twig b/views/layout.twig index 7000bdf..468fe6b 100644 --- a/views/layout.twig +++ b/views/layout.twig @@ -1,9 +1,12 @@ <!doctype html> <html lang="en"> -<head> - <!-- Required meta tags --> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <head> + <!-- Required meta tags --> + <meta charset="utf-8"> + <meta + name="viewport" + content="width=device-width, initial-scale=1, shrink-to-fit=no" + > <script>window.$ = window.jQuery = require('jquery');</script> @@ -15,22 +18,24 @@ <script src="js/bootstrap.min.js"></script> <script src="js/main.js"></script> - <!-- Bootstrap CSS --> - <!-- <link rel="stylesheet" href="css/bootstrap.min.css" crossorigin="anonymous"> --> + <!-- Bootstrap CSS --> + <!-- + <link rel="stylesheet" href="css/bootstrap.min.css" crossorigin="anonymous"> + --> <link rel="stylesheet" href="css/bootstrap-slate.min.css"> <!-- <link rel="stylesheet" href="https://bootswatch.com/4/sandstone/bootstrap.min.css"> --> <link rel="stylesheet" href="../node_modules/@fortawesome/fontawesome-free/css/all.min.css"> <link rel="stylesheet" href="css/main.css"> - <title>Quest-Sideloader</title> + <title>Quest-Sideloader</title> - <script> - const electron = window.require('electron'); - const remote = window.require('@electron/remote'); - console.log(electron); - const { ipcRenderer, /*remote, */shell } = electron; - const { dialog } = remote; + <script> + const electron = window.require('electron'); + const remote = window.require('@electron/remote'); + console.log(electron); + const { ipcRenderer, /*remote, */ shell } = electron; + const { dialog } = remote; function loadInclude(template, id = null, complete = null) { $(document).trigger("newTemplate" ); @@ -45,33 +50,33 @@ } } - function formatEta(seconds) { - if (seconds == null) { return 'Starting...'; } - hours = Math.floor(seconds / 3600); - seconds -= hours * 3600; - minutes = Math.floor(seconds / 60); - seconds -= minutes * 60; - if (hours > 0) { - return hours + 'h ' + minutes + 'm ' + seconds + 's'; + function formatEta(seconds) { + if (seconds == null) { + return 'Starting...'; + } + hours = Math.floor(seconds / 3600); + seconds -= hours * 3600; + minutes = Math.floor(seconds / 60); + seconds -= minutes * 60; + if (hours > 0) { + return hours + 'h ' + minutes + 'm ' + seconds + 's'; + } else if (minutes > 0) { + return minutes + 'm ' + seconds + 's'; + } else { + return seconds + 's'; + } } - else if (minutes > 0) { - return minutes + 'm ' + seconds + 's'; - } - else { - return seconds + 's'; - } - } - function formatBytes(bytes) { - const sizes = ['b', 'Kb', 'Mb', 'Gb', 'Tb']; - if (bytes == 0) return '0 b'; + function formatBytes(bytes) { + const sizes = ['b', 'Kb', 'Mb', 'Gb', 'Tb']; + if (bytes == 0) return '0 b'; - const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); - return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i]; - } + const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i]; + } - // screenhot stuff - /*async function makeScreenshot(selector = 'body') { + // screenhot stuff + /*async function makeScreenshot(selector = 'body') { return new Promise((resolve, reject) => { let node = document.querySelector(selector); html2canvas(node, { @@ -92,110 +97,160 @@ console.log(err); }); }*/ - </script> -</head> -<body> - -<div class="content_container"> - <div class="sticky" id="topbar"> - <nav class="navbar navbar-expand" style="padding: 5px"> - - <img src="img/quest-sidenoder-light.svg" width="60" height="50" class="mr-2"> - {% block navbar %} - <div class="navbar-collapse"> - <ul class="navbar-nav mr-auto"> - <li class="nav-item"> - <a class="btn btn-primary mr-1" onclick="loadInclude('browse_include.twig', null, () => ipcRenderer.send('get_dir', ''));"> - <i class="fa fa-folder-open-o"></i> - Browse - </a> - </li> - <li class="nav-item"> - <a id="remotebtn" class="disabled btn btn-info mr-1" onclick="loadInclude('browse_include.twig', null, () => ipcRenderer.send('get_dir', remote.getGlobal('mountFolder')));"> - <i class="fa fa-folder-open-o"></i> - Remote - </a> - </li> - <li class="nav-item"> - <a class="adbdev disabled btn btn-primary mr-1" onclick="loadInclude('modals/installed.twig', 'installedmodaldiv')" title="Installed APP's"> - <i class="fa fa-list"></i> - Installed - </a> - </li> - <li class="nav-item"> - <a class="adbdev btn btn-primary mr-1" onclick="loadInclude('modals/tweaks.twig', 'tweaksmodaldiv')" title="Device tweaks"> - <i class="fa fa-bug"></i> - </a> - </li> - <li class="nav-item"> - <a class="btn btn-primary mr-1" onclick="loadInclude('settings_include.twig')" title="Settings"> - <i class="fa fa-cog"></i> - </a> - </li> - </ul> + </script> + </head> + <body> + <div class="content_container"> + <div class="sticky" id="topbar"> + <nav class="navbar navbar-expand" style="padding: 5px"> + <img + src="img/quest-sidenoder-light.svg" + width="60" + height="50" + class="mr-2" + > + {% block navbar %} + <div class="navbar-collapse"> + <ul class="navbar-nav mr-auto"> + <li class="nav-item"> + <a + class="btn btn-primary mr-1" + onclick="loadInclude('browse_include.twig', null, () => ipcRenderer.send('get_dir', ''));" + > + <i class="fa fa-folder-open-o"></i> + Browse + </a> + </li> + <li class="nav-item"> + <a + id="remotebtn" + class="disabled btn btn-info mr-1" + onclick="loadInclude('browse_include.twig', null, () => ipcRenderer.send('get_dir', remote.getGlobal('mountFolder')));" + > + <i class="fa fa-folder-open-o"></i> + Remote + </a> + </li> + <li class="nav-item"> + <a + class="adbdev disabled btn btn-primary mr-1" + onclick="loadInclude('modals/installed.twig', 'installedmodaldiv')" + title="Installed APP's" + > + <i class="fa fa-list"></i> + Installed + </a> + </li> + <li class="nav-item"> + <a + class="adbdev btn btn-primary mr-1" + onclick="loadInclude('modals/tweaks.twig', 'tweaksmodaldiv')" + title="Device tweaks" + > + <i class="fa fa-bug"></i> + </a> + </li> + <li class="nav-item"> + <a + class="btn btn-primary mr-1" + onclick="loadInclude('settings_include.twig')" + title="Settings" + > + <i class="fa fa-cog"></i> + </a> + </li> + </ul> + + <div> + <span + type="button" + id="mountbtn" + onclick="checkMount()" + class="btn btn-sm btn-danger" + disabled + > + <i id="mountrefresh" class="fa fa-refresh"></i> | MOUNT:<br> + disconnected + </span> + <span + type="button" + id="devicebtn" + class="btn btn-sm btn-danger" + disabled + > + <i class="fa fa-spin fa-refresh"></i> | DEVICE:<br> + disconnected + </span> + <span + type="button" + id="wirelessbtn" + onclick="connectWireless()" + class="btn btn-sm btn-danger" + disabled + > + <i id="wirelessrefresh" class="fa fa-refresh"></i> | WIRELESS:<br> + disconnected + </span> + </div> + </div> + {% endblock %} + </nav> <div> - <span type="button" id="mountbtn" onclick="checkMount()" class="btn btn-sm btn-danger" disabled> - <i id="mountrefresh" class="fa fa-refresh"></i> | MOUNT:</br>disconnected - </span> - <span type="button" id="devicebtn" class="btn btn-sm btn-danger" disabled> - <i class="fa fa-spin fa-refresh"></i> | DEVICE:</br>disconnected - </span> - <span type="button" id="wirelessbtn" onclick="connectWireless()" class="btn btn-sm btn-danger" disabled> - <i id="wirelessrefresh" class="fa fa-refresh"></i> | WIRELESS:</br>disconnected - </span> + <div id="device_infoDiv"></div> </div> </div> - {% endblock %} - </nav> - <div> - <div id="device_infoDiv"></div> + {% block body %}{% endblock %} </div> - </div> - - {% block body %}{% endblock %} -</div> - -<footer class="footer text-center"> - <hr> - <a class="btn btn-sm" onclick="shell.openPath(remote.getGlobal('sidenoderHome') + '/debug_last.log')"><i class="fa fa-file-code-o"></i> log</a> - <b>SideNoder v{{ version }}</b> - <br> - https://github.com/VRPirates/sidenoder -</footer> - -<!-- modals --> -<div id="installedmodaldiv"></div> -<div id="tweaksmodaldiv"></div> -<div id="appinfomodaldiv"></div> -<div id="appinfoeventsmodaldiv"></div> -<div id="confirmmodaldiv"></div> -<div id="sideloadmodaldiv"></div> -<div id="sideload-queuediv"></div> -<div id="appstartmodaldiv"></div> -<div id="apptoolsmodaldiv"></div> -<div id="scrcpymodaldiv"></div> -<div id="processingmodaldiv"></div> -<div id="promptmodaldiv"></div> - -<a class="btn btn-dark btn-floating btn-lg" id="backToTop" onclick="backToTop()"><i class="fa fa-angle-up"></i></a> - -<script> - loadInclude('device_info.twig', 'device_infoDiv'); - loadInclude('modals/appinfo.twig', 'appinfomodaldiv'); - loadInclude('modals/appinfo_events.twig', 'appinfoeventsmodaldiv'); - loadInclude('modals/confirm.twig', 'confirmmodaldiv'); - loadInclude('modals/app_start.twig', 'appstartmodaldiv'); - loadInclude('modals/scrcpy.twig', 'scrcpymodaldiv'); - loadInclude('modals/processing.twig', 'processingmodaldiv'); - loadInclude('modals/prompt.twig', 'promptmodaldiv'); - - /*loadInclude('modals/sideload-queue.twig', 'sideload-queuediv', () => { + + <footer class="footer text-center"> + <hr> + <a + class="btn btn-sm" + onclick="shell.openPath(remote.getGlobal('sidenoderHome') + '/debug_last.log')" + ><i class="fa fa-file-code-o"></i> log</a + > + <b>SideNoder v{{ version }}</b> + <br> + https://github.com/VRPirates/sidenoder + </footer> + + <!-- modals --> + <div id="installedmodaldiv"></div> + <div id="tweaksmodaldiv"></div> + <div id="appinfomodaldiv"></div> + <div id="appinfoeventsmodaldiv"></div> + <div id="confirmmodaldiv"></div> + <div id="sideloadmodaldiv"></div> + <div id="sideload-queuediv"></div> + <div id="appstartmodaldiv"></div> + <div id="apptoolsmodaldiv"></div> + <div id="scrcpymodaldiv"></div> + <div id="processingmodaldiv"></div> + <div id="promptmodaldiv"></div> + + <a + class="btn btn-dark btn-floating btn-lg" + id="backToTop" + onclick="backToTop()" + ><i class="fa fa-angle-up"></i + ></a> + + <script> + loadInclude('device_info.twig', 'device_infoDiv'); + loadInclude('modals/appinfo.twig', 'appinfomodaldiv'); + loadInclude('modals/appinfo_events.twig', 'appinfoeventsmodaldiv'); + loadInclude('modals/confirm.twig', 'confirmmodaldiv'); + loadInclude('modals/app_start.twig', 'appstartmodaldiv'); + loadInclude('modals/scrcpy.twig', 'scrcpymodaldiv'); + loadInclude('modals/processing.twig', 'processingmodaldiv'); + loadInclude('modals/prompt.twig', 'promptmodaldiv'); + + /*loadInclude('modals/sideload-queue.twig', 'sideload-queuediv', () => { ipcRenderer.send('sideload_read_queue', ''); });*/ - document.addEventListener('dragover', (ev) => ev.preventDefault()); document.addEventListener('drop', (ev) => ev.preventDefault()); @@ -218,190 +273,198 @@ navPanel.style.top = $("#topbar").height() + "px"; }); - // actions - - function checkMount() { - $id('mountrefresh').addClass('fa-spin'); - $id('mountbtn') - .removeClass('btn-danger') - .removeClass('btn-success') - .addClass('btn-warning'); - $id('remotebtn').addClass('disabled'); - ipcRenderer.send('mount', 'bla'); - } - - function connectWireless() { - $id('wirelessrefresh').addClass('fa-spin'); - $id('wirelessbtn') - .removeClass('btn-danger') - .addClass('btn-warning'); - ipcRenderer.send('connect_wireless', ''); - } - function disconnectWireless() { - $id('wirelessrefresh').addClass('fa-spin'); - $id('wirelessbtn') - .removeClass('btn-success') - .addClass('btn-warning'); - ipcRenderer.send('disconnect_wireless', ''); - } - - // events - - - ipcRenderer.on('log', (event, arg) => { - console.log('log came'); - console.log(arg); - }); - - ipcRenderer.on('notify_update', (event, arg) => { - console.log('notify_update msg came ! '); - dialog.showMessageBox(null, { - type: 'info', - buttons: ['Cancel', 'Download'], - title: `Update available ${arg.current} to ${arg.remote}`, - message: `sidenoder-${arg.remote} is now available on github.`, - detail: arg.description, - }) - .then(({ response }) => { - if (response === 1) shell.openExternal(arg.url); - }) - }); + // actions - //edit btn on top - ipcRenderer.on('check_mount', (event, arg) => { - console.log('check_mount responded: ', arg); // prints "ping" - if (arg.success) { - $id('mountrefresh').removeClass('fa-spin'); - $id('mountbtn') - .removeClass('btn-warning') - .removeClass('btn-danger') - .addClass('btn-success') - .html(`<i id="mountrefresh" class="fa fa-check-circle-o"></i> | UNMOUNT:</br>connected`); - $id('remotebtn').removeClass('disabled'); - - if (!remote.getGlobal('adbDevice')) return; - - $id('updateBadge').removeClass('disabled'); - } - else { - $id('mountrefresh').removeClass('fa-spin'); - $id('mountbtn') - .removeClass('btn-success') - .removeClass('btn-warning') - .addClass('btn-danger') - .html(`<i id="mountrefresh" class="fa fa-refresh"></i> | MOUNT:</br>disconnected`); - $id('remotebtn').addClass('disabled'); - - $id('updateBadge').addClass('disabled'); - - if (arg.error) - dialog.showMessageBox(null, { - type: 'error', - buttons: ['Ok'], - title: 'RCLONE MOUNT FAILED', - message: 'Rclone failed on mount command', - detail: arg.error.toString(), - }); - } - }); + function checkMount() { + $id('mountrefresh').addClass('fa-spin'); + $id('mountbtn') + .removeClass('btn-danger') + .removeClass('btn-success') + .addClass('btn-warning'); + $id('remotebtn').addClass('disabled'); + ipcRenderer.send('mount', 'bla'); + } - ipcRenderer.on('check_device', (event, arg) => { - console.log('check_device msg:', arg); + function connectWireless() { + $id('wirelessrefresh').addClass('fa-spin'); + $id('wirelessbtn').removeClass('btn-danger').addClass('btn-warning'); + ipcRenderer.send('connect_wireless', ''); + } + function disconnectWireless() { + $id('wirelessrefresh').addClass('fa-spin'); + $id('wirelessbtn').removeClass('btn-success').addClass('btn-warning'); + ipcRenderer.send('disconnect_wireless', ''); + } - if (!!arg.success) { - console.log('GETDEVICE SUCESS'); - $id('devicebtn') - .removeClass('btn-danger') - .addClass('btn-success') - .html(`<i class="fa fa-check-circle-o"></i> | DEVICE:</br>connected`); + // events - $('a.adbdev').removeClass('disabled'); - if (!remote.getGlobal('mounted')) { - $id('updateBadge').addClass('disabled'); - } + ipcRenderer.on('log', (event, arg) => { + console.log('log came'); + console.log(arg); + }); - if (arg.success.endsWith(':5555')) { - id('wirelessbtn').onclick = disconnectWireless; - $id('wirelessbtn') - .removeClass('btn-danger') - .addClass('btn-success') - .html(`<i id="wirelessrefresh" class="fa fa-check-circle-o"></i> | WIRELESS:</br>connected`); - } + ipcRenderer.on('notify_update', (event, arg) => { + console.log('notify_update msg came ! '); + dialog + .showMessageBox(null, { + type: 'info', + buttons: ['Cancel', 'Download'], + title: `Update available ${arg.current} to ${arg.remote}`, + message: `sidenoder-${arg.remote} is now available on github.`, + detail: arg.description, + }) + .then(({ response }) => { + if (response === 1) shell.openExternal(arg.url); + }); + }); - ipcRenderer.send('get_device_info', ''); - ipcRenderer.send('mp_name', { cmd: 'get' }); - } - else { - $id('devicebtn') - .removeClass('btn-success') - .addClass('btn-danger') - .html(`<i class="fa fa-refresh fa-spin"></i> | DEVICE:</br>disconnected`); - - $('a.adbdev').addClass('disabled'); - - id('wirelessbtn').onclick = connectWireless; - $id('wirelessbtn') - .removeClass('btn-success') - .addClass('btn-danger') - .html(`<i id="wirelessbtnrefresh" class="fa fa-refresh"></i> | WIRELESS:</br>disconnected`); - } - }); + //edit btn on top + ipcRenderer.on('check_mount', (event, arg) => { + console.log('check_mount responded: ', arg); // prints "ping" + if (arg.success) { + $id('mountrefresh').removeClass('fa-spin'); + $id('mountbtn') + .removeClass('btn-warning') + .removeClass('btn-danger') + .addClass('btn-success') + .html( + `<i id="mountrefresh" class="fa fa-check-circle-o"></i> | UNMOUNT:</br>connected`, + ); + $id('remotebtn').removeClass('disabled'); + + if (!remote.getGlobal('adbDevice')) return; + + $id('updateBadge').removeClass('disabled'); + } else { + $id('mountrefresh').removeClass('fa-spin'); + $id('mountbtn') + .removeClass('btn-success') + .removeClass('btn-warning') + .addClass('btn-danger') + .html( + `<i id="mountrefresh" class="fa fa-refresh"></i> | MOUNT:</br>disconnected`, + ); + $id('remotebtn').addClass('disabled'); + + $id('updateBadge').addClass('disabled'); + + if (arg.error) + dialog.showMessageBox(null, { + type: 'error', + buttons: ['Ok'], + title: 'RCLONE MOUNT FAILED', + message: 'Rclone failed on mount command', + detail: arg.error.toString(), + }); + } + }); - ipcRenderer.on('connect_wireless', (event, arg) => { - console.log('check_wireless msg came FROM BE to fe:', arg); - $id('wirelessrefresh').removeClass('fa-spin'); - - if (arg.success) { - console.log('WIRELESS CONNECTED'); - id('wirelessbtn').onclick = disconnectWireless; - $id('wirelessbtn') - .removeClass('btn-danger') - .addClass('btn-success') - .html(`<i id="wirelessrefresh" class="fa fa-check-circle-o"></i> | WIRELESS:</br>connected`); - - dialog.showMessageBox(null, { - type: 'info', - buttons: ['Ok'], - title: 'Device connected by TCP', - message: 'Now you can unplug USB cable and continue used the programm by wireless connection', - }) - } - else { - id('wirelessbtn').onclick = connectWireless; - $id('wirelessbtn') - .removeClass('btn-success') - .addClass('btn-danger') - .html(`<i id="wirelessbtnrefresh" class="fa fa-refresh"></i> | WIRELESS:</br>disconnected`); - } - }); + ipcRenderer.on('check_device', (event, arg) => { + console.log('check_device msg:', arg); + + if (!!arg.success) { + console.log('GETDEVICE SUCESS'); + $id('devicebtn') + .removeClass('btn-danger') + .addClass('btn-success') + .html( + `<i class="fa fa-check-circle-o"></i> | DEVICE:</br>connected`, + ); + + $('a.adbdev').removeClass('disabled'); + if (!remote.getGlobal('mounted')) { + $id('updateBadge').addClass('disabled'); + } + + if (arg.success.endsWith(':5555')) { + id('wirelessbtn').onclick = disconnectWireless; + $id('wirelessbtn') + .removeClass('btn-danger') + .addClass('btn-success') + .html( + `<i id="wirelessrefresh" class="fa fa-check-circle-o"></i> | WIRELESS:</br>connected`, + ); + } + + ipcRenderer.send('get_device_info', ''); + ipcRenderer.send('mp_name', { cmd: 'get' }); + } else { + $id('devicebtn') + .removeClass('btn-success') + .addClass('btn-danger') + .html( + `<i class="fa fa-refresh fa-spin"></i> | DEVICE:</br>disconnected`, + ); + + $('a.adbdev').addClass('disabled'); + + id('wirelessbtn').onclick = connectWireless; + $id('wirelessbtn') + .removeClass('btn-success') + .addClass('btn-danger') + .html( + `<i id="wirelessbtnrefresh" class="fa fa-refresh"></i> | WIRELESS:</br>disconnected`, + ); + } + }); - let last_alert_msg; - ipcRenderer.on('alert', (event, arg) => { - if (arg == last_alert_msg) return; - last_alert_msg = arg; - setTimeout(()=> last_alert_msg = false, 500); - alert(arg); - }); + ipcRenderer.on('connect_wireless', (event, arg) => { + console.log('check_wireless msg came FROM BE to fe:', arg); + $id('wirelessrefresh').removeClass('fa-spin'); + + if (arg.success) { + console.log('WIRELESS CONNECTED'); + id('wirelessbtn').onclick = disconnectWireless; + $id('wirelessbtn') + .removeClass('btn-danger') + .addClass('btn-success') + .html( + `<i id="wirelessrefresh" class="fa fa-check-circle-o"></i> | WIRELESS:</br>connected`, + ); + + dialog.showMessageBox(null, { + type: 'info', + buttons: ['Ok'], + title: 'Device connected by TCP', + message: + 'Now you can unplug USB cable and continue used the programm by wireless connection', + }); + } else { + id('wirelessbtn').onclick = connectWireless; + $id('wirelessbtn') + .removeClass('btn-success') + .addClass('btn-danger') + .html( + `<i id="wirelessbtnrefresh" class="fa fa-refresh"></i> | WIRELESS:</br>disconnected`, + ); + } + }); - ipcRenderer.on('cmd_sended', (event, arg) => { - alert(`Command sended: \n ${arg.success}`); - }); + let last_alert_msg; + ipcRenderer.on('alert', (event, arg) => { + if (arg == last_alert_msg) return; + last_alert_msg = arg; + setTimeout(() => (last_alert_msg = false), 500); + alert(arg); + }); - ipcRenderer.on('ask_device', (event, arg) => { - console.log('ask_device msg came ! '); - $id('processingModal').modal('hide'); - dialog.showMessageBox(null, { - type: 'info', - buttons: ['Understood'], - title: 'Missing device', - message: `This action cannot be performed without a device attached.`, - }); - }); -</script> -<script src="js/search.js"></script> -<script src="js/browse.js"></script> -<script src="js/installed.js"></script> + ipcRenderer.on('cmd_sended', (event, arg) => { + alert(`Command sended: \n ${arg.success}`); + }); -</body> + ipcRenderer.on('ask_device', (event, arg) => { + console.log('ask_device msg came ! '); + $id('processingModal').modal('hide'); + dialog.showMessageBox(null, { + type: 'info', + buttons: ['Understood'], + title: 'Missing device', + message: `This action cannot be performed without a device attached.`, + }); + }); + </script> + <script src="js/search.js"></script> + <script src="js/browse.js"></script> + <script src="js/installed.js"></script> + </body> </html> - diff --git a/views/modals/app_start.twig b/views/modals/app_start.twig index 56532f6..58177c7 100644 --- a/views/modals/app_start.twig +++ b/views/modals/app_start.twig @@ -1,22 +1,42 @@ -<div class="modal fade" id="appStartModal" tabindex="-1" role="dialog" aria-labelledby="appStartModalLabel" data-backdrop="static" data-keyboard="false"> +<div + class="modal fade" + id="appStartModal" + tabindex="-1" + role="dialog" + aria-labelledby="appStartModalLabel" + data-backdrop="static" + data-keyboard="false" +> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="appStartModalLabel">Launch application</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> <div class="modal-body"> <div class="form-group"> <label for="appActivities">List of activities</label> - <select class="custom-select" id="appActivities"> - </select> + <select class="custom-select" id="appActivities"></select> </div> </div> <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> - <button type="button" id="startActivityButton" class="btn btn-primary disabled">Start</button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + Cancel + </button> + <button + type="button" + id="startActivityButton" + class="btn btn-primary disabled" + > + Start + </button> </div> </div> </div> @@ -33,7 +53,7 @@ let options = ''; for (const activity of arg.activities) { - options+= `<option value="${activity}">${activity.split('/')[1]}</option>`; + options += `<option value="${activity}">${activity.split('/')[1]}</option>`; } id('appActivities').innerHTML = options; @@ -44,8 +64,8 @@ $id('appStartModal').modal('hide'); });*/ - $id('startActivityButton').on('click', function(){ + $id('startActivityButton').on('click', function () { $id('appStartModal').modal('hide'); ipcRenderer.send('start_activity', $id('appActivities').val()); }); -</script> \ No newline at end of file +</script> diff --git a/views/modals/app_tools.twig b/views/modals/app_tools.twig index 6f1b8fa..bb49584 100644 --- a/views/modals/app_tools.twig +++ b/views/modals/app_tools.twig @@ -1,21 +1,33 @@ <!-- Modal --> -<div class="modal" id="appToolModal" > - <div role="document" style="margin: 1.75rem auto; min-width:580px; max-width:40%"> <!-- class="modal-dialog" --> +<div class="modal" id="appToolModal"> + <div + role="document" + style="margin: 1.75rem auto; min-width: 580px; max-width: 40%" + > + <!-- class="modal-dialog" --> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="appToolModalLabel">App tools</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> <div class="modal-body"> <h4 class="text-center" id="packageName"></h4> - <hr/> + <hr> <div class="card-body" id="configsCard" style="display: none"> - <div class="alert alert-warning"><small> - <i class="fa fa-exclamation-triangle"></i> - WARNING: All changes your own risk. Recommend to backup appdata before - </small></div> + <div class="alert alert-warning"> + <small> + <i class="fa fa-exclamation-triangle"></i> + WARNING: All changes your own risk. Recommend to backup appdata + before + </small> + </div> <div class="form-group"> <label class="mb-1" for="app_username"> <h6 class="card-title mb-0">User/Multiplayer name</h6> @@ -23,24 +35,38 @@ Just rename, without account id reseting </small> </label> - <input id="app_username" class="adbdev form-control" value="" /> + <input id="app_username" class="adbdev form-control" value=""> </div> - <hr/> + <hr> </div> <div class="card-body text-center"> - <a class="adbdev btn btn-info mb-1" id="startAppBtn"><i class="fa fa-play"></i> Launch</a> - <a class="adbdev btn btn-danger mb-1" id="uninstallAppBtn"><i class="fa fa-trash-o"></i> Uninstall</a> - <a class="adbdev btn btn-info mb-1" id="backupAppBtn" ><i class="fa fa-download"></i> Backup app + obbs</a> - <a class="adbdev btn btn-info mb-1" id="backupDataBtn"><i class="fa fa-download"></i> Backup appdata</a> - <a class="adbdev btn btn-success mb-1 disabled" id="restoreDataBtn"><i class="fa fa-upload"></i> Restore appdata</a> - <a class="btn btn-primary mb-1 disabled" id="backupAppPath"><i class="fa fa-folder-open"></i> Open backup folder</a> + <a class="adbdev btn btn-info mb-1" id="startAppBtn" + ><i class="fa fa-play"></i> Launch</a + > + <a class="adbdev btn btn-danger mb-1" id="uninstallAppBtn" + ><i class="fa fa-trash-o"></i> Uninstall</a + > + <a class="adbdev btn btn-info mb-1" id="backupAppBtn" + ><i class="fa fa-download"></i> Backup app + obbs</a + > + <a class="adbdev btn btn-info mb-1" id="backupDataBtn" + ><i class="fa fa-download"></i> Backup appdata</a + > + <a class="adbdev btn btn-success mb-1 disabled" id="restoreDataBtn" + ><i class="fa fa-upload"></i> Restore appdata</a + > + <a class="btn btn-primary mb-1 disabled" id="backupAppPath" + ><i class="fa fa-folder-open"></i> Open backup folder</a + > </div> <div style="min-height: 45px"> - <small id="tools_transfer_state" ></small> + <small id="tools_transfer_state"></small> </div> </div> <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + Close + </button> </div> </div> </div> @@ -51,30 +77,30 @@ ipcRenderer.removeAllListeners('process_data'); ipcRenderer.on('process_data', (event, data) => { console.log('process_data received', data); - if (!data) return id('tools_transfer_state').innerHTML = ''; + if (!data) return (id('tools_transfer_state').innerHTML = ''); let line = `${data.cmd}: ${formatBytes(data.bytes)}`; if (data.size) { - line+= ` of ${formatBytes(data.size)} (${data.percentage}%)`; + line += ` of ${formatBytes(data.size)} (${data.percentage}%)`; } if (data.speedAvg) { - line+= ` - ${formatBytes(data.speedAvg)}/s (${formatEta(data.eta)})`; + line += ` - ${formatBytes(data.speedAvg)}/s (${formatEta(data.eta)})`; } const name = data.name.split('/').pop(); id('tools_transfer_state').innerHTML = `${line}<br/> ${name}`; }); - ipcRenderer.removeAllListeners('app_backup'); ipcRenderer.on('app_backup', (event, arg) => { $id('backupAppBtn').removeClass('disabled'); if (arg.error) { - id('tools_transfer_state').innerHTML = `<b class="text-danger">Backup Error: ${arg.error}</b>`; - } - else { - id('tools_transfer_state').innerHTML = `<b class="text-success">Backup finished</b>`; + id('tools_transfer_state').innerHTML = + `<b class="text-danger">Backup Error: ${arg.error}</b>`; + } else { + id('tools_transfer_state').innerHTML = + `<b class="text-success">Backup finished</b>`; } }); ipcRenderer.removeAllListeners('data_backup'); @@ -84,26 +110,30 @@ $id('backupAppPath').removeClass('disabled'); if (arg.error) { - id('tools_transfer_state').innerHTML = `<b class="text-danger">Backup Error: ${arg.error}</b>`; - } - else { - id('tools_transfer_state').innerHTML = `<b class="text-success">Backup finished</b>`; + id('tools_transfer_state').innerHTML = + `<b class="text-danger">Backup Error: ${arg.error}</b>`; + } else { + id('tools_transfer_state').innerHTML = + `<b class="text-success">Backup finished</b>`; } }); ipcRenderer.removeAllListeners('data_restore'); ipcRenderer.on('data_restore', (event, arg) => { $id('restoreDataBtn').removeClass('disabled'); if (arg.error) { - id('tools_transfer_state').innerHTML = `<b class="text-danger">Restore Error: ${arg.error}</b>`; - } - else { - id('tools_transfer_state').innerHTML = `<b class="text-success">Restore finished</b>`; + id('tools_transfer_state').innerHTML = + `<b class="text-danger">Restore Error: ${arg.error}</b>`; + } else { + id('tools_transfer_state').innerHTML = + `<b class="text-success">Restore finished</b>`; } }); ipcRenderer.removeAllListeners('app_config_set'); ipcRenderer.on('app_config_set', (event, { success, key, val }) => { - $(`#app_${key}`).val(val).addClass(success ? 'is-valid' : 'is-invalid'); + $(`#app_${key}`) + .val(val) + .addClass(success ? 'is-valid' : 'is-invalid'); }); ipcRenderer.removeAllListeners('app_tools'); @@ -111,12 +141,7 @@ console.log('app_tools msg came: ', arg); $id('processingModal').modal('hide'); - const { - pkg, - backupPath, - availableRestore, - availableConfig, - } = arg; + const { package, backupPath, availableRestore, availableConfig } = arg; $id('packageName').text(pkg); @@ -144,7 +169,9 @@ return true; }); $id('backupAppPath').click((e) => { - shell.openPath(path.join(remote.getGlobal('sidenoderHome'), 'backup_data', pkg)); + shell.openPath( + path.join(remote.getGlobal('sidenoderHome'), 'backup_data', package), + ); return true; }); @@ -158,12 +185,16 @@ if (availableConfig) { $id('configsCard').show(); $id('app_username') - .val(availableConfig.username) - .change(({ target }) => { - $(target).removeClass('is-valid'); - ipcRenderer.send('app_config_set', { pkg, key: 'username', val: target.value }); - return true; - }); + .val(availableConfig.username) + .change(({ target }) => { + $(target).removeClass('is-valid'); + ipcRenderer.send('app_config_set', { + package, + key: 'username', + val: target.value, + }); + return true; + }); } $id('appToolModal').modal('show'); @@ -183,4 +214,4 @@ ipcRenderer.send('app_backup', { location, pkg }); return path; } -</script> \ No newline at end of file +</script> diff --git a/views/modals/appinfo.twig b/views/modals/appinfo.twig index 0e4e350..de3bf80 100644 --- a/views/modals/appinfo.twig +++ b/views/modals/appinfo.twig @@ -1,18 +1,29 @@ <!-- Modal --> -<div class="modal" id="appinfoModal" > - <div role="document" style="margin: 1.75rem auto; min-width:800px; max-width:834px"> <!-- class="modal-dialog" --> +<div class="modal" id="appinfoModal"> + <div + role="document" + style="margin: 1.75rem auto; min-width: 800px; max-width: 834px" + > + <!-- class="modal-dialog" --> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="appinfoModalLabel">Unknown</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> <div class="modal-body"> - <div class="alert alert-warning"><small> - <i class="fa fa-exclamation-triangle"></i> - WARNING: Info auto parsed and can contains mistakes - </small></div> + <div class="alert alert-warning"> + <small> + <i class="fa fa-exclamation-triangle"></i> + WARNING: Info auto parsed and can contains mistakes + </small> + </div> <div class="text-center"> <div id="appinfoSlider" class="cbp-fwslider"> <ul></ul> @@ -20,29 +31,42 @@ </div> <div id="appinfoGenres" class="text-center mb-2"></div> <div class="mb-2" id="appinfoShortDesc"></div> - <div class="mb-2"><b>Supported laguages:</b> <span id="supportedLanguages">Unknown</span></div> + <div class="mb-2"> + <b>Supported laguages:</b> + <span id="supportedLanguages">Unknown</span> + </div> <div class="text-center"> - <a id="appinfoUrl" class="btn btn-primary" style="display: none">Open info on resource</a> - <a id="appinfoUrlDev" class="adbdev btn btn-info" style="display: none">Open info on device</a> - <a id="appinfoEventsBtn" class="btn btn-success" style="display: none">Show last events</a> + <a id="appinfoUrl" class="btn btn-primary" style="display: none" + >Open info on resource</a + > + <a + id="appinfoUrlDev" + class="adbdev btn btn-info" + style="display: none" + >Open info on device</a + > + <a id="appinfoEventsBtn" class="btn btn-success" style="display: none" + >Show last events</a + > </div> - <hr/> + <hr> <b>Description:</b> - <br/> + <br> <div id="appinfoText"></div> <div id="appinfoAbout" class="text-center"></div> </div> <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + Close + </button> </div> </div> </div> </div> - - <link rel="stylesheet" type="text/css" href="css/component.css" /> - <script src="js/modernizr.custom.js"></script> - <script src="js/jquery.cbpFWSlider.min.js"></script> +<link rel="stylesheet" type="text/css" href="css/component.css"> +<script src="js/modernizr.custom.js"></script> +<script src="js/jquery.cbpFWSlider.min.js"></script> <script> console.log('ONLOAD APPINFO MODAL'); const appinfoSlider = $id('appinfoSlider'); @@ -65,21 +89,21 @@ $id('processingModal').modal('hide'); console.log('app_info received'); // console.log('app_info received', arg); - if (!arg || !arg.success) return console.log(arg); + if (!arg || !arg.success) return console.log(arg); const data = arg.data; - if (!data) return; + if (!data) return; if (data.res == 'steam') { $id('appinfoText').addClass('text-center'); } // if (['steam', 'oculus'].includes(data.res)) { - id('appinfoEventsBtn').onclick = () => { - $id('processingModal').modal('show'); - ipcRenderer.send('app_events_info', {res: data.res, pkg: data.pkg}); - } + id('appinfoEventsBtn').onclick = () => { + $id('processingModal').modal('show'); + ipcRenderer.send('app_events_info', { res: data.res, pkg: data.pkg }); + }; - $id('appinfoEventsBtn').show(); + $id('appinfoEventsBtn').show(); // } $id('appinfoModalLabel').text(data.name || ''); @@ -88,18 +112,21 @@ $id('appinfoAbout').html(data.about_the_game || ''); if (data.supported_languages) { $id('supportedLanguages') - .html(data.supported_languages - .replace('русский', '<b>русский</b>') - .replace('Русский', '<b>Русский</b>') - ) - .parent().show(); + .html( + data.supported_languages + .replace('русский', '<b>русский</b>') + .replace('Русский', '<b>Русский</b>'), + ) + .parent() + .show(); } if (data.url) { id('appinfoUrl').onclick = () => shell.openExternal(data.url); $id('appinfoUrl').show(); if (data.res != 'steam') { - id('appinfoUrlDev').onclick = () => ipcRenderer.send('dev_open_url', data.url); + id('appinfoUrlDev').onclick = () => + ipcRenderer.send('dev_open_url', data.url); $id('appinfoUrlDev').show(); } } @@ -108,39 +135,40 @@ for (const genre of data.genres) { const genre_text = genre.description || genre; if (['Бесплатно', 'Free'].includes(genre_text)) continue; - $id('appinfoGenres').append(`<span class="badge badge-secondary">${genre_text}</span> `); + $id('appinfoGenres').append( + `<span class="badge badge-secondary">${genre_text}</span> `, + ); } } let slides = '<ul>'; if (data.header_image) { - slides+= `<li><img src="${data.header_image}" width="800" style="max-height: 405px;" alt="header"/></li>`; + slides += `<li><img src="${data.header_image}" width="800" style="max-height: 405px;" alt="header"/></li>`; } if (data.movies) { for (const movie of data.movies) { if (!movie.mp4) continue; - slides+= `<li><video width="800" style="max-height: 405px;" controls><source src="${movie.mp4['480']}" type="video/mp4"></video></li>`; + slides += `<li><video width="800" style="max-height: 405px;" controls><source src="${movie.mp4['480']}" type="video/mp4"></video></li>`; } } if (data.youtube) { for (const link of data.youtube) { - slides+= `<li><iframe width="800" height="405" src="${link}" frameborder="0" allowfullscreen></iframe></li>`; + slides += `<li><iframe width="800" height="405" src="${link}" frameborder="0" allowfullscreen></iframe></li>`; } } if (data.screenshots) { for (const screenshot of data.screenshots) { - slides+= `<li><img src="${screenshot.path_thumbnail}" width="800" style="max-height: 405px;" alt="screenshot_${screenshot.id}" /></li>`; + slides += `<li><img src="${screenshot.path_thumbnail}" width="800" style="max-height: 405px;" alt="screenshot_${screenshot.id}" /></li>`; } } - slides+= '</ul>'; + slides += '</ul>'; appinfoSlider[0].innerHTML = slides; - appinfoSlider.cbpFWSlider({current: 0}); + appinfoSlider.cbpFWSlider({ current: 0 }); $id('appinfoModal').modal('show'); }); - -</script> \ No newline at end of file +</script> diff --git a/views/modals/appinfo_events.twig b/views/modals/appinfo_events.twig index dfba961..7c2d3a8 100644 --- a/views/modals/appinfo_events.twig +++ b/views/modals/appinfo_events.twig @@ -1,72 +1,86 @@ <!-- Modal --> -<div class="modal" id="appinfoEventsModal" > - <div role="document" style="margin: 1.75rem auto; min-width:790px; max-width:44%"> <!-- class="modal-dialog" --> +<div class="modal" id="appinfoEventsModal"> + <div + role="document" + style="margin: 1.75rem auto; min-width: 790px; max-width: 44%" + > + <!-- class="modal-dialog" --> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="appinfoEventsModalLabel">Events:</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> <div class="modal-body"> - <div class="alert alert-warning"><small> - <i class="fa fa-exclamation-triangle"></i> - WARNING: Info auto parsed and can contains mistakes - </small></div> - <hr/> + <div class="alert alert-warning"> + <small> + <i class="fa fa-exclamation-triangle"></i> + WARNING: Info auto parsed and can contains mistakes + </small> + </div> + <hr> <div class="text-center"> - <a id="appinfoEventsUrl" class="btn btn-primary" style="display: none">Open info on resource</a> + <a id="appinfoEventsUrl" class="btn btn-primary" style="display: none" + >Open info on resource</a + > </div> - <hr/> - <div id="appinfoEventsBody"> + <hr> + <div id="appinfoEventsBody"></div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + Close + </button> </div> - <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> </div> </div> </div> -</div> -<style type="text/css"> - #appinfoEventsBody img { - max-width: 756px; - max-height: 480px; - } -</style> + <style type="text/css"> + #appinfoEventsBody img { + max-width: 756px; + max-height: 480px; + } + </style> -<script> - console.log('ONLOAD APPINFO_EVENTS MODAL'); - $id('appinfoEventsModal').on('hidden.bs.modal', () => { - console.log('appinfoEvents closed'); - id('appinfoEventsBody').innerHTML = ''; - $id('appinfoEventsUrl').hide(); - }) + <script> + console.log('ONLOAD APPINFO_EVENTS MODAL'); + $id('appinfoEventsModal').on('hidden.bs.modal', () => { + console.log('appinfoEvents closed'); + id('appinfoEventsBody').innerHTML = ''; + $id('appinfoEventsUrl').hide(); + }); - ipcRenderer.removeAllListeners('app_events_info'); - ipcRenderer.on('app_events_info', (event, arg) => { - $id('processingModal').modal('hide'); - console.log('app_events_info received'); - // console.log(arg); - if (!arg || !arg.success) return console.log(arg); - const data = arg.data; - if (!data) return; + ipcRenderer.removeAllListeners('app_events_info'); + ipcRenderer.on('app_events_info', (event, arg) => { + $id('processingModal').modal('hide'); + console.log('app_events_info received'); + // console.log(arg); + if (!arg || !arg.success) return console.log(arg); + const data = arg.data; + if (!data) return; - if (data.url) { - id('appinfoEventsUrl').onclick = () => shell.openExternal(data.url); - $id('appinfoEventsUrl').show(); - } + if (data.url) { + id('appinfoEventsUrl').onclick = () => shell.openExternal(data.url); + $id('appinfoEventsUrl').show(); + } - let events = ''; - for (const e of data.events) { - let event = ''; - if (e.date) event+= `<small class="float-right">${e.date}</small>`; - event+= `<h4>${e.title || ''}</h4>`; - if (e.contents) event+= `<div>${e.contents}</div>`; - events+= `${event}<hr/>`; - } - - id('appinfoEventsBody').innerHTML = events; + let events = ''; + for (const e of data.events) { + let event = ''; + if (e.date) event += `<small class="float-right">${e.date}</small>`; + event += `<h4>${e.title || ''}</h4>`; + if (e.contents) event += `<div>${e.contents}</div>`; + events += `${event}<hr/>`; + } - $id('appinfoEventsModal').modal('show'); - }); + id('appinfoEventsBody').innerHTML = events; -</script> \ No newline at end of file + $id('appinfoEventsModal').modal('show'); + }); + </script> +</div> diff --git a/views/modals/confirm.twig b/views/modals/confirm.twig index 415b7d6..9d13891 100644 --- a/views/modals/confirm.twig +++ b/views/modals/confirm.twig @@ -1,9 +1,22 @@ -<div class="modal fade" id="confirmModal" tabindex="-1" role="dialog" aria-labelledby="confirmModalLabel" data-backdrop="static" data-keyboard="false"> +<div + class="modal fade" + id="confirmModal" + tabindex="-1" + role="dialog" + aria-labelledby="confirmModalLabel" + data-backdrop="static" + data-keyboard="false" +> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="confirmModalLabel">Confirm</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> @@ -13,23 +26,40 @@ <i class="fa fa-info-circle"></i> <b>Notes</b>: </div> - <pre id="confirmModalNotesDesc" class="card bg-dark" style="white-space: pre-wrap;padding: 8pt;"></pre> - <hr/> + <pre + id="confirmModalNotesDesc" + class="card bg-dark" + style="white-space: pre-wrap; padding: 8pt" + ></pre> + <hr> </div> <div id="confirmModalInstallCont" style="display: none"> <div class="alert alert-warning mb-0"> <i class="fa fa-exclamation-triangle"></i> - WARNING: Folder contain the install instructions, auto sideloading maybe not fully + WARNING: Folder contain the install instructions, auto sideloading + maybe not fully </div> <b>Install instructions:</b> - <pre id="confirmModalInstallDesc" class="card bg-dark" style="white-space: pre-wrap;padding: 8pt;"></pre> - <hr/> + <pre + id="confirmModalInstallDesc" + class="card bg-dark" + style="white-space: pre-wrap; padding: 8pt" + ></pre> + <hr> </div> <div id="confirmModalMessage"></div> </div> <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">No</button> - <button type="button" id="confirmSideloadButton" class="btn btn-primary disabled">Yes</button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + No + </button> + <button + type="button" + id="confirmSideloadButton" + class="btn btn-primary disabled" + > + Yes + </button> </div> </div> </div> @@ -37,16 +67,18 @@ <script> //setup listener for confirm btn, takes path form data dir, and sends start_sideload - $id('confirmSideloadButton').on('click', function(){ + $id('confirmSideloadButton').on('click', function () { $id('confirmModal').modal('hide'); // - data = {path: $id('confirmSideloadButton').data('path'), update: $id('confirmSideloadButton').data('update')} + data = { + path: $id('confirmSideloadButton').data('path'), + update: $id('confirmSideloadButton').data('update'), + }; loadInclude('modals/sideload.twig', 'sideloadmodaldiv', () => { ipcRenderer.send('start_sideload', data); }); }); - ipcRenderer.removeAllListeners('ask_sideload'); ipcRenderer.on('ask_sideload', (event, arg) => { console.log('ask_sideload msg came ! ', arg); @@ -55,28 +87,28 @@ if (!success) return; const lastslashindex = install.path.lastIndexOf('/'); - const file= install.path.substring(lastslashindex + 1); + const file = install.path.substring(lastslashindex + 1); $id('confirmSideloadButton').data('path', install.path); $id('confirmSideloadButton').data('update', !!update); - $id('confirmModalMessage').html('Do you want to sideload: <br><b>' + file + '</b>'); + $id('confirmModalMessage').html( + 'Do you want to sideload: <br><b>' + file + '</b>', + ); if (install.install_desc) { $id('confirmModalInstallDesc').html(install.install_desc); $id('confirmModalInstallCont').show(); - } - else { + } else { $id('confirmModalInstallCont').hide(); } if (install.notes) { $id('confirmModalNotesDesc').html(install.notes); $id('confirmModalNotesCont').show(); - } - else { + } else { $id('confirmModalNotesCont').hide(); } $id('confirmModal').modal('show'); }); -</script> \ No newline at end of file +</script> diff --git a/views/modals/donate.twig b/views/modals/donate.twig index 06efdcc..46690c2 100644 --- a/views/modals/donate.twig +++ b/views/modals/donate.twig @@ -3,23 +3,45 @@ <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="donateModalLabel">Donate</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> <div class="modal-body text-center" id="donateModalBody"> Your donation will motivate me to work on lot of new features. - <br>Thanks and please don't forget add your nickname at description + <br> + Thanks and please don't forget add your nickname at description <hr> - <a class="btn btn-outline-info disabled" nonclick="shell.openExternal('https://paypal.me/1xyst')"><i class="fa fa-paypal"></i> PayPal (blocked)</a> - <a class="btn btn-outline-light" onclick="shell.openExternal('https://yoomoney.ru/to/410011725792647')"><i class="fa fa-money"></i> YooMoney</a> + <a + class="btn btn-outline-info disabled" + nonclick="shell.openExternal('https://paypal.me/1xyst')" + ><i class="fa fa-paypal"></i> PayPal (blocked)</a + > + <a + class="btn btn-outline-light" + onclick="shell.openExternal('https://yoomoney.ru/to/410011725792647')" + ><i class="fa fa-money"></i> YooMoney</a + > <hr> <img class="mb-2" alt="eth-qr" src="./img/qreth.jpg"> - <br> ETH or BSC or POLYGON blockchains - <br><input readonly="readonly" value="0xc3F7cA87e558C423A546Ed3B6B037424454c0000" size="40" onclick="copyInput(this)"> + <br> + ETH or BSC or POLYGON blockchains <br> + <input + readonly="readonly" + value="0xc3F7cA87e558C423A546Ed3B6B037424454c0000" + size="40" + onclick="copyInput(this)" + > </div> <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + Close + </button> </div> </div> </div> @@ -28,9 +50,9 @@ <script> ipcRenderer.send('change_config', { key: 'donShown', val: Date.now() }); $id('donateModal') - .modal('show') - .on('hidden.bs.modal', () => { - console.log('donateModal closed'); - id('donatemodaldiv').innerHTML = ''; - }); -</script> \ No newline at end of file + .modal('show') + .on('hidden.bs.modal', () => { + console.log('donateModal closed'); + id('donatemodaldiv').innerHTML = ''; + }); +</script> diff --git a/views/modals/installed.twig b/views/modals/installed.twig index e3a73c1..4463e14 100644 --- a/views/modals/installed.twig +++ b/views/modals/installed.twig @@ -1,44 +1,62 @@ -<div class="modal" id="installedModal" > - <div role="document" style="margin: 1.75rem auto; min-width:800px; max-width:48%"> <!-- class="modal-dialog" --> +<div class="modal" id="installedModal"> + <div + role="document" + style="margin: 1.75rem auto; min-width:800px; max-width:48%" + > + <!-- class="modal-dialog" --> <div class="modal-content"> <div class="modal-header"> - <h5 class="modal-title" id="installedModalLabel"> + <h5 class="modal-title" id="installedModalLabel"> <div class="float-left mt-1"> <i class="fa fa-list"></i> Installed APP's </div> <div class="float-right ml-4"> - <a class="btn btn-md btn-primary" onclick="openSearch()"> <i class="fa fa-search"></i> Search</a> - <a onclick="getUpdates()" id="updateBadge" class="adbdev btn btn-info btn-md disabled"><i class="fa fa-search"></i> Check mount for updates</span></a> + <a class="btn btn-md btn-primary" onclick="openSearch()"> + <i class="fa fa-search"></i> Search</a + > + <a + onclick="getUpdates()" + id="updateBadge" + class="adbdev btn btn-info btn-md disabled" + ><i class="fa fa-search"></i> Check mount for updates</a + > </div> </h5> <button - class="btn btn-md btn-primary dropdown-toggle" - type="button" - onclick="$id('appmodaldropdownmenu').toggle()"> - <i class="fa fa-sort"></i> Sort By - </button> - <div - id="appmodaldropdownmenu" - class="dropdown-menu" - style="right: 0; left: auto; top: auto"> - <a class="dropdown-item" onclick="sortApps('simplename', 1)"> - <i class="fa fa-sort-alpha-asc"></i> - App name</a - > - <a class="dropdown-item" onclick="sortApps('simplename', 0)"> - <i class="fa fa-sort-alpha-desc"></i> - App name (decending)</a - > - <a class="dropdown-item" onclick="sortApps('packagename', 1)"> - <i class="fa fa-sort-amount-asc"></i> - Package name</a - > - <a class="dropdown-item" onclick="sortApps('packagename', 0)"> - <i class="fa fa-sort-amount-desc"></i> - Package name (decending)</a - > - </div> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + class="btn btn-md btn-primary dropdown-toggle" + type="button" + onclick="$id('appmodaldropdownmenu').toggle()" + > + <i class="fa fa-sort"></i> Sort By + </button> + <div + id="appmodaldropdownmenu" + class="dropdown-menu" + style="right: 0; left: auto; top: auto" + > + <a class="dropdown-item" onclick="sortApps('simplename', 1)"> + <i class="fa fa-sort-alpha-asc"></i> + App name</a + > + <a class="dropdown-item" onclick="sortApps('simplename', 0)"> + <i class="fa fa-sort-alpha-desc"></i> + App name (decending)</a + > + <a class="dropdown-item" onclick="sortApps('packagename', 1)"> + <i class="fa fa-sort-amount-asc"></i> + Package name</a + > + <a class="dropdown-item" onclick="sortApps('packagename', 0)"> + <i class="fa fa-sort-amount-desc"></i> + Package name (decending)</a + > + </div> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> @@ -49,7 +67,9 @@ </table> </div> <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + Close + </button> </div> </div> </div> @@ -63,16 +83,16 @@ if (remote.getGlobal('adbDevice') && remote.getGlobal('mounted')) { $id('updateBadge').removeClass('disabled'); } -function sortApps(key, asc) { - const suffix = asc ? "" : "-desc"; - sortAppElements($id("intalledTable"), key, asc); - $id("appmodaldropdownmenu").hide(); - ipcRenderer.send("change_config", { key: "sortApps", val: key + suffix }); + function sortApps(key, asc) { + const suffix = asc ? '' : '-desc'; + sortAppElements($id('intalledTable'), key, asc); + $id('appmodaldropdownmenu').hide(); + ipcRenderer.send('change_config', { key: 'sortApps', val: key + suffix }); } function sortAppElements(el, key, asc) { - const sortByName = key.startsWith("simplename"); + const sortByName = key.startsWith('simplename'); el.html( - el.find("tr").sort((a, b) => { + el.find('tr').sort((a, b) => { const valA = ( sortByName ? a.dataset.simplename : a.dataset.packagename ).toLowerCase(); @@ -89,4 +109,4 @@ function sortApps(key, asc) { }), ); } -</script> \ No newline at end of file +</script> diff --git a/views/modals/processing.twig b/views/modals/processing.twig index fcb08a5..9c4bad2 100644 --- a/views/modals/processing.twig +++ b/views/modals/processing.twig @@ -1,14 +1,22 @@ -<div class="modal" id="processingModal" tabindex="-1" role="dialog" aria-labelledby="processingModalLabel" data-backdrop="static" data-keyboard="false"> +<div + class="modal" + id="processingModal" + tabindex="-1" + role="dialog" + aria-labelledby="processingModalLabel" + data-backdrop="static" + data-keyboard="false" +> <div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-content text-center"> - <div class="modal-header" style="display:block"> - <h5 class="modal-title" id="processingModalLabel"><i id="processingRefresh" class="fa fa-refresh fa-spin"></i> Processing</h5> - </div> - <div class="modal-body" id="processingBody"> - Please wait.. - </div> - <div class="modal-footer" id="processingFooter"> + <div class="modal-header" style="display: block"> + <h5 class="modal-title" id="processingModalLabel"> + <i id="processingRefresh" class="fa fa-refresh fa-spin"></i> + Processing + </h5> </div> + <div class="modal-body" id="processingBody">Please wait..</div> + <div class="modal-footer" id="processingFooter"></div> </div> </div> </div> diff --git a/views/modals/prompt.twig b/views/modals/prompt.twig index 8871a70..2e424dc 100644 --- a/views/modals/prompt.twig +++ b/views/modals/prompt.twig @@ -1,23 +1,41 @@ -<div class="modal fade" id="promptModal" tabindex="-1" role="dialog" aria-labelledby="promptModalLabel" data-backdrop="static" data-keyboard="false"> +<div + class="modal fade" + id="promptModal" + tabindex="-1" + role="dialog" + aria-labelledby="promptModalLabel" + data-backdrop="static" + data-keyboard="false" +> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="promptModalLabel"></h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> <div class="modal-body"> <div id="promptModalMessage"></div> <div class="input-group"> - <input class="form-control ml-3" id="promptModalInput"/> + <input class="form-control ml-3" id="promptModalInput"> <a class="btn btn-info mr-3" id="promptModalBrowse"> - <i class="fa fa-folder-open" title="Browse.."></i></a> + <i class="fa fa-folder-open" title="Browse.."></i + ></a> </div> </div> <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">No</button> - <button type="button" id="promptModalButton" class="btn btn-primary">Yes</button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + No + </button> + <button type="button" id="promptModalButton" class="btn btn-primary"> + Yes + </button> </div> </div> </div> @@ -38,7 +56,6 @@ input.value = res.filePaths[0]; }; - async function promptDialog(title, message, inputValue, dir = false) { id('promptModalLabel').innerHTML = title; id('promptModalMessage').innerHTML = message; @@ -48,8 +65,7 @@ const browse = $id('promptModalBrowse'); if (dir) { browse.show(); - } - else { + } else { browse.hide(); } @@ -60,10 +76,10 @@ id('promptModalButton').onclick = () => { res(input.value); modal.modal('hide'); - } + }; modal.on('hide.bs.modal', (e) => { return res(null); }); }); } -</script> \ No newline at end of file +</script> diff --git a/views/modals/scrcpy.twig b/views/modals/scrcpy.twig index 536cb45..98cdc1c 100644 --- a/views/modals/scrcpy.twig +++ b/views/modals/scrcpy.twig @@ -1,10 +1,19 @@ <!-- Modal --> -<div class="modal" id="scrcpyModal" > - <div role="document" style="margin: 1.75rem auto; min-width:580px; max-width:40%"> <!-- class="modal-dialog" --> +<div class="modal" id="scrcpyModal"> + <div + role="document" + style="margin: 1.75rem auto; min-width: 580px; max-width: 40%" + > + <!-- class="modal-dialog" --> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="scrcpyModalLabel">Screen share</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> @@ -12,14 +21,17 @@ <div class="card-body"> <div class="alert alert-warning"> <i class="fa fa-exclamation-triangle"></i> - WARNING: You need installed <a class="btn btn-sm btn-info" onclick="shell.openExternal('https://github.com/Genymobile/scrcpy#linux')">scrcpy</a> + WARNING: You need installed + <a + class="btn btn-sm btn-info" + onclick="shell.openExternal('https://github.com/Genymobile/scrcpy#linux')" + >scrcpy</a + > </div> <div class="form-group"> <label class="mb-1" for="scrcpyBitrate"> <h6 class="card-title mb-0">Video BitRate</h6> - <small class="card-text"> - Performance option - </small> + <small class="card-text"> Performance option </small> </label> <select id="scrcpyBitrate" class="adbdev custom-select"></select> </div> @@ -27,10 +39,18 @@ <label class="mb-1" for="scrcpyCrop"> <h6 class="card-title mb-0">Crop</h6> <small class="card-text"> - Crop the device screen on the server. The values are expressed in the device natural orientation (typically, portrait for a phone, landscape for a tablet). Any MaxSize value is computed on the cropped size + Crop the device screen on the server. The values are expressed + in the device natural orientation (typically, portrait for a + phone, landscape for a tablet). Any MaxSize value is computed on + the cropped size </small> </label> - <input id="scrcpyCrop" class="adbdev form-control col-6" style="display:inline-block;" value="" /> + <input + id="scrcpyCrop" + class="adbdev form-control col-6" + style="display: inline-block" + value="" + > <a class="btn btn-info" id="crop_quest2">Quest 2</a> <a class="btn btn-info" id="crop_quest1">Quest 1</a> </div> @@ -41,80 +61,98 @@ Limit the frame rate of screen capture </small> </label> - <input id="scrcpyFps" class="adbdev form-control" value="" /> + <input id="scrcpyFps" class="adbdev form-control" value=""> </div> <div class="form-group"> <label class="mb-1" for="scrcpySize"> <h6 class="card-title mb-0">Max Size</h6> <small class="card-text"> - Limit both the width and height of the video to value. The other dimension is computed so that the device aspect-ratio is preserved + Limit both the width and height of the video to value. The other + dimension is computed so that the device aspect-ratio is + preserved </small> </label> - <input id="scrcpySize" class="adbdev form-control" value="" /> + <input id="scrcpySize" class="adbdev form-control" value=""> </div> <div class="form-check mb-3"> <label for="scrcpyWindow" class="form-check-label"> - <input class="form-check-input" type="checkbox" id="scrcpyWindow"> + <input + class="form-check-input" + type="checkbox" + id="scrcpyWindow" + > Cast in window </label> </div> <div class="form-check mb-3"> <label for="scrcpyControl" class="form-check-label"> - <input class="form-check-input" type="checkbox" id="scrcpyControl"> + <input + class="form-check-input" + type="checkbox" + id="scrcpyControl" + > Enable remote control </label> </div> </div> </div> <div class="modal-footer"> - <button id="scrcpy_start" type="button" class="btn btn-primary" data-dismiss="modal">Start stream</button> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button + id="scrcpy_start" + type="button" + class="btn btn-primary" + data-dismiss="modal" + > + Start stream + </button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + Close + </button> </div> </div> </div> </div> -<script>{ - console.log('ONLOAD scrcpy MODAL'); - const OPT_KEYS = [ - 'scrcpyBitrate', - 'scrcpyCrop', - 'scrcpySize', - ]; - const CHKBOXES = [ - 'scrcpyWindow', - 'scrcpyControl', - ]; +<script> + { + console.log('ONLOAD scrcpy MODAL'); + const OPT_KEYS = ['scrcpyBitrate', 'scrcpyCrop', 'scrcpySize']; + const CHKBOXES = ['scrcpyWindow', 'scrcpyControl']; - let options = ''; - for (let i = 1; i <= 10; i++) { - options+= `<option value="${i}">${i} Mbits/s</option>`; - } - id('scrcpyBitrate').innerHTML = options; + let options = ''; + for (let i = 1; i <= 10; i++) { + options += `<option value="${i}">${i} Mbits/s</option>`; + } + id('scrcpyBitrate').innerHTML = options; - for (const key of OPT_KEYS) { - $id(key) - .val(remote.getGlobal('currentConfiguration')[key]) - .on('change', (e) => { - ipcRenderer.send('change_config', {key, val: e.target.value}); - return; - }); - } + for (const key of OPT_KEYS) { + $id(key) + .val(remote.getGlobal('currentConfiguration')[key]) + .on('change', (e) => { + ipcRenderer.send('change_config', { key, val: e.target.value }); + return; + }); + } - for (const key of CHKBOXES) { - const el = $id('' + key); - if (remote.getGlobal('currentConfiguration')[key]) { - el.prop('checked', true); + for (const key of CHKBOXES) { + const el = $id('' + key); + if (remote.getGlobal('currentConfiguration')[key]) { + el.prop('checked', true); + } + + el.on('change.bootstrapSwitch', (e) => { + ipcRenderer.send('change_config', { key, val: e.target.checked }); + }); } - el.on('change.bootstrapSwitch', (e) => { - ipcRenderer.send('change_config', {key, val: e.target.checked}); + $id('crop_quest1').on('click', () => + $id('scrcpyCrop').val('1280:720:1500:350').trigger('change'), + ); + $id('crop_quest2').on('click', () => + $id('scrcpyCrop').val('1600:900:2017:510').trigger('change'), + ); + + $id('scrcpy_start').on('click', () => { + ipcRenderer.send('scrcpy_start'); }); } - - $id('crop_quest1').on('click', () => $id('scrcpyCrop').val('1280:720:1500:350').trigger('change')); - $id('crop_quest2').on('click', () => $id('scrcpyCrop').val('1600:900:2017:510').trigger('change')); - - $id('scrcpy_start').on('click', () => { - ipcRenderer.send('scrcpy_start'); - }); -}</script> \ No newline at end of file +</script> diff --git a/views/modals/sideload-queue.twig b/views/modals/sideload-queue.twig index 10df377..35386eb 100644 --- a/views/modals/sideload-queue.twig +++ b/views/modals/sideload-queue.twig @@ -1,16 +1,34 @@ <!-- Modal --> -<div class="modal fade modal-right" id="sideloadQueueModal" tabindex="-1" role="dialog" aria-labelledby="sideloadQueueModalLabel" data-backdrop="static" data-keyboard="false"> +<div + class="modal fade modal-right" + id="sideloadQueueModal" + tabindex="-1" + role="dialog" + aria-labelledby="sideloadQueueModalLabel" + data-backdrop="static" + data-keyboard="false" +> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> - <h5 class="modal-title" id="sideloadModalLabel"><i id="sideloadRefresh" class="fa fa-refresh"></i> Sideload queue</h5> <!-- fa-spin --> + <h5 class="modal-title" id="sideloadModalLabel"> + <i id="sideloadRefresh" class="fa fa-refresh"></i> Sideload queue + </h5> + <!-- fa-spin --> <small id="sideload_transfer_state" class="text-right"></small> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> <div class="modal-body"> - <table class="table table-striped-rookie table-hover table-fixed-header text-center"> + <table + class="table table-striped-rookie table-hover table-fixed-header text-center" + > <thead> <th>Name:</th> <th>Active process</th> @@ -20,8 +38,11 @@ <tbody id="sideloadQueue"> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-success"><i class="fa fa-check-circle-o"></i></span> - TestfdsfsdfsdfsTestfdsfsdfsdfsTestfdsfsdfsdfs TestfdsfsdfsdfsTestfdsfsdfsdfsTest fds fs dfs dfs.apk + <span class="btn-sm btn-success" + ><i class="fa fa-check-circle-o"></i + ></span> + TestfdsfsdfsdfsTestfdsfsdfsdfsTestfdsfsdfsdfs + TestfdsfsdfsdfsTestfdsfsdfsdfsTest fds fs dfs dfs.apk </td> <td> <span class="processText">Check device</span> @@ -35,8 +56,11 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-warning"><i class="fa fa-refresh fa-spin"></i></span> - Testfdsfsdfsdfs TestfdsfsdfsdfsTestfdsfsdfsdfsTest fds fs dfs dfs.apk + <span class="btn-sm btn-warning" + ><i class="fa fa-refresh fa-spin"></i + ></span> + Testfdsfsdfsdfs TestfdsfsdfsdfsTestfdsfsdfsdfsTest fds fs dfs + dfs.apk </td> <td> <span class="processText">Download APK</span> @@ -44,12 +68,13 @@ <td> <span class="processBadge badge badge-success">success</span> </td> - <td> - </td> + <td></td> </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-danger"><i class="fa fa-times-circle"></i></span> + <span class="btn-sm btn-danger" + ><i class="fa fa-times-circle"></i + ></span> Test fds fs dfs dfsTestfdsfsdfsdfs.apk </td> <td> @@ -59,7 +84,9 @@ <span class="processBadge badge badge-danger">fail</span> </td> <td> - <a class="actionBadge btn-sm btn-danger" title="retry"><i class="fa fa-refresh"></i></a> + <a class="actionBadge btn-sm btn-danger" title="retry" + ><i class="fa fa-refresh"></i + ></a> </td> </tr> <tr id="queue-1gdfvd3"> @@ -79,7 +106,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -94,7 +123,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -109,7 +140,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -124,7 +157,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -139,7 +174,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -154,7 +191,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -169,7 +208,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -184,7 +225,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -199,7 +242,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -214,7 +259,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -229,7 +276,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -244,7 +293,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -259,7 +310,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -274,7 +327,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -289,7 +344,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -304,7 +361,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -319,7 +378,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -334,7 +395,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -349,7 +412,9 @@ </tr> <tr id="queue-1gdfvd3"> <td> - <span class="btn-sm btn-secondary"><i class="fa fa-refresh"></i></span> + <span class="btn-sm btn-secondary" + ><i class="fa fa-refresh"></i + ></span> Test fds fs dfs dfs.apk </td> <td> @@ -366,15 +431,21 @@ </table> </div> <div class="modal-footer"> - <a class="btn btn-primary"><i class="fa fa-refresh"></i> Clear not in progress</a> - <a class="btn btn-success"><i class="fa fa-refresh"></i> Clear success</a> + <a class="btn btn-primary" + ><i class="fa fa-refresh"></i> Clear not in progress</a + > + <a class="btn btn-success" + ><i class="fa fa-refresh"></i> Clear success</a + > <a class="btn btn-danger"><i class="fa fa-refresh"></i> Retry failed</a> </div> </div> </div> </div> -<a class="btn btn-info btn-floating btn-lg" id="showQueue" onclick="showQueue()">1</a> +<a class="btn btn-info btn-floating btn-lg" id="showQueue" onclick="showQueue()" + >1</a +> <script> $id('showQueue').show(100); @@ -383,7 +454,10 @@ } function hash(s) { - const r = s.split('').reduce((a,b)=>(((a << 5)-a)+b.charCodeAt(0))|0,0).toString(18); + const r = s + .split('') + .reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0) + .toString(18); return r < 0 ? 'm' + Math.abs(r) : r; } @@ -404,15 +478,15 @@ ipcRenderer.removeAllListeners('process_data'); ipcRenderer.on('process_data', (event, data) => { console.log('process_data received', data); - if (!data) return id('sideload_transfer_state').innerHTML = ''; + if (!data) return (id('sideload_transfer_state').innerHTML = ''); let line = `${data.cmd}: ${formatBytes(data.bytes)}`; if (data.size) { - line+= ` of ${formatBytes(data.size)} (${data.percentage}%)`; + line += ` of ${formatBytes(data.size)} (${data.percentage}%)`; } if (data.speedAvg) { - line+= ` - ${formatBytes(data.speedAvg)}/s (${formatEta(data.eta)})`; + line += ` - ${formatBytes(data.speedAvg)}/s (${formatEta(data.eta)})`; } const name = data.name.split('/').pop(); @@ -427,10 +501,10 @@ if (!success) return; const lastslashindex = path.lastIndexOf('/'); - const file= path.substring(lastslashindex + 1) + const file = path.substring(lastslashindex + 1); $id('sideloadModal').modal('show'); - id('sideloadText').innerHTML = 'pending: <br/><b>'+file+'</b>'; + id('sideloadText').innerHTML = 'pending: <br/><b>' + file + '</b>'; }); ipcRenderer.removeAllListeners('sideload_process'); @@ -482,7 +556,9 @@ badgeState(job.find('.copyObbDoneBadge'), arg.push_obb); } - remote.getGlobal('win').setProgressBar($('.badge-warning').length / $('.badge').length); + remote + .getGlobal('win') + .setProgressBar($('.badge-warning').length / $('.badge').length); if (arg.done) { badgeState(job.find('.sideloadDoneBadge'), arg.done); @@ -497,14 +573,18 @@ .removeClass('fa-spin') .addClass('fa-check-circle-o'); - if (!arg.error) remote.getGlobal('notify')('Sucesfully sideloaded', arg.location, 'low'); + if (!arg.error) + remote.getGlobal('notify')( + 'Sucesfully sideloaded', + arg.location, + 'low', + ); } if (arg.update) { getUpdates(); } - if (arg.error) { $id('sideloadError').text(arg.error); $id('sideloadErrorArea').show(); @@ -512,9 +592,7 @@ } }); - function sideloadAddQueue(data) { - - } + function sideloadAddQueue(data) {} function badgeState(badge, text) { badge.text(text); @@ -528,15 +606,14 @@ const parts = text.split('/'); if (parts[0] == parts[1]) state = 'success'; else return; - } - else return; + } else return; badge.removeClass('badge-warning').addClass('badge-' + state); } - $id('sideloadDoneBtn').on('click', function(){ + $id('sideloadDoneBtn').on('click', function () { remote.getGlobal('win').setProgressBar(0); $id('sideloadModal').modal('hide'); // $('.modal-backdrop').remove(); }); -</script> \ No newline at end of file +</script> diff --git a/views/modals/sideload.twig b/views/modals/sideload.twig index c4dfcdf..1038f1e 100644 --- a/views/modals/sideload.twig +++ b/views/modals/sideload.twig @@ -1,99 +1,141 @@ <!-- Modal --> -<div class="modal fade" id="sideloadModal" tabindex="-1" role="dialog" aria-labelledby="sideloadModalLabel" data-backdrop="static" data-keyboard="false"> +<div + class="modal fade" + id="sideloadModal" + tabindex="-1" + role="dialog" + aria-labelledby="sideloadModalLabel" + data-backdrop="static" + data-keyboard="false" +> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> - <h5 class="modal-title" id="sideloadModalLabel"><i id="sideloadRefresh" class="fa fa-refresh fa-spin"></i> Sideloading</h5> + <h5 class="modal-title" id="sideloadModalLabel"> + <i id="sideloadRefresh" class="fa fa-refresh fa-spin"></i> Sideloading + </h5> <small id="sideload_transfer_state" class="text-right"></small> </div> <div class="modal-body"> <span id="sideloadText"></span> - <br/> + <br> <div class="row"> - <div class="col-4"> - Check device: + <div class="col-4">Check device:</div> + <div> + <span id="deviceBadge" class="badge badge-warning">pending</span> </div> - <div><span id="deviceBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Download APK: + <div class="col-4">Download APK:</div> + <div> + <span id="downloadDoneBadge" class="badge badge-warning" + >pending</span + > </div> - <div><span id="downloadDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Read Packageinfo: + <div class="col-4">Read Packageinfo:</div> + <div> + <span id="aaptDoneBadge" class="badge badge-warning">pending</span> </div> - <div><span id="aaptDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Check if installed: + <div class="col-4">Check if installed:</div> + <div> + <span id="checkDoneBadge" class="badge badge-warning">pending</span> </div> - <div><span id="checkDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Backup Appdata: + <div class="col-4">Backup Appdata:</div> + <div> + <span id="backupDoneBadge" class="badge badge-warning" + >pending</span + > </div> - <div><span id="backupDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Uninstall APK: + <div class="col-4">Uninstall APK:</div> + <div> + <span id="uninstallDoneBadge" class="badge badge-warning" + >pending</span + > </div> - <div><span id="uninstallDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Install APK: + <div class="col-4">Install APK:</div> + <div> + <span id="apkDoneBadge" class="badge badge-warning">pending</span> </div> - <div><span id="apkDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Restore Appdata: + <div class="col-4">Restore Appdata:</div> + <div> + <span id="restoreDoneBadge" class="badge badge-warning" + >pending</span + > </div> - <div><span id="restoreDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Remove OBB's: + <div class="col-4">Remove OBB's:</div> + <div> + <span id="removeObbDoneBadge" class="badge badge-warning" + >pending</span + > </div> - <div><span id="removeObbDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Download OBB's: + <div class="col-4">Download OBB's:</div> + <div> + <span id="downloadObbDoneBadge" class="badge badge-warning" + >pending</span + > </div> - <div><span id="downloadObbDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Copy OBB's: + <div class="col-4">Copy OBB's:</div> + <div> + <span id="copyObbDoneBadge" class="badge badge-warning" + >pending</span + > </div> - <div><span id="copyObbDoneBadge" class="badge badge-warning">pending</span></div> </div> <div class="row"> - <div class="col-4"> - Sideload finished: + <div class="col-4">Sideload finished:</div> + <div> + <span id="sideloadDoneBadge" class="badge badge-warning" + >pending</span + > </div> - <div><span id="sideloadDoneBadge" class="badge badge-warning">pending</span></div> </div> <div id="sideloadErrorArea" style="display: none"> - <a class="btn btn-sm btn-outline-danger" data-toggle="collapse" href="#collapseError" aria-expanded="false" aria-controls="collapseError"> + <a + class="btn btn-sm btn-outline-danger" + data-toggle="collapse" + href="#collapseError" + aria-expanded="false" + aria-controls="collapseError" + > <i class="fa fa-angle-down"></i> Fail log </a> <div class="collapse" id="collapseError"> - <pre id="sideloadError" class="card bg-dark" style="white-space: pre-wrap;padding: 8pt;"> + <pre + id="sideloadError" + class="card bg-dark" + style="white-space: pre-wrap;padding: 8pt;" + > </pre> </div> </div> </div> <div class="modal-footer"> {# <button type="button" class="btn btn-secondary disabled" data-dismiss="modal">Close</button> #} - <button type="button" id="sideloadDoneBtn" class="btn btn-primary disabled">Please wait for the process to finish</button> + <button + type="button" + id="sideloadDoneBtn" + class="btn btn-primary disabled" + > + Please wait for the process to finish + </button> </div> </div> </div> @@ -102,15 +144,15 @@ ipcRenderer.removeAllListeners('process_data'); ipcRenderer.on('process_data', (event, data) => { console.log('process_data received', data); - if (!data) return id('sideload_transfer_state').innerHTML = ''; + if (!data) return (id('sideload_transfer_state').innerHTML = ''); let line = `${data.cmd}: ${formatBytes(data.bytes)}`; if (data.size) { - line+= ` of ${formatBytes(data.size)} (${data.percentage}%)`; + line += ` of ${formatBytes(data.size)} (${data.percentage}%)`; } if (data.speedAvg) { - line+= ` - ${formatBytes(data.speedAvg)}/s (${formatEta(data.eta)})`; + line += ` - ${formatBytes(data.speedAvg)}/s (${formatEta(data.eta)})`; } const name = data.name.split('/').pop(); @@ -125,10 +167,10 @@ if (!success) return; const lastslashindex = path.lastIndexOf('/'); - const file = path.substring(lastslashindex + 1) + const file = path.substring(lastslashindex + 1); $id('sideloadModal').modal('show'); - id('sideloadText').innerHTML = 'pending: <br/><b>'+file+'</b>'; + id('sideloadText').innerHTML = 'pending: <br/><b>' + file + '</b>'; }); ipcRenderer.removeAllListeners('sideload_process'); @@ -179,7 +221,9 @@ badgeState($id('copyObbDoneBadge'), arg.push_obb); } - remote.getGlobal('win').setProgressBar($('.badge-warning').length / $('.badge').length); + remote + .getGlobal('win') + .setProgressBar($('.badge-warning').length / $('.badge').length); if (arg.done) { badgeState($id('sideloadDoneBadge'), arg.done); @@ -194,14 +238,18 @@ .removeClass('fa-spin') .addClass('fa-check-circle-o'); - if (!arg.error) remote.getGlobal('notify')('Sucesfully sideloaded', arg.location, 'low'); + if (!arg.error) + remote.getGlobal('notify')( + 'Sucesfully sideloaded', + arg.location, + 'low', + ); } if (arg.update) { getUpdates(); } - if (arg.error) { $id('sideloadError').text(arg.error); $id('sideloadErrorArea').show(); @@ -221,15 +269,14 @@ const parts = text.split('/'); if (parts[0] == parts[1]) state = 'success'; else return; - } - else return; + } else return; badge.removeClass('badge-warning').addClass('badge-' + state); } - $id('sideloadDoneBtn').on('click', function(){ + $id('sideloadDoneBtn').on('click', function () { remote.getGlobal('win').setProgressBar(0); $id('sideloadModal').modal('hide'); // $('.modal-backdrop').remove(); }); -</script> \ No newline at end of file +</script> diff --git a/views/modals/tweaks.twig b/views/modals/tweaks.twig index 12edc9d..f009fd2 100644 --- a/views/modals/tweaks.twig +++ b/views/modals/tweaks.twig @@ -1,17 +1,29 @@ -<div class="modal" id="tweaksModal" > - <div role="document" style="margin: 1.75rem auto; min-width:800px; max-width:50%"> <!-- class="modal-dialog" --> +<div class="modal" id="tweaksModal"> + <div + role="document" + style="margin: 1.75rem auto; min-width: 800px; max-width: 50%" + > + <!-- class="modal-dialog" --> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="tweaksModalLabel">Device Tweaks</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > <span>×</span> </button> </div> <div class="modal-body"> - <div class="alert alert-warning"><small> - <i class="fa fa-exclamation-triangle"></i> - WARNING: All changes your own risk. Some of this tweaks may reset to default when you restart your headset - </small></div> + <div class="alert alert-warning"> + <small> + <i class="fa fa-exclamation-triangle"></i> + WARNING: All changes your own risk. Some of this tweaks may reset to + default when you restart your headset + </small> + </div> <div class="card bg-primary mb-2"> <div class="card-header">Tools:</div> <div class="card-body"> @@ -19,7 +31,11 @@ <a id="scrcpyCfg" class="adbdev btn btn-primary"> <i class="fa fa-share-square"></i> Screen share </a> - <a id="enableMTP" class="adbdev btn btn-primary" title="mount device as usb storage"> + <a + id="enableMTP" + class="adbdev btn btn-primary" + title="mount device as usb storage" + > <i class="fa fa-plug"></i> Switch USB mode to MTP </a> <a id="rebootDevice" class="adbdev btn btn-danger"> @@ -30,17 +46,23 @@ </a> </div> - <!-- <div class="form-group mb-3"> - <h6 class="card-title"> - Proximity sensor - <a id="proximityOn" class="btn btn-sm btn-info" />Enable</a> - <a id="proximityOff" class="btn btn-sm btn-primary" />Disable</a> - </h6> - </div> --> + <!-- + <div class="form-group mb-3"> + <h6 class="card-title"> + Proximity sensor + <a id="proximityOn" class="btn btn-sm btn-info" />Enable</a> + <a id="proximityOff" class="btn btn-sm btn-primary" />Disable</a> + </h6> + </div> + --> <div class="form-check mb-3"> <label for="guardian_pause" class="form-check-label"> - <input class="adbdev form-check-input" type="checkbox" id="guardian_pause"> + <input + class="adbdev form-check-input" + type="checkbox" + id="guardian_pause" + > Pause the guardian(temporary disable it) </label> </div> @@ -52,35 +74,32 @@ Will not change for apps that are already installed </small> </label> - <input id="mp_name" class="adbdev form-control" value="" /> + <input id="mp_name" class="adbdev form-control" value=""> </div> - </div> </div> <div class="card bg-primary mb-2"> - <div class="card-header"> - FW Update (experimental): - </div> + <div class="card-header">FW Update (experimental):</div> <div class="card-body"> <div class="alert alert-info"> <small> <h6>Instructions:</h6> - <ol style="margin-bottom: 0"> - <li>Reboot to bootloader.</li> - <li> - Select the "firmware update" option on your device using the - volume buttons to move the selection, and power button to - select. - </li> - <li>Sideload update.zip</li> - </ol> + <ol style="margin-bottom: 0"> + <li>Reboot to bootloader.</li> + <li> + Select the "firmware update" option on your device using the + volume buttons to move the selection, and power button to + select. + </li> + <li>Sideload update.zip</li> + </ol> </small> </div> <div class="form-group mb-0"> <label for="updateFwPath" class="mb-0">Path to update.zip:</label> <div class="input-group mb-2"> - <input id="updateFwPath" class="form-control" value="" /> + <input id="updateFwPath" class="form-control" value=""> <a class="btn btn-primary" id="updateFwPathBtn">Browse</a> </div> <a id="rebootBootloader" class="adbdev btn btn-danger"> @@ -90,84 +109,99 @@ <i class="fa fa-download"></i> Sideload update.zip </a> </div> - </div> </div> - <div class="card bg-primary mb-2"> <div class="card-header">Snapshots:</div> <div class="card-body"> - <!-- <div class="form-group"> - <a id="pullScreenshots" class="adbdev btn btn-primary"> - <i class="fa fa-download"></i> Grub the Screenshots - </a> - <a id="pullVideoshots" class="adbdev btn btn-primary"> - <i class="fa fa-download"></i> Grub the Videoshots - </a> - </div> - <div class="form-check mb-3"> - <label for="snapshotsDelete" class="form-check-label"> - <input class="adbdev form-check-input" type="checkbox" id="snapshotsDelete"> - Clear from device after pulling - </label> - </div> - <hr/> --> + <!-- + <div class="form-group"> + <a id="pullScreenshots" class="adbdev btn btn-primary"> + <i class="fa fa-download"></i> Grub the Screenshots + </a> + <a id="pullVideoshots" class="adbdev btn btn-primary"> + <i class="fa fa-download"></i> Grub the Videoshots + </a> + </div> + <div class="form-check mb-3"> + <label for="snapshotsDelete" class="form-check-label"> + <input class="adbdev form-check-input" type="checkbox" id="snapshotsDelete"> + Clear from device after pulling + </label> + </div> + <hr/> + --> <div class="form-group mb-3"> <label class="mb-0" for="cres"> <h6 class="card-title mb-0">Set Screenshots Size</h6> - <small class="card-text">This changes the resolution of screenshots on the Quest + <small class="card-text" + >This changes the resolution of screenshots on the Quest </small> </label> <select class="adbdev custom-select" id="cres"> <option value="640x480">640x480</option> - <option value="1024x1024">1024x1024 (Quest2)</option> - <option value="1280x720">1280x720</option> - <option value="1920x1080">1920x1080</option> - <option value="3840x1920">3840x1920 (default - Quest3)</option> + <option value="1024x1024">1024x1024 (Quest2)</option> + <option value="1280x720">1280x720</option> + <option value="1920x1080">1920x1080</option> + <option value="3840x1920">3840x1920 (default - Quest3)</option> </select> </div> <div class="form-group mb-3"> <label class="mb-0" for="vres"> <h6 class="card-title mb-0">Set Video Capture Size</h6> - <small class="card-text">This changes the resolution of captured videos on the Quest</small> + <small class="card-text" + >This changes the resolution of captured videos on the Quest</small + > </label> <select class="adbdev custom-select" id="vres"> <option value="1024">1024</option> <option value="1536">1536</option> </select> </div> - <!-- <div class="form-group mb-3"> - <label class="mb-1" for="svb"> - <h6 class="card-title mb-0">Capture Video Bitrate ( Go Only )</h6> - <small class="card-text">This will increase the quality of captured videos on Oculus Go. - </small> - </label> - <select class="adbdev custom-select" id="svb"> - <option value="5000000">5Mbps</option> - <option value="15000000">15Mbps</option> - <option value="25000000">25Mbps</option> - </select> - </div> --> + <!-- + <div class="form-group mb-3"> + <label class="mb-1" for="svb"> + <h6 class="card-title mb-0">Capture Video Bitrate ( Go Only )</h6> + <small class="card-text">This will increase the quality of captured videos on Oculus Go. + </small> + </label> + <select class="adbdev custom-select" id="svb"> + <option value="5000000">5Mbps</option> + <option value="15000000">15Mbps</option> + <option value="25000000">25Mbps</option> + </select> + </div> + --> <div class="form-check mb-0"> <label for="frc" class="form-check-label"> - <input class="adbdev form-check-input" type="checkbox" id="frc"> - Full rate capture for videos recorded i.e. 60/72fps rather than 30fps. + <input + class="adbdev form-check-input" + type="checkbox" + id="frc" + > + Full rate capture for videos recorded i.e. 60/72fps rather than + 30fps. </label> </div> </div> </div> - <div class="card bg-primary mb-2"> <div class="card-header">Graficaly tweaks:</div> <div class="card-body"> <div class="form-group mb-3"> <label class="mb-0" for="gRR"> <h6 class="card-title mb-0">Refresh Rate</h6> - <small class="card-text">This will allow you to change to 90Hz refresh rate for 90fps support in games on Quest 2. - <br/> - WARNING: This does not work in all games, and does not work with Oculus Link. This setting may also reset to default when you restart your headset. You may need to press the power button twice to turn the screen off and on to enable this setting for apps that dont have it natively enabled. + <small class="card-text" + >This will allow you to change to 90Hz refresh rate for 90fps + support in games on Quest 2. + <br> + WARNING: This does not work in all games, and does not work + with Oculus Link. This setting may also reset to default when + you restart your headset. You may need to press the power + button twice to turn the screen off and on to enable this + setting for apps that dont have it natively enabled. </small> </label> <select class="adbdev custom-select" id="gRR"> @@ -190,9 +224,14 @@ </div> <div class="form-group mb-3"> <label class="mb-0" for="gFFR"> - <h6 class="card-title mb-0">FFR(Fixed Foveated Rendering) level</h6> + <h6 class="card-title mb-0"> + FFR(Fixed Foveated Rendering) level + </h6> <small class="card-text"> - This will change how the view is rendered in the outer edges of the viewscreen for each eye. Higher is better performance, lower is better quality. This gets reset when you reboot the device. + This will change how the view is rendered in the outer edges + of the viewscreen for each eye. Higher is better performance, + lower is better quality. This gets reset when you reboot the + device. </small> </label> <select class="adbdev custom-select" id="gFFR"> @@ -208,7 +247,8 @@ <label class="mb-0" for="gSSO"> <h6 class="card-title mb-0">Default Texture Size</h6> <small class="card-text"> - This will change how high quality some of the in game textures are rendered at. + This will change how high quality some of the in game textures + are rendered at. </small> </label> <select class="adbdev custom-select" id="gSSO"> @@ -233,11 +273,11 @@ </label> <select class="adbdev custom-select" id="CPU"> <option value="-1">dynamic</option> - <option value="0">level 0 (slowest)</option> - <option value="1">level 1</option> - <option value="2">level 2</option> - <option value="3">level 3</option> - <option value="4">level 4 (fastest)</option> + <option value="0">level 0 (slowest)</option> + <option value="1">level 1</option> + <option value="2">level 2</option> + <option value="3">level 3</option> + <option value="4">level 4 (fastest)</option> </select> </div> <div class="form-group col-6"> @@ -249,11 +289,11 @@ </label> <select class="adbdev custom-select" id="GPU"> <option value="-1">dynamic</option> - <option value="0">level 0 (slowest)</option> - <option value="1">level 1</option> - <option value="2">level 2</option> - <option value="3">level 3</option> - <option value="4">level 4 (fastest)</option> + <option value="0">level 0 (slowest)</option> + <option value="1">level 1</option> + <option value="2">level 2</option> + <option value="3">level 3</option> + <option value="4">level 4 (fastest)</option> </select> </div> </div> @@ -261,113 +301,128 @@ </div> </div> <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-secondary" data-dismiss="modal"> + Close + </button> </div> </div> </div> </div> -<script>{ - console.log('ONLOAD tweaks'); - const TWEAKS_KEYS = [ - 'mp_name', 'cres', 'vres', 'gRR', 'gCA', 'gFFR', 'gSSO', 'CPU', 'GPU' - ]; - const TWEAKS_CHECKBOXES = [ - 'guardian_pause', 'frc' - ]; +<script> + { + console.log('ONLOAD tweaks'); + const TWEAKS_KEYS = [ + 'mp_name', + 'cres', + 'vres', + 'gRR', + 'gCA', + 'gFFR', + 'gSSO', + 'CPU', + 'GPU', + ]; + const TWEAKS_CHECKBOXES = ['guardian_pause', 'frc']; - for (const key of TWEAKS_KEYS) { - ipcRenderer.send('device_tweaks', { cmd: 'get', key }); - } - for (const key of TWEAKS_CHECKBOXES) { - ipcRenderer.send('device_tweaks', { cmd: 'get', key }); - } + for (const key of TWEAKS_KEYS) { + ipcRenderer.send('device_tweaks', { cmd: 'get', key }); + } + for (const key of TWEAKS_CHECKBOXES) { + ipcRenderer.send('device_tweaks', { cmd: 'get', key }); + } - ipcRenderer.removeAllListeners('device_tweaks'); - ipcRenderer.on('device_tweaks', (event, arg) => { - // console.log('device_tweaks ! ', arg); - if (arg.cmd == 'get') { - for (const key of TWEAKS_KEYS) { - if (arg[key]) $id('' + key).val(arg[key]); + ipcRenderer.removeAllListeners('device_tweaks'); + ipcRenderer.on('device_tweaks', (event, arg) => { + // console.log('device_tweaks ! ', arg); + if (arg.cmd == 'get') { + for (const key of TWEAKS_KEYS) { + if (arg[key]) $id('' + key).val(arg[key]); + } + + for (const key of TWEAKS_CHECKBOXES) { + if (arg[key]) $id('' + key).prop('checked', arg[key] == '1'); + } } - for (const key of TWEAKS_CHECKBOXES) { - if (arg[key]) $id('' + key).prop('checked', arg[key] == '1'); + if (arg.cmd == 'set') { + for (const key of TWEAKS_KEYS) { + if (typeof arg[key] != 'undefined') + $id('' + key).addClass('is-valid'); + } } + }); + + for (const key of TWEAKS_KEYS) { + $id(key).on('change', function ({ target }) { + $(target).removeClass('is-valid'); + ipcRenderer.send('device_tweaks', { cmd: 'set', [key]: target.value }); + }); } - if (arg.cmd == 'set') { - for (const key of TWEAKS_KEYS) { - if (typeof arg[key] != 'undefined') $id('' + key).addClass('is-valid'); - } + for (const key of TWEAKS_CHECKBOXES) { + $id(key).on('change.bootstrapSwitch', ({ target }) => { + ipcRenderer.send('device_tweaks', { + cmd: 'set', + [key]: target.checked, + }); + }); } - }); - for (const key of TWEAKS_KEYS) { - $id(key).on('change', function ({ target }) { - $(target).removeClass('is-valid'); - ipcRenderer.send('device_tweaks', { cmd: 'set', [key]: target.value }); + if (remote.getGlobal('currentConfiguration').snapshotsDelete) { + $id('snapshotsDelete').prop('checked', true); + } + $id('snapshotsDelete').on('change.bootstrapSwitch', (e) => { + ipcRenderer.send('change_config', { + key: 'snapshotsDelete', + val: e.target.checked, + }); }); - } - for (const key of TWEAKS_CHECKBOXES) { - $id(key).on('change.bootstrapSwitch', ({ target }) => { - ipcRenderer.send('device_tweaks', {cmd: 'set', [key]: target.checked}); + $id('enableMTP').on('click', function (e) { + ipcRenderer.send('enable_mtp', ''); + }); + $id('scrcpyCfg').on('click', function (e) { + $id('scrcpyModal').modal('show'); + }); + $id('rebootDevice').on('click', function (e) { + ipcRenderer.send('reboot_device', ''); + }); + $id('rebootRecovery').on('click', function (e) { + ipcRenderer.send('reboot_recovery', ''); + }); + $id('rebootBootloader').on('click', function (e) { + ipcRenderer.send('reboot_bootloader', ''); + }); + $id('sideloadeFw').on('click', function (e) { + ipcRenderer.send('sideload_update', $id('updateFwPath').val()); }); - } - - - if (remote.getGlobal('currentConfiguration').snapshotsDelete) { - $id('snapshotsDelete').prop('checked', true); - } - $id('snapshotsDelete').on('change.bootstrapSwitch', (e) => { - ipcRenderer.send('change_config', {key: 'snapshotsDelete', val: e.target.checked}); - }); - - $id('enableMTP').on('click', function (e) { - ipcRenderer.send('enable_mtp', ''); - }); - $id('scrcpyCfg').on('click', function (e) { - $id('scrcpyModal').modal('show'); - }); - $id('rebootDevice').on('click', function (e) { - ipcRenderer.send('reboot_device', ''); - }); - $id('rebootRecovery').on('click', function (e) { - ipcRenderer.send('reboot_recovery', ''); - }); - $id('rebootBootloader').on('click', function (e) { - ipcRenderer.send('reboot_bootloader', ''); - }); - $id('sideloadeFw').on('click', function (e) { - ipcRenderer.send('sideload_update', $id('updateFwPath').val()); - }); - $id('updateFwPathBtn').on('click', updateFwPath); + $id('updateFwPathBtn').on('click', updateFwPath); - async function updateFwPath() { - $id('updateFwPath').removeClass('is-valid'); - const file = await dialog.showOpenDialog(null, { - properties: ['openFile'], - title: 'Update.zip path', - message: 'Browse to update.zip', - filters: [{ - name: 'Zip', extensions: ['zip'], - }], - }); - if (file.canceled) return; + async function updateFwPath() { + $id('updateFwPath').removeClass('is-valid'); + const file = await dialog.showOpenDialog(null, { + properties: ['openFile'], + title: 'Update.zip path', + message: 'Browse to update.zip', + filters: [ + { + name: 'Zip', + extensions: ['zip'], + }, + ], + }); + if (file.canceled) return; - const val = file.filePaths[0]; - $id('updateFwPath') - .val(val) - .addClass('is-valid'); - return val; - } + const val = file.filePaths[0]; + $id('updateFwPath').val(val).addClass('is-valid'); + return val; + } - $id('tweaksModal') - .modal('show') + $id('tweaksModal').modal('show'); /*.on('hidden.bs.modal', () => { console.log('tweaks closed'); });*/ - -}</script> \ No newline at end of file + } +</script> diff --git a/views/settings_include.twig b/views/settings_include.twig index 02a8bf7..28f1391 100644 --- a/views/settings_include.twig +++ b/views/settings_include.twig @@ -5,41 +5,50 @@ <div class="card-body"> <div class="card-text container"> <div class="card bg-primary mb-2"> - <div class="card-header">Rclone configs (needed for mount remote disks):</div> + <div class="card-header"> + Rclone configs (needed for mount remote disks): + </div> <div class="card-body"> <div class="form-group mb-3"> - <label for="cfgSection">Select mirror(click on mount status button to remount):</label> - <select class="custom-select" id="cfgSection"> - </select> + <label for="cfgSection" + >Select mirror(click on mount status button to remount):</label + > + <select class="custom-select" id="cfgSection"></select> </div> <div class="form-group mb-4"> <label for="rclonePath">Custom binary path:</label> <div class="input-group"> - <input id="rclonePath" class="form-control" value="" /> + <input id="rclonePath" class="form-control" value=""> <a class="btn btn-primary" id="rclonePathBtn">Browse</a> - <a class="btn btn-info" onclick="shell.openExternal('https://downloads.rclone.org/')">Download</a> + <a + class="btn btn-info" + onclick="shell.openExternal('https://downloads.rclone.org/')" + >Download</a + > </div> </div> <div class="form-group mb-4"> <label for="rcloneConf">Path config:</label> <div class="input-group"> - <input id="rcloneConf" class="form-control" value="" /> + <input id="rcloneConf" class="form-control" value=""> <a class="btn btn-primary" id="rcloneConfBtn">Browse</a> </div> </div> {% if platform == 'mac' %} - <div class="form-group mb-4"> - <label for="rcloneConf">Path config:</label> - <div class="input-group"> - <select class="adbdev custom-select" id="mountCmd"> - <option value="mount">mount</option> - <option value="cmount">cmount (support old rclone versions)</option> - </select> + <div class="form-group mb-4"> + <label for="rcloneConf">Path config:</label> + <div class="input-group"> + <select class="adbdev custom-select" id="mountCmd"> + <option value="mount">mount</option> + <option value="cmount"> + cmount (support old rclone versions) + </option> + </select> + </div> </div> - </div> {% endif %} <div class="form-check mb-0"> @@ -51,37 +60,53 @@ </div> </div> - <div class="card bg-primary mb-2"> <div class="card-header">Main:</div> <div class="card-body"> <div class="form-group mb-4"> - <label for="scrcpyPath"><a onclick="shell.openExternal('https://github.com/Genymobile/scrcpy/releases/latest')">SCRCPY</a> custom path:</label> + <label for="scrcpyPath" + ><a onclick="shell.openExternal('https://github.com/Genymobile/scrcpy/releases/latest')" + >SCRCPY</a + > + custom path:</label + > <div class="input-group"> - <input id="scrcpyPath" class="form-control" value="" /> + <input id="scrcpyPath" class="form-control" value=""> <a class="btn btn-primary" id="scrcpyPathBtn">Browse</a> - <a class="btn btn-info" onclick="shell.openExternal('https://github.com/Genymobile/scrcpy/releases/latest')">Download</a> + <a + class="btn btn-info" + onclick="shell.openExternal('https://github.com/Genymobile/scrcpy/releases/latest')" + >Download</a + > </div> </div> <div class="form-group mb-4"> <label for="tmpdir">Custom temp dirrectory:</label> <div class="input-group"> - <input id="tmpdir" class="form-control" value="" /> + <input id="tmpdir" class="form-control" value=""> <a class="btn btn-primary" id="tmpdirBtn">Browse</a> </div> </div> <div class="form-check mb-3"> <label for="allowOtherDevices" class="form-check-label"> - <input class="form-check-input" type="checkbox" id="allowOtherDevices"> + <input + class="form-check-input" + type="checkbox" + id="allowOtherDevices" + > Allow to connect not oculus devices </label> </div> <div class="form-check mb-3"> <label for="cacheOculusGames" class="form-check-label"> - <input class="form-check-input" type="checkbox" id="cacheOculusGames"> + <input + class="form-check-input" + type="checkbox" + id="cacheOculusGames" + > Cache Oculus Games on first opening(for faster reopening) </label> </div> @@ -93,11 +118,13 @@ </label> </div> - <!-- <div class="form-group"> - <a id="debugTool" class="btn btn-primary"> - <i class="fa fa-bug"></i> Open debug tools - </a> - </div> --> + <!-- + <div class="form-group"> + <a id="debugTool" class="btn btn-primary"> + <i class="fa fa-bug"></i> Open debug tools + </a> + </div> + --> </div> </div> @@ -105,14 +132,23 @@ <div class="card-header">Proxy settings EXPERIMENTAL:</div> <div class="card-body"> <div class="form-group mb-4"> - <label for="proxyUrl">Socks proxy url (for example from https://hideip.me/ru/proxy/socks5list):</label> - <input id="proxyUrl" class="form-control" value="" placeholder="socks://[HOST]:[PORT]" /> + <label for="proxyUrl" + >Socks proxy url (for example from + https://hideip.me/ru/proxy/socks5list):</label + > + <input + id="proxyUrl" + class="form-control" + value="" + placeholder="socks://[HOST]:[PORT]" + > </div> <div class="form-check mb-0"> <label for="proxyOculus" class="form-check-label"> <input class="form-check-input" type="checkbox" id="proxyOculus"> - <img src="img/oculus.svg" width="14" style="margin-top: -2px;" /> Enable proxy for fetch Oculus information + <img src="img/oculus.svg" width="14" style="margin-top: -2px;"> + Enable proxy for fetch Oculus information </label> </div> <div class="form-check mb-0"> @@ -124,115 +160,162 @@ <div class="form-check mb-0"> <label for="proxySQ" class="form-check-label"> <input class="form-check-input" type="checkbox" id="proxySQ"> - <img src="img/sq-white.svg" width="14" /> Enable proxy for fetch SideQuest information + <img src="img/sq-white.svg" width="14"> Enable proxy for fetch + SideQuest information </label> </div> - <!-- <div class="form-group"> - <a id="debugTool" class="btn btn-primary"> - <i class="fa fa-bug"></i> Open debug tools - </a> - </div> --> + <!-- + <div class="form-group"> + <a id="debugTool" class="btn btn-primary"> + <i class="fa fa-bug"></i> Open debug tools + </a> + </div> + --> </div> </div> </div> </div> </div> -<script>{ - console.log('ONLOAD settings'); - const OPT_KEYS = [ - 'cfgSection', - 'adbPath', - 'rclonePath', - 'rcloneConf', - 'mountCmd', - 'scrcpyPath', - 'tmpdir', - 'proxyUrl', - ]; - const CHKBOXES = [ - 'autoMount', - 'allowOtherDevices', - 'cacheOculusGames', - 'userHide', - 'proxyOculus', - 'proxySteam', - 'proxySQ', - ]; - - let options = ''; - for (const section of remote.getGlobal('rcloneSections')) { - options+= `<option value="${section}">${section}</option>`; - } - id('cfgSection').innerHTML = options; - - for (const key of OPT_KEYS) { - $id(key) - .val(remote.getGlobal('currentConfiguration')[key]) - .on('change', ({ target }) => { - $(target).removeClass('is-valid'); - ipcRenderer.send('change_config', {key, val: target.value}); - }); - } +<script> + { + console.log('ONLOAD settings'); + const OPT_KEYS = [ + 'cfgSection', + 'adbPath', + 'rclonePath', + 'rcloneConf', + 'mountCmd', + 'scrcpyPath', + 'tmpdir', + 'proxyUrl', + ]; + const CHKBOXES = [ + 'autoMount', + 'allowOtherDevices', + 'cacheOculusGames', + 'userHide', + 'proxyOculus', + 'proxySteam', + 'proxySQ', + ]; - for (const key of CHKBOXES) { - const el = $id('' + key); - if (remote.getGlobal('currentConfiguration')[key]) { - el.prop('checked', true); + let options = ''; + for (const section of remote.getGlobal('rcloneSections')) { + options += `<option value="${section}">${section}</option>`; } + id('cfgSection').innerHTML = options; - el.on('change.bootstrapSwitch', ({ target }) => { - ipcRenderer.send('change_config', {key, val: target.checked}); - }); - } + for (const key of OPT_KEYS) { + $id(key) + .val(remote.getGlobal('currentConfiguration')[key]) + .on('change', ({ target }) => { + $(target).removeClass('is-valid'); + ipcRenderer.send('change_config', { key, val: target.value }); + }); + } + for (const key of CHKBOXES) { + const el = $id('' + key); + if (remote.getGlobal('currentConfiguration')[key]) { + el.prop('checked', true); + } - $id('debugTool').on('click', (e) => { - remote.getGlobal('win').webContents.openDevTools(); - }); + el.on('change.bootstrapSwitch', ({ target }) => { + ipcRenderer.send('change_config', { key, val: target.checked }); + }); + } - $id('adbPathBtn').on('click', (e) => setCustomPath('adbPath', 'Android Debug Bridge custom path', 'Browse to adb binary location')); - $id('rclonePathBtn').on('click', (e) => setCustomPath('rclonePath', 'RClone custom path', 'Browse to rclone binary location')); - $id('scrcpyPathBtn').on('click', (e) => setCustomPath('scrcpyPath', 'SCRCPY custom path', 'Browse to scrcpy binary location')); - $id('rcloneConfBtn').on('click', (e) => setCustomPath('rcloneConf', 'RClone config location', 'Browse to rclone config location', [{ - name: 'Config', extensions: ['conf', 'cfg', 'ini'], - name: 'All', extensions: ['*'], - }])); - $id('tmpdirBtn').on('click', (e) => setCustomPath('tmpdir', 'Temp dirrectory custom path', 'Browse to new temp dirrectory location', undefined, 'openDirectory')); + $id('debugTool').on('click', (e) => { + remote.getGlobal('win').webContents.openDevTools(); + }); - ipcRenderer.removeAllListeners('change_config'); - ipcRenderer.on('change_config', (event, { key, val }) => { - if (!OPT_KEYS.includes(key)) return; - if ([ - 'cfgSection', - 'rcloneConf', - 'mountCmd', - ].includes(key)) { - if (remote.getGlobal('mounted')) checkMount(); + $id('adbPathBtn').on('click', (e) => + setCustomPath( + 'adbPath', + 'Android Debug Bridge custom path', + 'Browse to adb binary location', + ), + ); + $id('rclonePathBtn').on('click', (e) => + setCustomPath( + 'rclonePath', + 'RClone custom path', + 'Browse to rclone binary location', + ), + ); + $id('scrcpyPathBtn').on('click', (e) => + setCustomPath( + 'scrcpyPath', + 'SCRCPY custom path', + 'Browse to scrcpy binary location', + ), + ); + $id('rcloneConfBtn').on('click', (e) => + setCustomPath( + 'rcloneConf', + 'RClone config location', + 'Browse to rclone config location', + [ + { + name: 'Config', + extensions: ['conf', 'cfg', 'ini'], + name: 'All', + extensions: ['*'], + }, + ], + ), + ); + $id('tmpdirBtn').on('click', (e) => + setCustomPath( + 'tmpdir', + 'Temp dirrectory custom path', + 'Browse to new temp dirrectory location', + undefined, + 'openDirectory', + ), + ); - loadInclude('settings_include.twig'); - } + ipcRenderer.removeAllListeners('change_config'); + ipcRenderer.on('change_config', (event, { key, val }) => { + if (!OPT_KEYS.includes(key)) return; + if (['cfgSection', 'rcloneConf', 'mountCmd'].includes(key)) { + if (remote.getGlobal('mounted')) checkMount(); - $(`#${key}`).val(val).addClass('is-valid'); - }); + loadInclude('settings_include.twig'); + } + + $(`#${key}`).val(val).addClass('is-valid'); + }); - async function setCustomPath(key, title, message, filters, type = 'openFile') { - filters = filters || [{ - name: 'Exe', extensions: ['exe'], - name: 'All', extensions: ['*'], - }]; - const res = await dialog.showOpenDialog(null, { - properties: [type], + async function setCustomPath( + key, title, message, filters, - }); - if (res.canceled) return; + type = 'openFile', + ) { + filters = filters || [ + { + name: 'Exe', + extensions: ['exe'], + name: 'All', + extensions: ['*'], + }, + ]; + const res = await dialog.showOpenDialog(null, { + properties: [type], + title, + message, + filters, + }); + if (res.canceled) return; - const val = res.filePaths[0]; - $(`#${key}`).val(val); - ipcRenderer.send('change_config', {key, val}); - return val; + const val = res.filePaths[0]; + $(`#${key}`).val(val); + ipcRenderer.send('change_config', { key, val }); + return val; + } } -}</script> \ No newline at end of file +</script>