diff options
author | Minteck <contact@minteck.org> | 2022-06-27 17:02:30 +0200 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2022-06-27 17:02:30 +0200 |
commit | eed86d556ccba5d6bfdc8795990f188d0c573fc7 (patch) | |
tree | 3494d9fb204550eb4eba0321b808f771fb6c94e2 /index.html | |
parent | d2a4546fdb55ac8dba8d41d3a7dad39e68555c81 (diff) | |
download | bits-client-eed86d556ccba5d6bfdc8795990f188d0c573fc7.tar.gz bits-client-eed86d556ccba5d6bfdc8795990f188d0c573fc7.tar.bz2 bits-client-eed86d556ccba5d6bfdc8795990f188d0c573fc7.zip |
Update
Diffstat (limited to 'index.html')
-rw-r--r-- | index.html | 1236 |
1 files changed, 618 insertions, 618 deletions
@@ -1,619 +1,619 @@ -<!DOCTYPE html> -<!--suppress HtmlFormInputWithoutLabel, JSUndeclaredVariable --> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <title>Bits</title> - <script> - isNodeJS = typeof require === "function"; - </script> - <style> - :root { - --perc-color: black; - } - * { - user-select: none; - overflow-x: hidden !important; - } - body { - color: white; - font-family: sans-serif; - } - .ln:hover { - text-decoration: underline; - } - .ln:active { - opacity: .75; - } - .transaction { - transition: background-color 100ms; - } - .transaction:hover { - background-color: rgba(0, 0, 0, .1); - } - - @keyframes shake { - 0% { - transform: scale(1); - } - 100% { - transform: scale(1.5); - } - } - </style> - <link rel="stylesheet" href="./assets/black.css" disabled id="mobile-css"> - <script src="./chart.js"></script> - <script src="./trendline.js"></script> -</head> -<body style="position:fixed;inset:0;"> - <div id="loader" style="background:#222;transition:opacity 500ms;z-index:9999;position:fixed;inset:0;display:flex;align-items: center;justify-content: center;"> - <img alt="" src="assets/logo.svg" style="width:96px;"> - <script> - window.onload = async () => { - if (!isNodeJS) { - document.getElementById("mobile-css").removeAttribute("disabled"); - } - - setTimeout(async () => { - loginStatus = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Test/")).text()).status; - if (loginStatus === 1) { - if (isNodeJS) { - console.info("Starting authentication procedure (Electron)"); - const { ipcRenderer } = require('electron'); - ipcRenderer.send("login"); - } else { - console.info("Starting authentication procedure (Mobile)"); - location.href = "https://money-v1.equestria.dev/Authentication/Mobile/" - } - } else { - console.info("Authenticated successfully"); - - await refresh(); - } - }, 1000) - } - - async function refresh() { - document.getElementById("transactions").innerHTML = ""; - document.getElementById("username").innerText = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Username/")).text()).name; - document.getElementById("user-profile").src = "https://account.minteck.org/hub/api/rest/avatar/" + JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Username/")).text()).id + "?dpr=2&size=48"; - - window.transactions = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Application/TransactionsList/")).text()); - window.goal = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Application/GetGoal/")).text()); - - for (let transaction of transactions) { - demo = document.getElementById("demo-transaction"); - demo.id = ""; - - if (transaction.type === "pay") { - word = "used"; - color = "#b31500"; - } else { - word = "added"; - color = "#0065b3"; - } - - if (transaction.amount['original'] === "eur") { - baseCurrency = transaction.amount['eur'].toFixed(2) + "€"; - convertedCurrency = "£" + transaction.amount['gbp'].toFixed(2); - } else { - baseCurrency = "£" + transaction.amount['gbp'].toFixed(2); - convertedCurrency = transaction.amount['eur'].toFixed(2) + "€"; - } - - document.getElementById("transactions").innerHTML += demo.outerHTML - .replace("%user%", transaction.author.name) - .replace("%picture%", "\" src=\"" + transaction.author.avatar + "\"") - .replace("%time%", transaction.date.relative) - .replace("%transactionId%", transaction.date.absolute) - .replace("%description%", transaction.description.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")) - .replace("%type%", word) - .replace("%amount_bc%", baseCurrency) - .replace("%amount_cc%", convertedCurrency) - .replace("%type%", word) - .replace("var(--perc-color)", color) - - demo.id = "demo-transaction"; - } - - try { - totalPaidEUR = transactions.filter(i => i.type === "pay").map(i => { return i.amount['eur']; }).reduce((a, b) => { return a+b; }); - } catch (e) { - totalPaidEUR = 0; - } - - try { - totalPaidGBP = transactions.filter(i => i.type === "pay").map(i => { return i.amount['gbp']; }).reduce((a, b) => { return a+b; }); - } catch (e) { - totalPaidGBP = 0; - } - - try { - totalGainedEUR = transactions.filter(i => i.type !== "pay").map(i => { return i.amount['eur']; }).reduce((a, b) => { return a+b; }); - } catch (e) { - totalGainedEUR = 0; - } - - try { - totalGainedGBP = transactions.filter(i => i.type !== "pay").map(i => { return i.amount['gbp']; }).reduce((a, b) => { return a+b; }); - } catch (e) { - totalGainedGBP = 0; - } - - totalEUR = totalGainedEUR - totalPaidEUR; - totalGBP = totalGainedGBP - totalPaidGBP; - - document.getElementById("balance-eur").innerText = totalEUR.toFixed(2); - document.getElementById("balance-gbp").innerText = totalGBP.toFixed(2); - - document.getElementById("goal-name").innerText = goal.name; - document.getElementById("goal-amount-eur").innerText = goal.amount['eur'].toFixed(2); - document.getElementById("goal-amount-gbp").innerText = goal.amount['gbp'].toFixed(2); - - if (goal.amount['eur'] === 0 || goal.amount['gbp'] === 0) { - document.getElementById("goal-amount-percentage").innerText = "N/A"; - document.getElementById("goal-bar-fill").style.width = "0%"; - } else { - percentage = (totalEUR / goal.amount['eur']) * 100; - document.getElementById("goal-amount-percentage").innerText = percentage.toFixed(2); - document.getElementById("goal-bar-fill").style.width = percentage.toFixed(5) + "%"; - } - - document.getElementById("app").style.display = ""; - document.getElementById("loader").style.opacity = "0"; - document.getElementById("loader").style.pointerEvents = "none"; - - for (let item of Array.from(document.getElementsByClassName("transaction"))) { - item.onclick = () => { - deleteTransaction(item.getAttribute("data-transaction-id")); - } - } - - graph.data.labels = transactions.map((i) => { return new Date(i.date.absolute).toString().split(":")[0] + ":" + new Date(i.date.absolute).toString().split(":")[1]; }).reverse().filter((_, i) => i > 1); - - let last = 0; - let balances = []; - - transactions.map((i) => { if (i.type === "pay") { return -(i.amount.eur); } else { return i.amount.eur; } }).reverse().map((i) => { - last = last + i; - balances.push(last); - }); - graph.data.datasets[0].data = balances.filter((_, i) => i > 1); - - trendData = balances.filter((_, i) => i > 1).map((i, _) => { return { x: _, y: i } }); - trend = trendline(trendData, 'x', 'y'); - - let lastTrend = trend.yStart; - let balancesTrend = []; - - transactions.filter((_, i) => i > 1).map(() => { - lastTrend = lastTrend + trend.slope; - balancesTrend.push(lastTrend); - }); - - graph.data.datasets[1].data = balancesTrend; - - document.getElementById("graph-insights-color").style.backgroundColor = "black"; - document.getElementById("graph-insights-text").style.color = "black"; - document.getElementById("graph-insights-text").innerText = "No insights available. Please try again later."; - - let avgSlope = Math.round(trend.slope); - if (avgSlope < -3) { - document.getElementById("graph-insights-color").style.backgroundColor = "red"; - document.getElementById("graph-insights-text").style.color = "red"; - document.getElementById("graph-insights-text").innerText = "Money is going down faster than it should, you must immediately reduce your expenses."; - } else if (avgSlope < 0) { - document.getElementById("graph-insights-color").style.backgroundColor = "orange"; - document.getElementById("graph-insights-text").style.color = "orange"; - document.getElementById("graph-insights-text").innerText = "You are not saving money, consider reducing your expenses."; - } else if (avgSlope === 0) { - document.getElementById("graph-insights-color").style.backgroundColor = "green"; - document.getElementById("graph-insights-text").style.color = "green"; - document.getElementById("graph-insights-text").innerText = "Your balance is stable, consider saving more money."; - } else if (avgSlope > 0) { - document.getElementById("graph-insights-color").style.backgroundColor = "sky"; - document.getElementById("graph-insights-text").style.color = "sky"; - document.getElementById("graph-insights-text").innerText = "You are effectively saving money."; - } - - if (avgSlope < 0) { - it = 0; b = totalEUR; while (b > 0) { it++; b = b + trend.slope; } - let avgDelay = Math.round(averageDelta(transactions.map((i) => { return new Date(i.date.absolute).getTime() }).reverse())); - let timeUntilEmpty = avgDelay * it; - let date = new Date(new Date().getTime() + timeUntilEmpty).toString().split(":")[0]; - document.getElementById("graph-insights-text").innerText += " (reaching zero on " + date.substring(0, date.length - 3) + ")"; - } else { - it = 0; b = totalEUR; while (b < goal.amount.eur) { it++; b = b + trend.slope; } - let avgDelay = Math.round(averageDelta(transactions.map((i) => { return new Date(i.date.absolute).getTime() }).reverse())); - let timeUntilGoal = avgDelay * it; - let date = new Date(new Date().getTime() + timeUntilGoal).toString().split(":")[0]; - document.getElementById("graph-insights-text").innerText += " (reaching goal on " + date.substring(0, date.length - 3) + ")"; - } - } - - async function createTransaction() { - document.getElementById('create-action').disabled = true; - document.getElementById('create-currency').disabled = true; - document.getElementById('create-description').disabled = true; - document.getElementById('create-amount').disabled = true; - document.getElementById('create-button-create').disabled = true; - document.getElementById('create-button-cancel').disabled = true; - - if (isNodeJS) { - await (await window.fetch("https://money-v1.equestria.dev/Application/AddTransaction/?Currency=" + document.getElementById('create-currency').value + "&Amount=" + document.getElementById('create-amount').value + "&Operation=" + document.getElementById('create-action').value + "&Description=" + Buffer.from(document.getElementById('create-description').value).toString("base64url"))).text(); - } else { - await (await window.fetch("https://money-v1.equestria.dev/Application/AddTransaction/?Currency=" + document.getElementById('create-currency').value + "&Amount=" + document.getElementById('create-amount').value + "&Operation=" + document.getElementById('create-action').value + "&Description=" + btoa(document.getElementById('create-description').value).replaceAll("+", "-").replaceAll("/", "_"))).text(); - } - - await refresh(); - - document.getElementById('create-action').disabled = false; - document.getElementById('create-currency').disabled = false; - document.getElementById('create-description').disabled = false; - document.getElementById('create-amount').disabled = false; - document.getElementById('create-button-create').disabled = false; - document.getElementById('create-button-cancel').disabled = false; - document.getElementById('create-modal').style.display = 'none'; - document.getElementById('create-action').value = '+'; - document.getElementById('create-currency').value = '£'; - document.getElementById('create-description').value = ''; - document.getElementById('create-amount').value = ''; - } - - function showConfirm(text) { - document.getElementById("confirm-modal").style.display = isNodeJS ? "flex" : ""; - document.getElementById("confirm-text").innerText = text; - document.getElementById('confirm-button-cancel').disabled = false; - document.getElementById('confirm-button-ok').disabled = false; - } - - confirmAction = () => { - console.log("Confirmed!"); - document.getElementById('confirm-modal').style.display = 'none'; - } - - async function deleteTransaction(id) { - let transaction = transactions.filter(i => i.date.absolute === id)[0]; - console.log(transaction); - showConfirm("This will remove the transaction made by " + transaction.author.name + " " + transaction.date.relative + ".\nIt will be removed from the list and the global balance will be recalculated without this transaction."); - confirmAction = async () => { - if (isNodeJS) { - await (await window.fetch("https://money-v1.equestria.dev/Application/RemoveTransaction/?Transaction=" + Buffer.from(id).toString("base64url"))).text(); - } else { - await (await window.fetch("https://money-v1.equestria.dev/Application/RemoveTransaction/?Transaction=" + btoa(id).replaceAll("+", "-").replaceAll("/", "_"))).text(); - } - await refresh(); - document.getElementById('confirm-modal').style.display = 'none'; - } - } - </script> - </div> - <script> - if (!isNodeJS) { - document.getElementById("loader").style.backgroundColor = "#000000"; - } - </script> - <div id="app" style="display:none;position:fixed;z-index:3;"> - <div id="header" style="z-index:5;background: #111;position: fixed;top: 0;padding: 8px 30px;left: 0;right: 0;height: 32px;"> - <img alt="Bits" src="assets/logo.svg" style="width:32px;vertical-align: middle;"> - <span style="vertical-align: middle;" id="menu-desktop"> - <a class="ln" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('create-modal').style.display = isNodeJS ? 'flex' : '';">New transaction</a><a class="ln" id="tab-0" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = 'none';document.getElementById('tab-0').style.display = 'none';document.getElementById('tab-1').style.display = '';document.getElementById('graph').style.display = '';">Statistics</a><a class="ln" id="tab-1" style="display:none;cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = '';document.getElementById('tab-0').style.display = '';document.getElementById('tab-1').style.display = 'none';document.getElementById('graph').style.display = 'none';">Transactions</a><a class="ln" onclick="document.getElementById('about-modal').style.display = isNodeJS ? 'flex' : '';" style="cursor:pointer;margin-left:20px;">About</a> - </span> - <span style="vertical-align: middle;display:none;" id="menu-mobile"> - <a class="ln" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('create-modal').style.display = isNodeJS ? 'flex' : '';">Add</a><a class="ln" id="tab-0m" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = 'none';document.getElementById('tab-0m').style.display = 'none';document.getElementById('tab-1m').style.display = '';document.getElementById('graph').style.display = '';">Stats</a><a class="ln" id="tab-1m" style="display:none;cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = '';document.getElementById('tab-0m').style.display = '';document.getElementById('tab-1m').style.display = 'none';document.getElementById('graph').style.display = 'none';">List</a><a class="ln" onclick="document.getElementById('about-modal').style.display = isNodeJS ? 'flex' : '';" style="cursor:pointer;margin-left:20px;">About</a> - </span> - <span style="float: right;margin-right: 10px;margin-top: 7px;" id="total-data"> - <span id="balance" style="opacity: .5;"> - <span id="balance-eur">%total_eur%</span>€, £<span id="balance-gbp">%total_gbp%</span> - </span><span id="username-desktop" style="opacity:.5;"> · <span id="username">%user%</span></span><span id="username-mobile" style="display:none;vertical-align: middle;"><img id="user-profile" alt="" style="border-radius: 999px;width: 32px;height: 32px;vertical-align: middle;position:relative;top:-6px;display:inline-block;margin-left:7px;" src=""></span> - </span> - </div> - <script> - if (!isNodeJS) { - document.getElementById("menu-desktop").style.display = "none"; - document.getElementById("menu-mobile").style.display = ""; - document.getElementById("username-desktop").style.display = "none"; - document.getElementById("username-mobile").style.display = ""; - document.getElementById("header").style.padding = "8px"; - document.getElementById("header").style.backgroundColor = "#000000"; - document.getElementById("balance").style.position = "relative"; - document.getElementById("balance").style.top = "-4px"; - } - </script> - <div id="view" style="z-index:5;color:black;background:rgb(247, 249, 250);position:fixed;top:48px;left:0;right:0;bottom:64px;"> - <div id="list" style="position: fixed;top: 48px;left: 0;bottom: 64px;right:0;border-right: 1px solid rgba(0, 0, 0, .25);"> - <div id="demo-zone" style="display:none;"> - <div class="transaction" data-transaction-id="%transactionId%" id="demo-transaction" style="padding:10px;border-bottom: 1px solid rgba(0, 0, 0, .25);display:grid;grid-template-columns: 48px 1fr;grid-column-gap: 15px;"> - <div class="transaction-user" style="display:flex;align-items: center;justify-content: center;"> - <img alt="%picture%" style="border-radius: 999px;width: 48px;height: 48px;"> - </div> - <div class="transaction-details"> - <b>%user%</b> %type% <span style="color:var(--perc-color);"><b>%amount_bc%</b> <i>(%amount_cc%)</i></span><br> - %time%<br> - <div style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;width: 100%;">%description%</div> - </div> - </div> - </div> - <div id="transactions" style="overflow: scroll;height: 100%;"></div> - </div> - - <div id="graph" style="display:none;width:100%;height:100%;"> - <p style=" - font-size: 14px; - margin: 14px; -"> - <span id="graph-insights-color" style="display: inline-block;width: 12px;height: 12px;border-radius: 999px;opacity: .75;background: black;vertical-align: middle;margin-right: 5px;"></span> - <span id="graph-insights-text" style=" - display: inline-block; - vertical-align: middle; -">No insights available. Please try again later.</span> - </p> - <canvas id="graph-display" style="width:100%;height:100%;"></canvas> - <script> - const ctx = document.getElementById('graph-display').getContext('2d'); - const graph = new Chart(ctx, { - type: 'line', - data: { - labels: [], - datasets: [{ - label: 'Balance', - data: [], - borderColor: 'rgba(153, 102, 255, 1)', - fill: false, - }, - { - label: 'Trendline', - data: [], - borderColor: 'rgba(153, 102, 255, .5)', - borderDash: [5], - pointRadius: 0, - fill: false, - }] - }, - options: { - scales: { - y: { - beginAtZero: true - } - }, - scaleLabel: "<%=value%>%", - legend: { - display: false - }, - tooltips: { - callbacks: { - label: function(tooltipItem, data) { - return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + '€'; - } - } - } - } - }); - </script> - </div> - </div> - - <div id="create-modal" style=" - display: none; - position: fixed; - z-index: 999; - inset: 0; - background: rgba(0, 0, 0, .75); - backdrop-filter: blur(10px); - align-items: center; - justify-content: center; - color: black; -"><div id="create-modal-inner" style=" - background: whitesmoke; - width: 500px; - padding: 10px; - border-radius: 5px; - box-shadow: 0 0 20px rgba(0, 0, 0, .5); -"> - <h3 style=" - margin: 0; - margin-bottom: 20px; - margin-left: -10px; - margin-right: -10px; - text-align: center; - margin-top: -10px; - padding: 10px; - background: rgba(0, 0, 0, .1); -">New transaction</h3> - Amount: <select id="create-action"><option>+</option><option>-</option></select> - <input id="create-amount" type="number" max="99999"> - <select id="create-currency"><option>£</option><option>€</option></select><br><br>Description: <input maxlength="100" id="create-description" type="text" style=" - display: block; - margin-top: 5px; - width: calc(100% - 10px); -"> - <div style=" - display: block; - margin-top: 20px; - margin-left: auto; - margin-right: auto; -width:max-content;"> - <button id="create-button-create" onclick="createTransaction();">Create</button> <button id="create-button-cancel" onclick="document.getElementById('create-modal').style.display = 'none';document.getElementById('create-action').value='+';document.getElementById('create-currency').value='£';document.getElementById('create-description').value='';document.getElementById('create-amount').value='';">Cancel</button> - </div> - </div></div> - - <div id="confirm-modal" style=" - display: none; - position: fixed; - z-index: 999; - inset: 0; - background: rgba(0, 0, 0, .75); - backdrop-filter: blur(10px); - align-items: center; - justify-content: center; - color: black; -"><div id="confirm-modal-inner" style=" - background: whitesmoke; - width: 500px; - padding: 10px; - border-radius: 5px; - box-shadow: 0 0 20px rgba(0, 0, 0, .5); -"> - <h3 style=" - margin: 0; - margin-bottom: 20px; - margin-left: -10px; - margin-right: -10px; - text-align: center; - margin-top: -10px; - padding: 10px; - background: rgba(0, 0, 0, .1); -">Confirm action</h3> - <p id="confirm-text">Are you sure you want to do this?</p> - <div style=" - display: block; - margin-top: 20px; - margin-left: auto; - margin-right: auto; -width:max-content;"> - <button id="confirm-button-ok" onclick="document.getElementById('confirm-button-cancel').disabled = true;document.getElementById('confirm-button-ok').disabled = true;confirmAction();">OK</button> <button id="confirm-button-cancel" onclick="document.getElementById('confirm-modal').style.display = 'none';">Cancel</button> - </div> - </div></div> - - - <div id="about-modal" style=" - display: none; - position: fixed; - z-index: 999; - inset: 0; - background: rgba(0, 0, 0, .75); - backdrop-filter: blur(10px); - align-items: center; - justify-content: center; - color: black; -"><div id="about-modal-inner" style=" - background: whitesmoke; - width: 500px; - padding: 10px; - border-radius: 5px; - box-shadow: 0 0 20px rgba(0, 0, 0, .5); -"> - <h3 style=" - margin: 0; - margin-bottom: 20px; - margin-left: -10px; - margin-right: -10px; - text-align: center; - margin-top: -10px; - padding: 10px; - background: rgba(0, 0, 0, .1); -">About Bits</h3> - <div id="about-text"> - <div style="font-weight: bold;text-align:center;margin-bottom:5px;">Bits</div> - <b>Server:</b> money-v1.equestria.dev<br> - <b>Platform:</b> <span id="about-platform"></span><script>document.getElementById("about-platform").innerText = isNodeJS ? "Desktop (Electron, NodeJS)" : "Android (Chrome, Android WebView)";</script><br> - <b>User Agent:</b> <span id="about-useragent"></span><script>document.getElementById("about-useragent").innerText = navigator.userAgent;</script></div> - <div style=" - display: block; - margin-top: 20px; - margin-left: auto; - margin-right: auto; -width:max-content;"> - <button id="about-button-cancel" onclick="document.getElementById('about-modal').style.display = 'none';">Close</button> - </div> - </div></div> - - <div id="goal" style=" - position: fixed; - background: rgb(64, 64, 64); - bottom: 0; - left: 0; - right: 0; - padding: 10px; - z-index: 2; -"><b id="goal-name">%goal%</b> <span style=" - float: right; -"><span id="goal-amount-eur">%goal_eur%</span>€, £<span id="goal-amount-gbp">%goal_gbp%</span> · <span id="goal-amount-percentage">%goal_percentage%</span>% completed</span><span id="goal-bar" style=" - display: block; - height: 16px; - margin-top: 10px; - border-radius: 999px; - background: rgba(0, 0, 0, .25); -"><span id="goal-bar-fill" style=" - height: 16px; - background: linear-gradient(90deg, rgba(183,153,201,1) 0%, rgba(87,58,152,1) 25%, rgba(204,129,148,1) 50%, rgba(235,186,115,1) 75%, rgba(169,83,144,1) 100%); - width: 0; - display: block; - border-radius: 999px; -"></span></span></div> - </div> -<script> - if (!isNodeJS) { - document.getElementById("create-modal-inner").style.borderRadius = "0"; - document.getElementById("create-modal-inner").style.borderBottomLeftRadius = "5px"; - document.getElementById("create-modal-inner").style.borderBottomRightRadius = "5px"; - document.getElementById("create-modal-inner").style.width = "calc(100% - 30px)"; - document.getElementById("confirm-modal-inner").style.borderRadius = "0"; - document.getElementById("confirm-modal-inner").style.width = "calc(100% - 30px)"; - document.getElementById("confirm-modal-inner").style.borderBottomLeftRadius = "5px"; - document.getElementById("confirm-modal-inner").style.borderBottomRightRadius = "5px"; - } - - f13presses = 0; - document.onkeydown = (event) => { - if (event.keyCode === 124) { - if (require('os').platform() === "win32") { - alert("haha you're using an inferior OS! Doing F13 on here is too easy so you might have expected " + - "something but you're not going to get it.") - } else { - f13presses++; - - switch (f13presses) { - case 1: - Array.from(document.querySelectorAll("*")).forEach((item) => { - item.style.fontFamily = "'Noto Sans Symbols', 'Wingdings', 'Webdings', monospace"; - }); - break; - - case 2: - Array.from(document.querySelectorAll("*")).forEach((item) => { - item.style.background = "red"; - }); - break; - - case 3: - Array.from(document.querySelectorAll("*")).forEach((item) => { - item.style.color = "blue"; - }); - break; - - case 4: - Array.from(document.querySelectorAll("img")).forEach((item) => { - item.src = ""; - item.style.color = "yellow"; - }); - break; - - case 5: - Array.from(document.querySelectorAll("*")).forEach((item) => { - item.style.transition = "transform 200ms"; - item.style.animationName = "shake"; - item.style.animationDuration = "200ms"; - item.style.animationIterationCount = "infinite"; - item.style.animationDirection = "alternate-reverse"; - }); - break; - - case 6: - Array.from(document.querySelectorAll("*")).forEach((item) => { - item.style.display = "none"; - }); - break; - - default: - Array.from(document.querySelectorAll("*")).forEach((item) => { - window.close(); - }); - break; - } - } - } - } -</script> -</body> +<!DOCTYPE html>
+<!--suppress HtmlFormInputWithoutLabel, JSUndeclaredVariable -->
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Bits</title>
+ <script>
+ isNodeJS = typeof require === "function";
+ </script>
+ <style>
+ :root {
+ --perc-color: black;
+ }
+ * {
+ user-select: none;
+ overflow-x: hidden !important;
+ }
+ body {
+ color: white;
+ font-family: sans-serif;
+ }
+ .ln:hover {
+ text-decoration: underline;
+ }
+ .ln:active {
+ opacity: .75;
+ }
+ .transaction {
+ transition: background-color 100ms;
+ }
+ .transaction:hover {
+ background-color: rgba(0, 0, 0, .1);
+ }
+
+ @keyframes shake {
+ 0% {
+ transform: scale(1);
+ }
+ 100% {
+ transform: scale(1.5);
+ }
+ }
+ </style>
+ <link rel="stylesheet" href="./assets/black.css" disabled id="mobile-css">
+ <script src="./chart.js"></script>
+ <script src="./trendline.js"></script>
+</head>
+<body style="position:fixed;inset:0;">
+ <div id="loader" style="background:#222;transition:opacity 500ms;z-index:9999;position:fixed;inset:0;display:flex;align-items: center;justify-content: center;">
+ <img alt="" src="assets/logo.svg" style="width:96px;">
+ <script>
+ window.onload = async () => {
+ if (!isNodeJS) {
+ document.getElementById("mobile-css").removeAttribute("disabled");
+ }
+
+ setTimeout(async () => {
+ loginStatus = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Test/")).text()).status;
+ if (loginStatus === 1) {
+ if (isNodeJS) {
+ console.info("Starting authentication procedure (Electron)");
+ const { ipcRenderer } = require('electron');
+ ipcRenderer.send("login");
+ } else {
+ console.info("Starting authentication procedure (Mobile)");
+ location.href = "https://money-v1.equestria.dev/Authentication/Mobile/"
+ }
+ } else {
+ console.info("Authenticated successfully");
+
+ await refresh();
+ }
+ }, 1000)
+ }
+
+ async function refresh() {
+ document.getElementById("transactions").innerHTML = "";
+ document.getElementById("username").innerText = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Username/")).text()).name;
+ document.getElementById("user-profile").src = "https://account.minteck.org/hub/api/rest/avatar/" + JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Username/")).text()).id + "?dpr=2&size=48";
+
+ window.transactions = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Application/TransactionsList/")).text());
+ window.goal = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Application/GetGoal/")).text());
+
+ for (let transaction of transactions) {
+ demo = document.getElementById("demo-transaction");
+ demo.id = "";
+
+ if (transaction.type === "pay") {
+ word = "used";
+ color = "#b31500";
+ } else {
+ word = "added";
+ color = "#0065b3";
+ }
+
+ if (transaction.amount['original'] === "eur") {
+ baseCurrency = transaction.amount['eur'].toFixed(2) + "€";
+ convertedCurrency = "£" + transaction.amount['gbp'].toFixed(2);
+ } else {
+ baseCurrency = "£" + transaction.amount['gbp'].toFixed(2);
+ convertedCurrency = transaction.amount['eur'].toFixed(2) + "€";
+ }
+
+ document.getElementById("transactions").innerHTML += demo.outerHTML
+ .replace("%user%", transaction.author.name)
+ .replace("%picture%", "\" src=\"" + transaction.author.avatar + "\"")
+ .replace("%time%", transaction.date.relative)
+ .replace("%transactionId%", transaction.date.absolute)
+ .replace("%description%", transaction.description.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"))
+ .replace("%type%", word)
+ .replace("%amount_bc%", baseCurrency)
+ .replace("%amount_cc%", convertedCurrency)
+ .replace("%type%", word)
+ .replace("var(--perc-color)", color)
+
+ demo.id = "demo-transaction";
+ }
+
+ try {
+ totalPaidEUR = transactions.filter(i => i.type === "pay").map(i => { return i.amount['eur']; }).reduce((a, b) => { return a+b; });
+ } catch (e) {
+ totalPaidEUR = 0;
+ }
+
+ try {
+ totalPaidGBP = transactions.filter(i => i.type === "pay").map(i => { return i.amount['gbp']; }).reduce((a, b) => { return a+b; });
+ } catch (e) {
+ totalPaidGBP = 0;
+ }
+
+ try {
+ totalGainedEUR = transactions.filter(i => i.type !== "pay").map(i => { return i.amount['eur']; }).reduce((a, b) => { return a+b; });
+ } catch (e) {
+ totalGainedEUR = 0;
+ }
+
+ try {
+ totalGainedGBP = transactions.filter(i => i.type !== "pay").map(i => { return i.amount['gbp']; }).reduce((a, b) => { return a+b; });
+ } catch (e) {
+ totalGainedGBP = 0;
+ }
+
+ totalEUR = totalGainedEUR - totalPaidEUR;
+ totalGBP = totalGainedGBP - totalPaidGBP;
+
+ document.getElementById("balance-eur").innerText = totalEUR.toFixed(2);
+ document.getElementById("balance-gbp").innerText = totalGBP.toFixed(2);
+
+ document.getElementById("goal-name").innerText = goal.name;
+ document.getElementById("goal-amount-eur").innerText = goal.amount['eur'].toFixed(2);
+ document.getElementById("goal-amount-gbp").innerText = goal.amount['gbp'].toFixed(2);
+
+ if (goal.amount['eur'] === 0 || goal.amount['gbp'] === 0) {
+ document.getElementById("goal-amount-percentage").innerText = "N/A";
+ document.getElementById("goal-bar-fill").style.width = "0%";
+ } else {
+ percentage = (totalEUR / goal.amount['eur']) * 100;
+ document.getElementById("goal-amount-percentage").innerText = percentage.toFixed(2);
+ document.getElementById("goal-bar-fill").style.width = percentage.toFixed(5) + "%";
+ }
+
+ document.getElementById("app").style.display = "";
+ document.getElementById("loader").style.opacity = "0";
+ document.getElementById("loader").style.pointerEvents = "none";
+
+ for (let item of Array.from(document.getElementsByClassName("transaction"))) {
+ item.onclick = () => {
+ deleteTransaction(item.getAttribute("data-transaction-id"));
+ }
+ }
+
+ graph.data.labels = transactions.map((i) => { return new Date(i.date.absolute).toString().split(":")[0] + ":" + new Date(i.date.absolute).toString().split(":")[1]; }).reverse().filter((_, i) => i > 1);
+
+ let last = 0;
+ let balances = [];
+
+ transactions.map((i) => { if (i.type === "pay") { return -(i.amount.eur); } else { return i.amount.eur; } }).reverse().map((i) => {
+ last = last + i;
+ balances.push(last);
+ });
+ graph.data.datasets[0].data = balances.filter((_, i) => i > 1);
+
+ trendData = balances.filter((_, i) => i > 1).map((i, _) => { return { x: _, y: i } });
+ trend = trendline(trendData, 'x', 'y');
+
+ let lastTrend = trend.yStart;
+ let balancesTrend = [];
+
+ transactions.filter((_, i) => i > 1).map(() => {
+ lastTrend = lastTrend + trend.slope;
+ balancesTrend.push(lastTrend);
+ });
+
+ graph.data.datasets[1].data = balancesTrend;
+
+ document.getElementById("graph-insights-color").style.backgroundColor = "black";
+ document.getElementById("graph-insights-text").style.color = "black";
+ document.getElementById("graph-insights-text").innerText = "No insights available. Please try again later.";
+
+ let avgSlope = Math.round(trend.slope);
+ if (avgSlope < -3) {
+ document.getElementById("graph-insights-color").style.backgroundColor = "red";
+ document.getElementById("graph-insights-text").style.color = "red";
+ document.getElementById("graph-insights-text").innerText = "Money is going down faster than it should, you must immediately reduce your expenses.";
+ } else if (avgSlope < 0) {
+ document.getElementById("graph-insights-color").style.backgroundColor = "orange";
+ document.getElementById("graph-insights-text").style.color = "orange";
+ document.getElementById("graph-insights-text").innerText = "You are not saving money, consider reducing your expenses.";
+ } else if (avgSlope === 0) {
+ document.getElementById("graph-insights-color").style.backgroundColor = "green";
+ document.getElementById("graph-insights-text").style.color = "green";
+ document.getElementById("graph-insights-text").innerText = "Your balance is stable, consider saving more money.";
+ } else if (avgSlope > 0) {
+ document.getElementById("graph-insights-color").style.backgroundColor = "sky";
+ document.getElementById("graph-insights-text").style.color = "sky";
+ document.getElementById("graph-insights-text").innerText = "You are effectively saving money.";
+ }
+
+ if (avgSlope < 0) {
+ it = 0; b = totalEUR; while (b > 0) { it++; b = b + trend.slope; }
+ let avgDelay = Math.round(averageDelta(transactions.map((i) => { return new Date(i.date.absolute).getTime() }).reverse()));
+ let timeUntilEmpty = avgDelay * it;
+ let date = new Date(new Date().getTime() + timeUntilEmpty).toString().split(":")[0];
+ document.getElementById("graph-insights-text").innerText += " (reaching zero on " + date.substring(0, date.length - 3) + ")";
+ } else {
+ it = 0; b = totalEUR; while (b < goal.amount.eur) { it++; b = b + trend.slope; }
+ let avgDelay = Math.round(averageDelta(transactions.map((i) => { return new Date(i.date.absolute).getTime() }).reverse()));
+ let timeUntilGoal = avgDelay * it;
+ let date = new Date(new Date().getTime() + timeUntilGoal).toString().split(":")[0];
+ document.getElementById("graph-insights-text").innerText += " (reaching goal on " + date.substring(0, date.length - 3) + ")";
+ }
+ }
+
+ async function createTransaction() {
+ document.getElementById('create-action').disabled = true;
+ document.getElementById('create-currency').disabled = true;
+ document.getElementById('create-description').disabled = true;
+ document.getElementById('create-amount').disabled = true;
+ document.getElementById('create-button-create').disabled = true;
+ document.getElementById('create-button-cancel').disabled = true;
+
+ if (isNodeJS) {
+ await (await window.fetch("https://money-v1.equestria.dev/Application/AddTransaction/?Currency=" + document.getElementById('create-currency').value + "&Amount=" + document.getElementById('create-amount').value + "&Operation=" + document.getElementById('create-action').value + "&Description=" + Buffer.from(document.getElementById('create-description').value).toString("base64url"))).text();
+ } else {
+ await (await window.fetch("https://money-v1.equestria.dev/Application/AddTransaction/?Currency=" + document.getElementById('create-currency').value + "&Amount=" + document.getElementById('create-amount').value + "&Operation=" + document.getElementById('create-action').value + "&Description=" + btoa(document.getElementById('create-description').value).replaceAll("+", "-").replaceAll("/", "_"))).text();
+ }
+
+ await refresh();
+
+ document.getElementById('create-action').disabled = false;
+ document.getElementById('create-currency').disabled = false;
+ document.getElementById('create-description').disabled = false;
+ document.getElementById('create-amount').disabled = false;
+ document.getElementById('create-button-create').disabled = false;
+ document.getElementById('create-button-cancel').disabled = false;
+ document.getElementById('create-modal').style.display = 'none';
+ document.getElementById('create-action').value = '+';
+ document.getElementById('create-currency').value = '£';
+ document.getElementById('create-description').value = '';
+ document.getElementById('create-amount').value = '';
+ }
+
+ function showConfirm(text) {
+ document.getElementById("confirm-modal").style.display = isNodeJS ? "flex" : "";
+ document.getElementById("confirm-text").innerText = text;
+ document.getElementById('confirm-button-cancel').disabled = false;
+ document.getElementById('confirm-button-ok').disabled = false;
+ }
+
+ confirmAction = () => {
+ console.log("Confirmed!");
+ document.getElementById('confirm-modal').style.display = 'none';
+ }
+
+ async function deleteTransaction(id) {
+ let transaction = transactions.filter(i => i.date.absolute === id)[0];
+ console.log(transaction);
+ showConfirm("This will remove the transaction made by " + transaction.author.name + " " + transaction.date.relative + ".\nIt will be removed from the list and the global balance will be recalculated without this transaction.");
+ confirmAction = async () => {
+ if (isNodeJS) {
+ await (await window.fetch("https://money-v1.equestria.dev/Application/RemoveTransaction/?Transaction=" + Buffer.from(id).toString("base64url"))).text();
+ } else {
+ await (await window.fetch("https://money-v1.equestria.dev/Application/RemoveTransaction/?Transaction=" + btoa(id).replaceAll("+", "-").replaceAll("/", "_"))).text();
+ }
+ await refresh();
+ document.getElementById('confirm-modal').style.display = 'none';
+ }
+ }
+ </script>
+ </div>
+ <script>
+ if (!isNodeJS) {
+ document.getElementById("loader").style.backgroundColor = "#000000";
+ }
+ </script>
+ <div id="app" style="display:none;position:fixed;z-index:3;">
+ <div id="header" style="z-index:5;background: #111;position: fixed;top: 0;padding: 8px 30px;left: 0;right: 0;height: 32px;">
+ <img alt="Bits" src="assets/logo.svg" style="width:32px;vertical-align: middle;">
+ <span style="vertical-align: middle;" id="menu-desktop">
+ <a class="ln" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('create-modal').style.display = isNodeJS ? 'flex' : '';">New transaction</a><a class="ln" id="tab-0" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = 'none';document.getElementById('tab-0').style.display = 'none';document.getElementById('tab-1').style.display = '';document.getElementById('graph').style.display = '';">Statistics</a><a class="ln" id="tab-1" style="display:none;cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = '';document.getElementById('tab-0').style.display = '';document.getElementById('tab-1').style.display = 'none';document.getElementById('graph').style.display = 'none';">Transactions</a><a class="ln" onclick="document.getElementById('about-modal').style.display = isNodeJS ? 'flex' : '';" style="cursor:pointer;margin-left:20px;">About</a>
+ </span>
+ <span style="vertical-align: middle;display:none;" id="menu-mobile">
+ <a class="ln" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('create-modal').style.display = isNodeJS ? 'flex' : '';">Add</a><a class="ln" id="tab-0m" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = 'none';document.getElementById('tab-0m').style.display = 'none';document.getElementById('tab-1m').style.display = '';document.getElementById('graph').style.display = '';">Stats</a><a class="ln" id="tab-1m" style="display:none;cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = '';document.getElementById('tab-0m').style.display = '';document.getElementById('tab-1m').style.display = 'none';document.getElementById('graph').style.display = 'none';">List</a><a class="ln" onclick="document.getElementById('about-modal').style.display = isNodeJS ? 'flex' : '';" style="cursor:pointer;margin-left:20px;">About</a>
+ </span>
+ <span style="float: right;margin-right: 10px;margin-top: 7px;" id="total-data">
+ <span id="balance" style="opacity: .5;">
+ <span id="balance-eur">%total_eur%</span>€, £<span id="balance-gbp">%total_gbp%</span>
+ </span><span id="username-desktop" style="opacity:.5;"> · <span id="username">%user%</span></span><span id="username-mobile" style="display:none;vertical-align: middle;"><img id="user-profile" alt="" style="border-radius: 999px;width: 32px;height: 32px;vertical-align: middle;position:relative;top:-6px;display:inline-block;margin-left:7px;" src=""></span>
+ </span>
+ </div>
+ <script>
+ if (!isNodeJS) {
+ document.getElementById("menu-desktop").style.display = "none";
+ document.getElementById("menu-mobile").style.display = "";
+ document.getElementById("username-desktop").style.display = "none";
+ document.getElementById("username-mobile").style.display = "";
+ document.getElementById("header").style.padding = "8px";
+ document.getElementById("header").style.backgroundColor = "#000000";
+ document.getElementById("balance").style.position = "relative";
+ document.getElementById("balance").style.top = "-4px";
+ }
+ </script>
+ <div id="view" style="z-index:5;color:black;background:rgb(247, 249, 250);position:fixed;top:48px;left:0;right:0;bottom:64px;">
+ <div id="list" style="position: fixed;top: 48px;left: 0;bottom: 64px;right:0;border-right: 1px solid rgba(0, 0, 0, .25);">
+ <div id="demo-zone" style="display:none;">
+ <div class="transaction" data-transaction-id="%transactionId%" id="demo-transaction" style="padding:10px;border-bottom: 1px solid rgba(0, 0, 0, .25);display:grid;grid-template-columns: 48px 1fr;grid-column-gap: 15px;">
+ <div class="transaction-user" style="display:flex;align-items: center;justify-content: center;">
+ <img alt="%picture%" style="border-radius: 999px;width: 48px;height: 48px;">
+ </div>
+ <div class="transaction-details">
+ <b>%user%</b> %type% <span style="color:var(--perc-color);"><b>%amount_bc%</b> <i>(%amount_cc%)</i></span><br>
+ %time%<br>
+ <div style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;width: 100%;">%description%</div>
+ </div>
+ </div>
+ </div>
+ <div id="transactions" style="overflow: scroll;height: 100%;"></div>
+ </div>
+
+ <div id="graph" style="display:none;width:100%;height:100%;">
+ <p style="
+ font-size: 14px;
+ margin: 14px;
+">
+ <span id="graph-insights-color" style="display: inline-block;width: 12px;height: 12px;border-radius: 999px;opacity: .75;background: black;vertical-align: middle;margin-right: 5px;"></span>
+ <span id="graph-insights-text" style="
+ display: inline-block;
+ vertical-align: middle;
+">No insights available. Please try again later.</span>
+ </p>
+ <canvas id="graph-display" style="width:100%;height:100%;"></canvas>
+ <script>
+ const ctx = document.getElementById('graph-display').getContext('2d');
+ const graph = new Chart(ctx, {
+ type: 'line',
+ data: {
+ labels: [],
+ datasets: [{
+ label: 'Balance',
+ data: [],
+ borderColor: 'rgba(153, 102, 255, 1)',
+ fill: false,
+ },
+ {
+ label: 'Trendline',
+ data: [],
+ borderColor: 'rgba(153, 102, 255, .5)',
+ borderDash: [5],
+ pointRadius: 0,
+ fill: false,
+ }]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ },
+ scaleLabel: "<%=value%>%",
+ legend: {
+ display: false
+ },
+ tooltips: {
+ callbacks: {
+ label: function(tooltipItem, data) {
+ return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + '€';
+ }
+ }
+ }
+ }
+ });
+ </script>
+ </div>
+ </div>
+
+ <div id="create-modal" style="
+ display: none;
+ position: fixed;
+ z-index: 999;
+ inset: 0;
+ background: rgba(0, 0, 0, .75);
+ backdrop-filter: blur(10px);
+ align-items: center;
+ justify-content: center;
+ color: black;
+"><div id="create-modal-inner" style="
+ background: whitesmoke;
+ width: 500px;
+ padding: 10px;
+ border-radius: 5px;
+ box-shadow: 0 0 20px rgba(0, 0, 0, .5);
+">
+ <h3 style="
+ margin: 0;
+ margin-bottom: 20px;
+ margin-left: -10px;
+ margin-right: -10px;
+ text-align: center;
+ margin-top: -10px;
+ padding: 10px;
+ background: rgba(0, 0, 0, .1);
+">New transaction</h3>
+ Amount: <select id="create-action"><option>+</option><option>-</option></select>
+ <input id="create-amount" type="number" max="99999">
+ <select id="create-currency"><option>£</option><option>€</option></select><br><br>Description: <input maxlength="100" id="create-description" type="text" style="
+ display: block;
+ margin-top: 5px;
+ width: calc(100% - 10px);
+">
+ <div style="
+ display: block;
+ margin-top: 20px;
+ margin-left: auto;
+ margin-right: auto;
+width:max-content;">
+ <button id="create-button-create" onclick="createTransaction();">Create</button> <button id="create-button-cancel" onclick="document.getElementById('create-modal').style.display = 'none';document.getElementById('create-action').value='+';document.getElementById('create-currency').value='£';document.getElementById('create-description').value='';document.getElementById('create-amount').value='';">Cancel</button>
+ </div>
+ </div></div>
+
+ <div id="confirm-modal" style="
+ display: none;
+ position: fixed;
+ z-index: 999;
+ inset: 0;
+ background: rgba(0, 0, 0, .75);
+ backdrop-filter: blur(10px);
+ align-items: center;
+ justify-content: center;
+ color: black;
+"><div id="confirm-modal-inner" style="
+ background: whitesmoke;
+ width: 500px;
+ padding: 10px;
+ border-radius: 5px;
+ box-shadow: 0 0 20px rgba(0, 0, 0, .5);
+">
+ <h3 style="
+ margin: 0;
+ margin-bottom: 20px;
+ margin-left: -10px;
+ margin-right: -10px;
+ text-align: center;
+ margin-top: -10px;
+ padding: 10px;
+ background: rgba(0, 0, 0, .1);
+">Confirm action</h3>
+ <p id="confirm-text">Are you sure you want to do this?</p>
+ <div style="
+ display: block;
+ margin-top: 20px;
+ margin-left: auto;
+ margin-right: auto;
+width:max-content;">
+ <button id="confirm-button-ok" onclick="document.getElementById('confirm-button-cancel').disabled = true;document.getElementById('confirm-button-ok').disabled = true;confirmAction();">OK</button> <button id="confirm-button-cancel" onclick="document.getElementById('confirm-modal').style.display = 'none';">Cancel</button>
+ </div>
+ </div></div>
+
+
+ <div id="about-modal" style="
+ display: none;
+ position: fixed;
+ z-index: 999;
+ inset: 0;
+ background: rgba(0, 0, 0, .75);
+ backdrop-filter: blur(10px);
+ align-items: center;
+ justify-content: center;
+ color: black;
+"><div id="about-modal-inner" style="
+ background: whitesmoke;
+ width: 500px;
+ padding: 10px;
+ border-radius: 5px;
+ box-shadow: 0 0 20px rgba(0, 0, 0, .5);
+">
+ <h3 style="
+ margin: 0;
+ margin-bottom: 20px;
+ margin-left: -10px;
+ margin-right: -10px;
+ text-align: center;
+ margin-top: -10px;
+ padding: 10px;
+ background: rgba(0, 0, 0, .1);
+">About Bits</h3>
+ <div id="about-text">
+ <div style="font-weight: bold;text-align:center;margin-bottom:5px;">Bits</div>
+ <b>Server:</b> money-v1.equestria.dev<br>
+ <b>Platform:</b> <span id="about-platform"></span><script>document.getElementById("about-platform").innerText = isNodeJS ? "Desktop (Electron, NodeJS)" : "Android (Chrome, Android WebView)";</script><br>
+ <b>User Agent:</b> <span id="about-useragent"></span><script>document.getElementById("about-useragent").innerText = navigator.userAgent;</script></div>
+ <div style="
+ display: block;
+ margin-top: 20px;
+ margin-left: auto;
+ margin-right: auto;
+width:max-content;">
+ <button id="about-button-cancel" onclick="document.getElementById('about-modal').style.display = 'none';">Close</button>
+ </div>
+ </div></div>
+
+ <div id="goal" style="
+ position: fixed;
+ background: rgb(64, 64, 64);
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 10px;
+ z-index: 2;
+"><b id="goal-name">%goal%</b> <span style="
+ float: right;
+"><span id="goal-amount-eur">%goal_eur%</span>€, £<span id="goal-amount-gbp">%goal_gbp%</span> · <span id="goal-amount-percentage">%goal_percentage%</span>% completed</span><span id="goal-bar" style="
+ display: block;
+ height: 16px;
+ margin-top: 10px;
+ border-radius: 999px;
+ background: rgba(0, 0, 0, .25);
+"><span id="goal-bar-fill" style="
+ height: 16px;
+ background: linear-gradient(90deg, rgba(183,153,201,1) 0%, rgba(87,58,152,1) 25%, rgba(204,129,148,1) 50%, rgba(235,186,115,1) 75%, rgba(169,83,144,1) 100%);
+ width: 0;
+ display: block;
+ border-radius: 999px;
+"></span></span></div>
+ </div>
+<script>
+ if (!isNodeJS) {
+ document.getElementById("create-modal-inner").style.borderRadius = "0";
+ document.getElementById("create-modal-inner").style.borderBottomLeftRadius = "5px";
+ document.getElementById("create-modal-inner").style.borderBottomRightRadius = "5px";
+ document.getElementById("create-modal-inner").style.width = "calc(100% - 30px)";
+ document.getElementById("confirm-modal-inner").style.borderRadius = "0";
+ document.getElementById("confirm-modal-inner").style.width = "calc(100% - 30px)";
+ document.getElementById("confirm-modal-inner").style.borderBottomLeftRadius = "5px";
+ document.getElementById("confirm-modal-inner").style.borderBottomRightRadius = "5px";
+ }
+
+ f13presses = 0;
+ document.onkeydown = (event) => {
+ if (event.keyCode === 124) {
+ if (require('os').platform() === "win32") {
+ alert("haha you're using an inferior OS! Doing F13 on here is too easy so you might have expected " +
+ "something but you're not going to get it.")
+ } else {
+ f13presses++;
+
+ switch (f13presses) {
+ case 1:
+ Array.from(document.querySelectorAll("*")).forEach((item) => {
+ item.style.fontFamily = "'Noto Sans Symbols', 'Wingdings', 'Webdings', monospace";
+ });
+ break;
+
+ case 2:
+ Array.from(document.querySelectorAll("*")).forEach((item) => {
+ item.style.background = "red";
+ });
+ break;
+
+ case 3:
+ Array.from(document.querySelectorAll("*")).forEach((item) => {
+ item.style.color = "blue";
+ });
+ break;
+
+ case 4:
+ Array.from(document.querySelectorAll("img")).forEach((item) => {
+ item.src = "";
+ item.style.color = "yellow";
+ });
+ break;
+
+ case 5:
+ Array.from(document.querySelectorAll("*")).forEach((item) => {
+ item.style.transition = "transform 200ms";
+ item.style.animationName = "shake";
+ item.style.animationDuration = "200ms";
+ item.style.animationIterationCount = "infinite";
+ item.style.animationDirection = "alternate-reverse";
+ });
+ break;
+
+ case 6:
+ Array.from(document.querySelectorAll("*")).forEach((item) => {
+ item.style.display = "none";
+ });
+ break;
+
+ default:
+ Array.from(document.querySelectorAll("*")).forEach((item) => {
+ window.close();
+ });
+ break;
+ }
+ }
+ }
+ }
+</script>
+</body>
</html>
\ No newline at end of file |