Compare commits

..

No commits in common. "main" and "next" have entirely different histories.
main ... next

55 changed files with 482 additions and 1962 deletions

2
.gitignore vendored
View file

@ -5,5 +5,3 @@ output/
# the world isn't ready yet # the world isn't ready yet
views/projects/item/wavetapper views/projects/item/wavetapper
views/projects/item/dice_2
views/projects/item/text

View file

@ -1,14 +0,0 @@
<p id="attr">
Image attribution:
<a href="https://exok.com/games/celeste/" target="_blank">Celeste</a>
</p>
<style>
#attr {
display: inline-block;
position: fixed;
left: 0;
bottom: 0;
color: white !important;
text-shadow: 0 0.1em 0.5em black;
}
</style>

View file

@ -19,7 +19,7 @@
> >
<div> <div>
<h1>Loading...</h1> <h1>Loading...</h1>
<p>Please wait.</p> <h2>Please wait.</h2>
<p>If this page does not load, try enabling JavaScript.</p> <p>If this page does not load, try enabling JavaScript.</p>
<small>(This site won't work well without it.)</small> <small>(This site won't work well without it.)</small>
<p>Or <a href="/">go back home</a>.</p> <p>Or <a href="/">go back home</a>.</p>

View file

@ -1,9 +1,11 @@
<section class="nav" role="navigation" aria-label="Site navigation"> <section class="nav" role="navigation" aria-label="Site navigation">
<p> <p>
<a href="/">Home</a> <a href="/">Home</a> <a href="/branding/">Branding</a>
<a href="https://blog.abtmtr.link/mtr/quick-q-and-a">FAQ</a> <a href="/site/">Site Information</a>
</p>
<p>
<a href="/projects/">Projects</a> <a href="/projects/">Projects</a>
<a href="/domain/">Domain</a>
<a href="/links/">Links</a> <a href="/links/">Links</a>
<!-- <a href="/whois/">Who are we?</a> -->
</p> </p>
</section> </section>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

127
views/branding/index.html Normal file
View file

@ -0,0 +1,127 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Branding - abtmtr.link</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" />
<style>
:root {
--base-color: 0, 0%;
}
.chip {
background-color: var(--mainColor);
display: inline-block;
width: 12em;
aspect-ratio: 1/1;
padding: 1em;
vertical-align: top;
margin: 0.5em;
}
</style>
</head>
<body>
<header>
<section>
<h1>Branding</h1>
</section>
<$ nav.html $>
<section id="accessibility" hidden></section>
</header>
<main>
<section>
<p>
I don't really have strict branding guidelines, but I do have a few
important rules if you would like to refer to me in a professional or
formal context.
</p>
</section>
<section>
<h2>Name</h2>
<p>
If you would like to refer to me online, you'll usually be able to use
my username <b>MeowcaTheoRange</b>. If that's too long, you can always
shorten it to <strong>MTR</strong>.
</p>
<p>
Please make sure to keep the styling. It's <b>MeowcaTheoRange</b>, not<!-- -->
<strong>MeowcatHeOrange</strong>, <strong>MeowcaTheOrange</strong>,
nor <strong>Meowca Theo Range</strong>.
</p>
<p class="hv">
The styling <strong>meowcatheorange</strong> is OK if necessary.
</p>
<p>
If you would like to refer to me in a more professional context,
<a href="https://pronouns.cc/@MeowcaTheoRange" target="_blank"
>check my pronouns.cc page for my preferred names.</a
>
</p>
</section>
<section id="characters" style="display: none">
<h2>Characters</h2>
<p>
If you would like to use a character to represent me, I'd prefer you
use <b class="characterName"></b>
<small>(<span class="pronouns"></span>)</small>.
</p>
<img class="characterImage" />
</section>
<section>
<h2>Colors</h2>
<h3>Main Colors</h3>
<p>
If you would like to use colors to refer to me, whether that be the
primary color on a card, or the color of my name, I recommend you use
these colors:
</p>
<div style="--mainColor: #00c0ff; color: #000000" class="chip">
<b>Iszac Blue</b><br /><small>Primary</small>
<p>#00c0ff</p>
</div>
<div style="--mainColor: #ff4000; color: #000000" class="chip">
<b>Rocco Orange</b><br /><small>Primary Negative</small>
<p>#ff4000</p>
</div>
<p>
<b>Iszac Blue</b> is literally <strong>Rocco Orange</strong> but
inverted. This color is named after <strong>Iszac</strong>.<br />If
you were to use any of these colors to represnt me as a
<strong>person</strong>, use this one.
</p>
<p>
<b>Rocco Orange</b> is my favourite color orange, but now more red.
The name comes from my character <strong>Rocco</strong>.<br />If you
were to use any of these colors to represnt me as an
<strong>entity</strong>, use this one whenever possible.
</p>
<h3>Sub Colors</h3>
<p>
If you need filler colors for something that refers to me, feel free
to use these extra colors:
</p>
<div style="--mainColor: #8000ff; color: #000000" class="chip">
<b>Grape Soda</b><br /><small>Secondary</small>
<p>#8000ff</p>
</div>
<div style="--mainColor: #80ff00; color: #000000" class="chip">
<b>Avalonian Waste</b><br /><small>Secondary Negative</small>
<p>#80ff00</p>
</div>
<p>
<b>Grape Soda</b> represents my love for grape soda.<br />My favourite
is Fanta Grape. :]
</p>
<p>
<b>Avalonian Waste</b> is also literally
<strong>Grape Soda</strong> but inverted. This represents some old
lore that I'm still screwing with.
</p>
</section>
</main>
<section id="accessibility" hidden></section>
<script src="/scripts/accessibility.js"></script>
</body>
</html>

View file

@ -0,0 +1,22 @@
const characters = document.getElementById("characters");
const CURRENT_CHARACTER = "meowcatheorange/iszac";
fetch(`https://beta.trollcall.xyz/api/troll/${CURRENT_CHARACTER}`)
.then((x) => x.json())
.then((char) => {
var name = char.name
.map((string) => {
string = string[0].toUpperCase() + string.slice(1);
return string;
})
.join(" ");
characters.querySelector(
".characterName"
).innerHTML = `<a href="https://beta.trollcall.xyz/troll/${CURRENT_CHARACTER}" target="_blank">${name}</a>`;
characters.querySelector(".characterImage").src = char.images[0];
characters.querySelector(".pronouns").innerHTML = char.pronouns
.map((pronoun) => pronoun[0])
.join("/");
characters.style.display = null;
});

View file

@ -1,30 +1,34 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>abtmtr.link</title> <title>Domain - abtmtr.link</title>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" /> <link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" /> <link rel="stylesheet" href="/styles/style.css" />
<style>
:root {
--base-color: 15, 100%;
}
</style>
</head> </head>
<body> <body>
<$ loader.html $>
<header> <header>
<h1>Who are we?</h1> <section>
<h1>Domain</h1>
<p>What's on this domain.</p>
</section>
<$ nav.html $> <$ nav.html $>
<section id="accessibility" hidden></section> <section id="accessibility" hidden></section>
<div id="buttonroll" hidden></div>
</header> </header>
<main> <main>
<section>
<h1>We're abtmtr.link.</h1>
<p>abtmtr.link is an unprofessional consulting-firm/open-source-project-manager ran by one person who is actually more.</p>
<p>We're not an actual company. Don't try to call us. <a href="mailto:me@abtmtr.link">You can email us, however.</a></p>
</section>
<section id="data_get"> <section id="data_get">
<h1>Getting member indexes...</h1> <h1>Getting domain indexes...</h1>
</section> </section>
</main> </main>
<section id="accessibility" hidden></section>
<script src="/scripts/accessibility.js"></script> <script src="/scripts/accessibility.js"></script>
<script src="./scripts/data_get_members.js"></script> <script src="./scripts/data_get_domain.js"></script>
</body> </body>
</html> </html>

View file

@ -0,0 +1,14 @@
const data_get = document.getElementById("data_get");
fetch("https://blog.abtmtr.link/api/collections/paste/posts/domain")
.then((x) => x.json())
.then((paste) => {
const data = JSON.parse(paste.data.body);
data_get.innerHTML = data.reduce(
(html, subdomain) =>
html +
`<h1><a href="${subdomain.url}" target="_blank">${subdomain.name}</a></h1>
${subdomain.description.map((x) => `<p>${x}</p>`).join("")}
<img src="${subdomain.src}" alt="${subdomain.name}" title="${subdomain.name}" />`,
""
);
});

BIN
views/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -7,96 +7,62 @@
<link rel="stylesheet" href="/styles/normal.css" /> <link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/windows.css" /> <link rel="stylesheet" href="/styles/windows.css" />
<link rel="stylesheet" href="/styles/style.css" /> <link rel="stylesheet" href="/styles/style.css" />
<link rel="stylesheet" href="./styles/buttonroll.css" />
<style> <style>
.header { :root {
width: 100%; --base-color: 195, 100%;
aspect-ratio: 3 / 1;
background: url("/assets/bg.jpg");
background-position: center;
background-size: cover;
background-attachment: fixed;
text-align: center;
color: white;
text-shadow: 0 0.1em 0.5em black;
box-shadow: inset 0 0 1em var(--accent-color);
font-size: 1.5em;
border-radius: 2rem;
overflow: hidden;
display: grid;
align-items: center;
justify-content: center;
margin: 0.5em 0;
margin-bottom: 1em;
user-select: none;
}
.header h1 {
line-height: 1em;
margin-bottom: 0.35em;
border: none;
padding: 0;
}
.horizontal {
display: grid;
grid-auto-flow: column;
gap: 1em;
}
@keyframes wave {
0% {
transform: rotate(0deg);
}
20% {
transform: rotate(-15deg);
}
40% {
transform: rotate(15deg);
}
60% {
transform: rotate(-15deg);
}
80% {
transform: rotate(15deg);
}
100% {
transform: rotate(0deg);
}
}
.waving {
transform-origin: 60% 60%;
cursor: none;
}
.waving:hover {
animation: infinite wave 0.5s;
} }
</style> </style>
</head> </head>
<body> <body>
<header> <header>
<h1><small><small>welcome to</small></small> ABTMTR.LINK</h1> <section>
<h1>ABTMTR.LINK</h1>
</section>
<$ nav.html $> <$ nav.html $>
<section id="accessibility" hidden></section> <section id="accessibility" hidden></section>
<div id="buttonroll" hidden></div>
</header> </header>
<main> <main>
<section> <section>
<h1> <h2>Welcome! 👋</h2>
Hey! <section id="data_whoami">
<span class="rs waving">👋</span> <p>
</h1> I'm...
<p>Welcome to abtmtr.link.</p> <small
<p>abtmtr.link is a domain for a suite of services, ranging from <b>micro-blogging</b> to <b>web search</b>.</p> ><a href="https://pronouns.cc/@MeowcaTheoRange" target="_blank"
<p>This site was once a little less personal. That should be over for now.</p> >not loading</a
<p>Check out our <a href="/projects/">projects</a>!</p> >.</small
>
</p>
</section> </section>
<section id="data_get"> <p>
<h1>Getting domain indexes...</h1> I'm a web developer, Fediverse enthusiast, and compulsory Minnesotan.
</p>
<p>I run this domain and all of the services on it!</p>
<p id="no-fetchy" style="display: none">
For me, it is <b id="time">12:00 PM</b> on a
<b id="weekday">Sunday</b>. <b id="timezone">(UTC-0)</b><br />
<small
>data locally fetched from
<a href="https://pronouns.cc/@MeowcaTheoRange" target="_blank"
>pronouns.cc</a
></small
>
</p>
</section> </section>
<section id="status"></section>
<section id="about"></section>
<button id="buttonflags" disabled>Cool Flags</button>
<section id="fields"></section>
</main> </main>
<div id="WindowHolder"></div>
<script src="/scripts/windows.js"></script>
<script src="/scripts/accessibility.js"></script> <script src="/scripts/accessibility.js"></script>
<script src="./scripts/roll_buttons.js"></script> <script src="./scripts/data_get_whoami.js"></script>
<script src="./scripts/data_get_domain.js"></script> <script src="/projects/item/hex/scripts/index.js"></script>
<script>
window.manager = new WindowManager(
document.getElementById("WindowHolder")
);
</script>
</body> </body>
</html> </html>

View file

@ -6,14 +6,18 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" /> <link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" /> <link rel="stylesheet" href="/styles/style.css" />
<style></style> <style>
:root {
--base-color: 270, 100%;
}
</style>
</head> </head>
<body> <body>
<$ loader.html $> <$ loader.html $>
<header> <header>
<section> <section>
<h1>Links</h1> <h1>Links</h1>
<p>Where else you can find traces of abtmtr.link.</p> <p>Where else you can find me.</p>
</section> </section>
<$ nav.html $> <$ nav.html $>
<section id="accessibility" hidden></section> <section id="accessibility" hidden></section>

View file

@ -1,5 +1,5 @@
const data_get = document.getElementById("data_get"); const data_get = document.getElementById("data_get");
fetch_ask("https://pronouns.cc/api/v1/users/clhd8desmggedm3q1t3g") fetch("https://pronouns.cc/api/v1/users/MeowcaTheoRange")
.then((x) => x.json()) .then((x) => x.json())
.then((user) => { .then((user) => {
data_get.innerHTML = user.links.reduce( data_get.innerHTML = user.links.reduce(

View file

@ -7,14 +7,18 @@
<link rel="stylesheet" href="/styles/normal.css" /> <link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/windows.css" /> <link rel="stylesheet" href="/styles/windows.css" />
<link rel="stylesheet" href="/styles/style.css" /> <link rel="stylesheet" href="/styles/style.css" />
<style></style> <style>
:root {
--base-color: 15, 100%;
}
</style>
</head> </head>
<body> <body>
<$ loader.html $> <$ loader.html $>
<header> <header>
<section> <section>
<h1>Projects</h1> <h1>Projects</h1>
<p>Various projects.</p> <p>Various projects I've made.</p>
</section> </section>
<$ nav.html $> <$ nav.html $>
<section id="accessibility" hidden></section> <section id="accessibility" hidden></section>

View file

@ -8,16 +8,14 @@ function reqData(start) {
if (resp != prevSha) { if (resp != prevSha) {
window.sessionStorage.setItem("commitsha", resp); window.sessionStorage.setItem("commitsha", resp);
if (start) return; if (start) return;
document.querySelector( document.querySelector("#updatesha").innerHTML = `from job v. ${prevSha} to job v. ${resp}`;
"#updatesha"
).innerHTML = `from job v. ${prevSha} to job v. ${resp}`;
document.querySelector("#updatedialog").classList.add("open"); document.querySelector("#updatedialog").classList.add("open");
} }
}; };
fetch_ask("https://ClockCheckGithub.meowcatheorange.repl.co") fetch("https://ClockCheckGithub.meowcatheorange.repl.co")
.then((x) => x.text()) .then(x => x.text())
.then((y) => donedata(y)); .then(y => donedata(y));
} }
setInterval(reqData, 60000); setInterval(reqData, 60000);

View file

@ -1,175 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fediverse Madness - abtmtr.link</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/myapp.css" />
<link rel="stylesheet" href="./style.css" />
<style>
:root {
--base-color: 60, 40%;
}
</style>
</head>
<body>
<header>
<section>
<h1>Fediverse Madness</h1>
<p>Competitive bracket-based comparisons of Fediverse users.</p>
<p>Requires JavaScript.</p>
</section>
<section id="accessibility" hidden></section>
</header>
<main>
<section id="whoami" hidden>
<h3>Who are you?</h3>
<span class="userinputbox">
<span>@</span>
<input type="text" name="user" id="user" class="userinstance" />
<span>@</span>
<input type="text" name="instance" id="instance" class="userinstance" />
</span>
<br />
<hr />
<button id="submitwhoami_followers">Play (Followers)</button>
<button id="submitwhoami_following">Play (Following)</button>
<button id="submitwhoami_manual">Play (Manual)</button>
<p id="errorwhoami"></p>
</section>
<section id="followers" hidden>
<h3>Followers list</h3>
<p id="loadingfollowers">Retrieving followers... Please wait.</p>
<table id="listfollowers" hidden>
<tr id="listfollowersvalues">
<th>S.</th>
<th>Av.</th>
<th>User</th>
<th>Handle</th>
</tr>
</table>
<div>
<button id="selectallfollowers">Select all</button>
<button id="selectnofollowers">Select none</button>
<button id="selectrandomfollowers">Select random</button>
<button id="deselectrandomfollowers">Deselect random</button>
</div>
<div>
<button id="morefollowers">Load more</button>
<button id="savehandlesfollowers">Save selection</button>
<button id="loadhandlesfollowers">Load selection</button>
</div>
<button id="submitfollowers" disabled>Done</button>
</section>
<section id="following" hidden>
<h3>Following list</h3>
<p id="loadingfollowing">Retrieving following... Please wait.</p>
<table id="listfollowing" hidden>
<tr id="listfollowingvalues">
<th>S.</th>
<th>Av.</th>
<th>User</th>
<th>Handle</th>
</tr>
</table>
<div>
<button id="selectallfollowing">Select all</button>
<button id="selectnofollowing">Select none</button>
<button id="selectrandomfollowing">Select random</button>
<button id="deselectrandomfollowing">Deselect random</button>
</div>
<div>
<button id="morefollowing">Load more</button>
<button id="savehandlesfollowing">Save selection</button>
<button id="loadhandlesfollowing">Load selection</button>
</div>
<button id="submitfollowing" disabled>Done</button>
</section>
<section id="manual" hidden>
<h3>Manual list</h3>
<p id="loadingmanual"></p>
<table id="listmanual" hidden>
<tr id="listmanualvalues">
<th>S.</th>
<th>Av.</th>
<th>User</th>
<th>Handle</th>
</tr>
</table>
<div>
<button id="selectallmanual">Select all</button>
<button id="selectnomanual">Select none</button>
<button id="selectrandommanual">Select random</button>
<button id="deselectrandommanual">Deselect random</button>
</div>
<div>
<span class="userinputbox">
<span>@</span>
<input type="text" name="user" id="manualuser" class="userinstance" />
<span>@</span>
<input type="text" name="instance" id="manualinstance" class="userinstance" />
</span>
<button id="moremanual">Fetch</button>
</div>
<button id="submitmanual" disabled>Done</button>
</section>
<section id="game" hidden>
<h3>Fight!</h3>
<p id="gameBracketLevel">Bracket Level ...</p>
<p id="gameBracketFight">Round ...</p>
<div class="gridUsersVert">
<div class="gridUser" id="gameUserOne">
<img src="" id="gameUserOneImage" width="128" height="128" />
<div>
<h3 id="gameUserOneName"></h3>
<p id="gameUserOneId"></p>
<!-- <p id="gameUserOneNote"></p>
<table id="gameUserOneFields">
</table> -->
</div>
</div>
<div class="gridUser" id="gameUserTwo">
<img src="" id="gameUserTwoImage" width="128" height="128" />
<div>
<h3 id="gameUserTwoName"></h3>
<p id="gameUserTwoId"></p>
<!-- <p id="gameUserTwoNote"></p>
<table id="gameUserTwoFields">
</table> -->
</div>
</div>
</div>
<hr />
<button id="gameSubmitLeft" disabled>Please wait...</button>
<button id="gameSubmitRight" disabled>Please wait...</button>
</section>
<section id="winner" hidden>
<h3>Winner</h3>
<p id="winnermessage">Congratulations, ...</p>
<div class="gridUser" id="winnerUser">
<img src="" id="winnerUserImage" width="128" height="128" />
<div>
<h3 id="winnerUserName"></h3>
<p id="winnerUserId"></p>
<!-- <p id="winnerUserNote"></p>
<table id="winnerUserFields">
</table> -->
</div>
</div>
<h3>Bracket</h3>
<div class="flexUsersVert" id="winnerusers">
</div>
<hr />
<button id="winnerPlayAgain">Play Again</button>
</section>
</main>
<section id="accessibility" hidden></section>
<script src="/scripts/accessibility.js"></script>
<script>
let game = null;
</script>
<script src="./script.js"></script>
<script src="./scripts/game.js"></script>
</body>
</html>

View file

@ -1,735 +0,0 @@
function escapeHtml(unsafe)
{
return unsafe
.replace(/<\/?script\/?>/g, "")
.replace(/<\/?img\/?>/g, "")
.replace(/<\/?iframe\/?>/g, "")
.replace(/<\/?xml\/?>/g, "")
.replace(/<\/?audio\/?>/g, "")
.replace(/<\/?video\/?>/g, "")
.replace(/<\/?object\/?>/g, "");
}
const n=x=>null;
const el_id_user = document.querySelector("#user");
const el_id_instance = document.querySelector("#instance");
const el_id_whoami = document.querySelector("#whoami");
const el_id_submitwhoamiFollowers = document.querySelector("#submitwhoami_followers");
const el_id_submitwhoamiFollowing = document.querySelector("#submitwhoami_following");
const el_id_submitwhoamiManual = document.querySelector("#submitwhoami_manual");
const el_id_errorwhoami = document.querySelector("#errorwhoami");
let gamemodeFollowers = "";
el_id_user.addEventListener("input", (e) => {
console.log(e);
// User types @ Chrome autofill
if (e.data === "@" || e.data === undefined) {
el_id_user.value =
el_id_user.value.replace(/@/gim, "");
el_id_instance.focus();
}
})
async function verify() {
const username = el_id_user.value.replace(/[^a-z0-9_]/gim, "");
const instance = el_id_instance.value.replace(/@/gim, "");
el_id_user.value = username;
el_id_instance.value = instance;
if (username.length < 1) return null;
if (instance.length < 1) return null;
// webfinger domain delegation
const domain = await fetch(`https://${instance}/.well-known/webfinger?resource=acct:${username}@${instance}`).catch(n);
if (domain == null || !domain.ok) return null;
const domain_json = (await domain.json());
const domain_url = new URL(domain_json.links.find(x=>x.type=="application/activity+json").href).hostname;
const user_req = await fetch(`https://${domain_url}/api/v1/accounts/lookup?acct=${username}`).catch(n);
if (user_req == null || !domain.ok) return null;
const user_json = await user_req.json();
window.localStorage.setItem("fediversemadness_game", JSON.stringify({ username, instance }));
return {
USER_ID: user_json.id,
INSTANCE: domain_url
};
}
async function lookupUser() {
const lsGame = JSON.parse(window.localStorage.getItem("fediversemadness_game"));
if (lsGame != null) {
el_id_user.value = lsGame.username;
el_id_instance.value = lsGame.instance;
}
}
lookupUser();
async function doVerif() {
el_id_submitwhoamiFollowers.disabled = true;
el_id_submitwhoamiFollowing.disabled = true;
el_id_submitwhoamiManual.disabled = true;
game = await verify();
el_id_submitwhoamiFollowers.disabled = false;
el_id_submitwhoamiFollowing.disabled = false;
el_id_submitwhoamiManual.disabled = false;
}
el_id_submitwhoamiFollowers.addEventListener("click", async (e) => {
el_id_errorwhoami.innerHTML = "";
await doVerif();
if (game == null) {
el_id_errorwhoami.innerHTML = "Invalid user!";
return false;
}
gamemodeFollowers = "followers";
startFollowers();
})
el_id_submitwhoamiFollowing.addEventListener("click", async (e) => {
el_id_errorwhoami.innerHTML = "";
await doVerif();
if (game == null) {
el_id_errorwhoami.innerHTML = "Invalid user!";
return false;
}
gamemodeFollowers = "following";
startFollowing();
})
el_id_submitwhoamiManual.addEventListener("click", async (e) => {
el_id_errorwhoami.innerHTML = "";
await doVerif();
if (game == null) {
el_id_errorwhoami.innerHTML = "Invalid user!";
return false;
}
gamemodeFollowers = "manual";
startManual();
})
// Followers
const el_id_followers = document.querySelector("#followers");
const el_id_loadingfollowers = document.querySelector("#loadingfollowers");
const el_id_listfollowers = document.querySelector("#listfollowers");
const el_id_listfollowersvalues = document.querySelector("#listfollowersvalues");
const el_id_selectallfollowers = document.querySelector("#selectallfollowers");
const el_id_selectrandomfollowers = document.querySelector("#selectrandomfollowers");
const el_id_deselectrandomfollowers = document.querySelector("#deselectrandomfollowers");
const el_id_selectnofollowers = document.querySelector("#selectnofollowers");
const el_id_morefollowers = document.querySelector("#morefollowers");
const el_id_submitfollowers = document.querySelector("#submitfollowers");
const el_id_savehandlesfollowers = document.querySelector("#savehandlesfollowers");
const el_id_loadhandlesfollowers = document.querySelector("#loadhandlesfollowers");
let selectboxes = [];
let userList = [];
let lastId = "";
let selectedUsers = [];
function generateFollowersMap() {
const checks = Array.from(el_id_listfollowers.querySelectorAll(".follower_checkbox"));
let codeMap = {};
checks.forEach((checkbox) => {
const handle = checkbox.parentElement.parentElement.querySelector(".follower_checkname");
codeMap[handle.innerHTML] = checkbox.checked;
});
return codeMap;
}
function readFollowersMap(m) {
if (m == null) return;
const handles = Array.from(el_id_listfollowers.querySelectorAll(".follower_checkname"));
handles.forEach((handle) => {
const checkbox = handle.parentElement.querySelector(".follower_checkbox");
if (m[handle.innerHTML] != null)
checkbox.checked = m[handle.innerHTML];
});
}
function renderNameHTML(name, user) {
return `<span>${escapeHtml(name).replace(/:([a-z0-9_-]+?):/gim, (m, p1) => {
const emoji = user.emojis.find(x => x.shortcode == p1);
if (emoji == null) return null;
return `<img src="${emoji.url}" width="16" height="16" title=":${emoji.shortcode}:" />`;
})}</span>`;
}
function startFollowers(reload = true) {
goToState("followers");
if (reload) {
selectboxes = [];
userList = [];
lastId = "";
selectedUsers = [];
el_id_listfollowers.innerHTML = "";
}
getFollowers(!reload);
}
async function getFollowers(dontLoadNew = false) {
el_id_submitfollowers.disabled = true;
el_id_morefollowers.disabled = true;
let res;
let out;
if (!dontLoadNew) {
el_id_loadingfollowers.innerHTML = `Retrieving followers... Please wait.`;
res = await fetch(`https://${game.INSTANCE}/api/v1/accounts/${game.USER_ID}/followers${lastId.length > 0 ? `?max_id=${lastId}` : ""}`);
out = await res.json();
}
el_id_submitfollowers.disabled = false;
el_id_morefollowers.disabled = false;
if (dontLoadNew) return false;
if (out.length < 1) {
el_id_loadingfollowers.innerHTML = `No followers found.`;
return false;
}
el_id_listfollowers.hidden = false;
out.forEach((cuser) => {
const element = document.createElement("tr");
element.innerHTML = `<td><input type="checkbox" class="follower_checkbox" checked onchange="checkSelectedAmtFollowers()" /></td>
<td><img src="${cuser.avatar}" width="32" height="32" /></td>
<td class="follower_namelabel">
<a href="${cuser.url}" target="_blank">
${renderNameHTML(cuser.display_name, cuser)}
</a>
</td>
<td class="follower_namelabel follower_checkname">@${cuser.fqn}</td>`;
el_id_listfollowers.appendChild(element);
});
userList.push(...out.map(user => ({
fqn: user.fqn || user.acct,
avatar: user.avatar,
bot: user.bot,
created_at: user.created_at,
display_name: user.display_name,
emojis: user.emojis,
fields: user.fields,
id: user.id,
note: user.note,
url: user.url
})));
lastId = userList.at(-1).id;
selectboxes = Array.from(el_id_listfollowers.querySelectorAll(".follower_checkbox"));
checkSelectedAmtFollowers();
}
el_id_selectallfollowers.addEventListener("click", () => {
selectboxes.forEach(x => x.checked = true);
checkSelectedAmtFollowers();
})
el_id_selectrandomfollowers.addEventListener("click", () => {
const unselectedPick = selectboxes.filter(x => !x.checked);
const selected = unselectedPick[Math.floor(Math.random() * (unselectedPick.length - 1))];
if (selected == null) return;
selected.checked = true;
checkSelectedAmtFollowers();
})
el_id_deselectrandomfollowers.addEventListener("click", () => {
const selectedPick = selectboxes.filter(x => x.checked);
const selected = selectedPick[Math.floor(Math.random() * (selectedPick.length - 1))];
if (selected == null) return;
selected.checked = false;
checkSelectedAmtFollowers();
})
el_id_selectnofollowers.addEventListener("click", () => {
selectboxes.forEach(x => x.checked = false);
checkSelectedAmtFollowers();
})
el_id_savehandlesfollowers.addEventListener("click", () => window.localStorage.setItem("fediversemadness_followerssel", JSON.stringify(generateFollowersMap())));
el_id_loadhandlesfollowers.addEventListener("click", () => readFollowersMap(JSON.parse(window.localStorage.getItem("fediversemadness_followerssel"))));
el_id_morefollowers.addEventListener("click", () => getFollowers());
el_id_submitfollowers.addEventListener("click", () => {
selectedUsers = [];
selectboxes.forEach(({checked}, i) => {
if (checked) selectedUsers.push(userList.at(i));
});
if (selectedUsers.length < 2) {
el_id_loadingfollowers.innerHTML = `Selected user count is less than 2! (${selectboxes.filter(x => x.checked).length} selected)`;
return;
}
sortSelectedUsers();
})
function checkSelectedAmtFollowers() {
el_id_loadingfollowers.innerHTML = `${selectboxes.filter(x => x.checked).length} selected`;
}
// Following
const el_id_following = document.querySelector("#following");
const el_id_loadingfollowing = document.querySelector("#loadingfollowing");
const el_id_listfollowing = document.querySelector("#listfollowing");
const el_id_listfollowingvalues = document.querySelector("#listfollowingvalues");
const el_id_selectallfollowing = document.querySelector("#selectallfollowing");
const el_id_selectrandomfollowing = document.querySelector("#selectrandomfollowing");
const el_id_deselectrandomfollowing = document.querySelector("#deselectrandomfollowing");
const el_id_selectnofollowing = document.querySelector("#selectnofollowing");
const el_id_morefollowing = document.querySelector("#morefollowing");
const el_id_submitfollowing = document.querySelector("#submitfollowing");
const el_id_savehandlesfollowing = document.querySelector("#savehandlesfollowing");
const el_id_loadhandlesfollowing = document.querySelector("#loadhandlesfollowing");
function generateFollowingMap() {
const checks = Array.from(el_id_listfollowing.querySelectorAll(".following_checkbox"));
let codeMap = {};
checks.forEach((checkbox) => {
const handle = checkbox.parentElement.parentElement.querySelector(".following_checkname");
codeMap[handle.innerHTML] = checkbox.checked;
});
return codeMap;
}
function readFollowingMap(m) {
if (m == null) return;
const handles = Array.from(el_id_listfollowing.querySelectorAll(".following_checkname"));
handles.forEach((handle) => {
const checkbox = handle.parentElement.querySelector(".following_checkbox");
if (m[handle.innerHTML] != null)
checkbox.checked = m[handle.innerHTML];
});
}
function startFollowing(reload = true) {
goToState("following");
if (reload) {
selectboxes = [];
userList = [];
lastId = "";
selectedUsers = [];
el_id_listfollowing.innerHTML = "";
}
getFollowing(!reload);
}
async function getFollowing(dontLoadNew = false) {
el_id_submitfollowing.disabled = true;
el_id_morefollowing.disabled = true;
let res;
let out;
if (!dontLoadNew) {
el_id_loadingfollowing.innerHTML = `Retrieving following... Please wait.`;
res = await fetch(`https://${game.INSTANCE}/api/v1/accounts/${game.USER_ID}/following${lastId.length > 0 ? `?max_id=${lastId}` : ""}`);
out = await res.json();
}
el_id_submitfollowing.disabled = false;
el_id_morefollowing.disabled = false;
if (dontLoadNew) return false;
if (out.length < 1) {
el_id_loadingfollowing.innerHTML = `No following found.`;
return false;
}
el_id_listfollowing.hidden = false;
out.forEach((cuser) => {
const element = document.createElement("tr");
element.innerHTML = `<td><input type="checkbox" class="following_checkbox" checked onchange="checkSelectedAmtFollowing()" /></td>
<td><img src="${cuser.avatar}" width="32" height="32" /></td>
<td class="following_namelabel">
<a href="${cuser.url}" target="_blank">
${renderNameHTML(cuser.display_name, cuser)}
</a>
</td>
<td class="following_namelabel following_checkname">@${cuser.fqn}</td>`;
el_id_listfollowing.appendChild(element);
});
userList.push(...out.map(user => ({
fqn: user.fqn,
avatar: user.avatar,
bot: user.bot,
created_at: user.created_at,
display_name: user.display_name,
emojis: user.emojis,
fields: user.fields,
id: user.id,
note: user.note,
url: user.url
})));
lastId = userList.at(-1).id;
selectboxes = Array.from(el_id_listfollowing.querySelectorAll(".following_checkbox"));
checkSelectedAmtFollowing();
}
el_id_selectallfollowing.addEventListener("click", () => {
selectboxes.forEach(x => x.checked = true);
checkSelectedAmtFollowing();
})
el_id_selectrandomfollowing.addEventListener("click", () => {
const unselectedPick = selectboxes.filter(x => !x.checked);
const selected = unselectedPick[Math.floor(Math.random() * (unselectedPick.length - 1))];
if (selected == null) return;
selected.checked = true;
checkSelectedAmtFollowing();
})
el_id_deselectrandomfollowing.addEventListener("click", () => {
const selectedPick = selectboxes.filter(x => x.checked);
const selected = selectedPick[Math.floor(Math.random() * (selectedPick.length - 1))];
if (selected == null) return;
selected.checked = false;
checkSelectedAmtFollowing();
})
el_id_selectnofollowing.addEventListener("click", () => {
selectboxes.forEach(x => x.checked = false);
checkSelectedAmtFollowing();
})
el_id_savehandlesfollowing.addEventListener("click", () => window.localStorage.setItem("fediversemadness_followingsel", JSON.stringify(generateFollowingMap())));
el_id_loadhandlesfollowing.addEventListener("click", () => readFollowingMap(JSON.parse(window.localStorage.getItem("fediversemadness_followingsel"))));
el_id_morefollowing.addEventListener("click", () => getFollowing());
el_id_submitfollowing.addEventListener("click", () => {
selectedUsers = [];
selectboxes.forEach(({checked}, i) => {
if (checked) selectedUsers.push(userList.at(i));
});
if (selectedUsers.length < 2) {
el_id_loadingfollowing.innerHTML = `Selected user count is less than 2! (${selectboxes.filter(x => x.checked).length} selected)`;
return;
}
sortSelectedUsers();
})
function checkSelectedAmtFollowing() {
el_id_loadingfollowing.innerHTML = `${selectboxes.filter(x => x.checked).length} selected`;
}
// Manual
const el_id_manual = document.querySelector("#manual");
const el_id_loadingmanual = document.querySelector("#loadingmanual");
const el_id_listmanual = document.querySelector("#listmanual");
const el_id_listmanualvalues = document.querySelector("#listmanualvalues");
const el_id_selectallmanual = document.querySelector("#selectallmanual");
const el_id_selectrandommanual = document.querySelector("#selectrandommanual");
const el_id_deselectrandommanual = document.querySelector("#deselectrandommanual");
const el_id_selectnomanual = document.querySelector("#selectnomanual");
const el_id_manualuser = document.querySelector("#manualuser");
const el_id_manualinstance = document.querySelector("#manualinstance");
const el_id_moremanual = document.querySelector("#moremanual");
const el_id_submitmanual = document.querySelector("#submitmanual");
function startManual(reload = true) {
goToState("manual");
if (reload) {
selectboxes = [];
userList = [];
lastId = "";
selectedUsers = [];
el_id_listmanual.innerHTML = "";
}
checkSelectedAmtManual();
}
async function addUserToManual() {
const username = el_id_manualuser.value.replace(/[^a-z0-9_]/gim, "");
const instance = el_id_manualinstance.value.replace(/@/gim, "");
el_id_manualuser.value = username;
el_id_manualinstance.value = instance;
if (username.length < 1) return null;
if (instance.length < 1) return null;
el_id_submitmanual.disabled = true;
el_id_moremanual.disabled = true;
el_id_loadingmanual.innerHTML = `Finding user...`;
let res = await fetch(`https://${game.INSTANCE}/api/v1/accounts/lookup?acct=${username}@${instance}`);
let out = await res.json();
el_id_submitmanual.disabled = false;
el_id_moremanual.disabled = false;
if (out.length < 1) {
el_id_loadingmanual.innerHTML = `No manual found.`;
return false;
}
el_id_listmanual.hidden = false;
const element = document.createElement("tr");
element.innerHTML = `<td><input type="checkbox" class="manual_checkbox" checked onchange="checkSelectedAmtManual()" /></td>
<td><img src="${out.avatar}" width="32" height="32" /></td>
<td class="manual_namelabel">
<a href="${out.url}" target="_blank">
${renderNameHTML(out.display_name, out)}
</a>
</td>
<td class="manual_namelabel manual_checkname">@${out.fqn}</td>`;
el_id_listmanual.appendChild(element);
userList.push({
fqn: out.fqn,
avatar: out.avatar,
bot: out.bot,
created_at: out.created_at,
display_name: out.display_name,
emojis: out.emojis,
fields: out.fields,
id: out.id,
note: out.note,
url: out.url
});
selectboxes = Array.from(el_id_listmanual.querySelectorAll(".manual_checkbox"));
checkSelectedAmtManual();
}
el_id_selectallmanual.addEventListener("click", () => {
selectboxes.forEach(x => x.checked = true);
checkSelectedAmtManual();
})
el_id_selectrandommanual.addEventListener("click", () => {
const unselectedPick = selectboxes.filter(x => !x.checked);
const selected = unselectedPick[Math.floor(Math.random() * (unselectedPick.length - 1))];
if (selected == null) return;
selected.checked = true;
checkSelectedAmtManual();
})
el_id_deselectrandommanual.addEventListener("click", () => {
const selectedPick = selectboxes.filter(x => x.checked);
const selected = selectedPick[Math.floor(Math.random() * (selectedPick.length - 1))];
if (selected == null) return;
selected.checked = false;
checkSelectedAmtManual();
})
el_id_selectnomanual.addEventListener("click", () => {
selectboxes.forEach(x => x.checked = false);
checkSelectedAmtManual();
})
el_id_moremanual.addEventListener("click", () => addUserToManual());
el_id_submitmanual.addEventListener("click", () => {
selectedUsers = [];
selectboxes.forEach(({checked}, i) => {
if (checked) selectedUsers.push(userList.at(i));
});
if (selectedUsers.length < 2) {
el_id_loadingmanual.innerHTML = `Selected user count is less than 2! (${selectboxes.filter(x => x.checked).length} selected)`;
return;
}
sortSelectedUsers();
})
function checkSelectedAmtManual() {
el_id_loadingmanual.innerHTML = `${selectboxes.filter(x => x.checked).length} selected`;
}
// Brackets
function shuf(array) {
var count = array.length,
randomnumber,
temp;
while (count) {
randomnumber = Math.random() * count-- | 0;
temp = array[count];
array[count] = array[randomnumber];
array[randomnumber] = temp
}
return array;
}
function chunk(arr, size) {
return Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
arr.slice(i * size, i * size + size)
);
}
const el_id_game = document.querySelector("#game");
const el_id_gameUserOne = document.querySelector("#gameUserOne");
const el_id_gameUserOneImage = document.querySelector("#gameUserOneImage");
const el_id_gameUserOneName = document.querySelector("#gameUserOneName");
const el_id_gameUserOneId = document.querySelector("#gameUserOneId");
// const el_id_gameUserOneNote = document.querySelector("#gameUserOneNote");
// const el_id_gameUserOneFields = document.querySelector("#gameUserOneFields");
const el_id_gameUserTwo = document.querySelector("#gameUserTwo");
const el_id_gameUserTwoImage = document.querySelector("#gameUserTwoImage");
const el_id_gameUserTwoName = document.querySelector("#gameUserTwoName");
const el_id_gameUserTwoId = document.querySelector("#gameUserTwoId");
// const el_id_gameUserTwoNote = document.querySelector("#gameUserTwoNote");
// const el_id_gameUserTwoFields = document.querySelector("#gameUserTwoFields");
const el_id_gameSubmitLeft = document.querySelector("#gameSubmitLeft");
const el_id_gameSubmitRight = document.querySelector("#gameSubmitRight");
const el_id_gameBracketLevel = document.querySelector("#gameBracketLevel");
const el_id_gameBracketFight = document.querySelector("#gameBracketFight");
let state = [];
let curDepth = 0;
let curFight = 0;
function sortSelectedUsers() {
goToState("game");
state = [];
curDepth = 0;
curFight = 0;
state.push(chunk(shuf(selectedUsers), 2));
state.push([]);
prepareGameStage();
}
function prepareGameStage() {
el_id_gameSubmitLeft.innerHTML = "Please wait...";
el_id_gameSubmitRight.innerHTML = "Please wait...";
el_id_gameUserOneImage.src = "";
el_id_gameUserTwoImage.src = "";
el_id_gameBracketLevel.innerHTML = "Bracket Level " + (curDepth + 1);
el_id_gameBracketFight.innerHTML = "Round " + (curFight + 1);
const curSubStage = state[curDepth][curFight];
el_id_gameUserOneImage.src = curSubStage[0].avatar;
el_id_gameUserOneName.innerHTML = `<a href="${curSubStage[0].url}" target="_blank">${renderNameHTML(escapeHtml(curSubStage[0].display_name), curSubStage[0])}</a>`;
el_id_gameUserOneId.innerHTML = "@" + curSubStage[0].fqn;
// el_id_gameUserOneNote.innerHTML = escapeHtml(curSubStage[0].note);
// el_id_gameUserOneFields.innerHTML = curSubStage[0].fields.reduce((pv, fields) => pv + `<tr><td>${escapeHtml(fields.name)}</td><td>${escapeHtml(fields.value)}</td></tr>`, "");
el_id_gameUserTwoImage.src = curSubStage[1].avatar;
el_id_gameUserTwoName.innerHTML = `<a href="${curSubStage[1].url}" target="_blank">${renderNameHTML(escapeHtml(curSubStage[1].display_name), curSubStage[1])}</a>`;
el_id_gameUserTwoId.innerHTML = "@" + curSubStage[1].fqn;
// el_id_gameUserTwoNote.innerHTML = escapeHtml(curSubStage[1].note);
// el_id_gameUserTwoFields.innerHTML = curSubStage[1].fields.reduce((pv, fields) => pv + `<tr><td>${escapeHtml(fields.name)}</td><td>${escapeHtml(fields.value)}</td></tr>`, "");
el_id_gameSubmitLeft.disabled = false;
el_id_gameSubmitRight.disabled = false;
el_id_gameSubmitLeft.innerHTML = "Pick " + "@" + curSubStage[0].fqn;
el_id_gameSubmitRight.innerHTML = "Pick " + "@" + curSubStage[1].fqn;
}
function addNewThingy(thingy) {
if (state[curDepth + 1] == null) state.push([]);
if (curFight % 2 == 0)
state[curDepth + 1][Math.floor(curFight / 2)] = [];
state[curDepth + 1][Math.floor(curFight / 2)].push(thingy);
}
el_id_gameSubmitLeft.addEventListener("click", () => {
addNewThingy(state[curDepth][curFight][0]);
nextStage();
})
el_id_gameSubmitRight.addEventListener("click", () => {
addNewThingy(state[curDepth][curFight][1]);
nextStage();
})
function nextStage() {
if (curFight >= state[curDepth].length - 1) {
curDepth++;
curFight = 0;
} else curFight++;
console.log(state, curFight, curDepth);
if (state[curDepth][curFight].length < 2) {
if (state[curDepth].length < 2) {
endGame();
return;
}
addNewThingy(state[curDepth][curFight][0]);
curDepth++;
curFight = 0;
}
prepareGameStage();
}
// End
const el_id_winner = document.querySelector("#winner");
const el_id_winnerUser = document.querySelector("#winnerUser");
const el_id_winnerUserImage = document.querySelector("#winnerUserImage");
const el_id_winnerUserName = document.querySelector("#winnerUserName");
const el_id_winnerUserId = document.querySelector("#winnerUserId");
// const el_id_winnerUserNote = document.querySelector("#winnerUserNote");
// const el_id_winnerUserFields = document.querySelector("#winnerUserFields");
const el_id_winnermessage = document.querySelector("#winnermessage");
const el_id_winnerusers = document.querySelector("#winnerusers");
const el_id_winnerPlayAgain = document.querySelector("#winnerPlayAgain");
function endGame() {
goToState("winner");
const winningPlayer = state[curDepth][curFight][0];
el_id_winnerUserImage.src = winningPlayer.avatar;
el_id_winnerUserName.innerHTML = `<a href="${winningPlayer.url}" target="_blank">${renderNameHTML(escapeHtml(winningPlayer.display_name), winningPlayer)}</a>`;
el_id_winnerUserId.innerHTML = "@" + winningPlayer.fqn;
// el_id_winnerUserNote.innerHTML = escapeHtml(winningPlayer.note);
// el_id_winnerUserFields.innerHTML = winningPlayer.fields.reduce((pv, fields) => pv + `<tr><td>${escapeHtml(fields.name)}</td><td>${escapeHtml(fields.value)}</td></tr>`, "");
el_id_winnermessage.innerHTML = `Congratulations, ${renderNameHTML(escapeHtml(winningPlayer.display_name), winningPlayer)}! You won the bracket!`;
el_id_winnerusers.innerHTML = state.reduce((pv, cs) => {
return pv + `<div class="flexUser">${cs.reduce((pvus, cus) => {
console.log(cus);
return pvus + `<div class="flexUserGroup"><div class="flexUserUser">
<img src="${cus[0].avatar}" width="64" height="64" />
<h3><a href="${cus[0].url}" target="_blank">${renderNameHTML(escapeHtml(cus[0].display_name), cus[0])}</a></h3>
<p>@${cus[0].fqn}</p>
</div>${cus[1] ? `<div class="flexUserUser">
<img src="${cus[1].avatar}" width="64" height="64" />
<h3><a href="${cus[1].url}" target="_blank">${renderNameHTML(escapeHtml(cus[1].display_name), cus[1])}</a></h3>
<p>@${cus[1].fqn}</p>
</div>` : ""}</div>`
}, "")}</div>`
}, "");
}
el_id_winnerPlayAgain.addEventListener("click", (x) => {
state = [];
selectedUsers = [];
curDepth = 0;
curFight = 0;
switch (gamemodeFollowers) {
case "followers":
startFollowers(false);
break;
case "following":
startFollowing(false);
break;
case "manual":
startManual(false);
break;
}
})
// Game state
const gameStates = new Map([
["whoami", {
state: el_id_whoami
}],
["followers", {
state: el_id_followers
}],
["following", {
state: el_id_following
}],
["manual", {
state: el_id_manual
}],
["game", {
state: el_id_game,
links: {
"winner": () => gamemodeFollowers
}
}],
["winner", {
state: el_id_winner
}]
]);
let prevState = "";
function goToState(state) {
window.location.hash = state;
const curState = gameStates.get(state);
if (curState.links != null)
if (curState.links[prevState])
return goToState(curState.links[prevState]());
gameStates.forEach(x => x.state.hidden = true);
curState.state.hidden = false;
prevState = state;
}
goToState("whoami");
addEventListener("popstate", () => goToState(window.location.hash.slice(1)));

View file

@ -1,91 +0,0 @@
.userinstance {
border: 0;
margin: 0;
padding: 0;
vertical-align: middle;
}
.userinputbox {
display: inline-block;
border: var(--border-width) var(--border-style) var(--border-color);
border-radius: var(--border-radius);
padding-inline: 0.25em;
padding-block: 0.25em;
white-space: nowrap;
}
/* .userinputbox * {
vertical-align: 0%;
} */
#listfollowers, #listfollowing {
display: inline-block;
position: relative;
width: 100%;
max-height: 31.25em;
overflow:auto;
}
#listfollowers #listfollowersvalues,
#listfollowing #listfollowingvalues {
position: sticky;
top: 0;
background-color: var(--background-color);
}
.followers_namelabel,
.following_namelabel {
max-width: 20em;
}
.gridUser {
display: grid;
grid-template-columns: 128px auto;
overflow: auto;
gap: 0.5em;
}
.gridUsersVert {
display: grid;
overflow: auto;
gap: 0.5em;
}
.flexUsersVert {
display: flex;
flex-direction: row;
align-items: stretch;
overflow: auto;
gap: 0.5em;
}
.flexUser {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: space-around;
gap: 0.5em;
}
.flexUserUser {
text-align: center;
width: 256px;
min-width: 256px;
height: 150px;
min-height: 150px;
box-sizing: content-box;
overflow: hidden;
word-wrap: break-word;
}
.flexUserGroup {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: space-around;
gap: 0.5em;
}
.flexUserUser * {
word-wrap: break-word;
}

View file

@ -5,58 +5,12 @@
"normalize": "#808080" "normalize": "#808080"
}, },
"items": [ "items": [
{
"name": "Fediverse Madness",
"date": 1710804730000,
"description": [
"A March Madness style bracket-based competition for the entities on the Fediverse.",
"Compare your friends! Have a meltdown-sized crisis over who you like more!",
"This project will make you do it.",
"CONTENT WARNING // uncomfortable decisions"
],
"url": "/projects/item/fediverse-madness/",
"tags": [
"normalize"
]
},
{
"name": "LastFMDownloader",
"date": 1708382160000,
"description": [
"Downloader from Last.fm scrobble to MP3 file. Uploads to Nextcloud because it can.",
"Not piracy. I don't think so?"
],
"url": "https://git.abtmtr.link/MeowcaTheoRange/LastFMDownloader",
"hotlink": true
},
{
"name": "XKCDViewer",
"date": 1702875360000,
"description": [
"Unofficial XKCD Reader for Roku®."
],
"url": "https://git.abtmtr.link/MeowcaTheoRange/XKCDViewer",
"hotlink": true
},
{
"name": "Karkat Public License",
"date": 1704316500000,
"description": [
"The Karkat Public License"
],
"url": "https://git.abtmtr.link/MeowcaTheoRange/KarkatPublicLicense",
"hotlink": true
},
{ {
"name": "DiceApp", "name": "DiceApp",
"date": 1655589660000, "date": 1655589660000,
"description": [ "description": ["An app. For managing virtual dice."],
"An app. For managing virtual dice."
],
"url": "/projects/item/dice/", "url": "/projects/item/dice/",
"tags": [ "tags": ["old"]
"old"
]
}, },
{ {
"name": "FunnyClock²", "name": "FunnyClock²",
@ -65,9 +19,16 @@
"FunnyClock² is the successor of FunnyClock, an application made for my school to show when smartboards are idle." "FunnyClock² is the successor of FunnyClock, an application made for my school to show when smartboards are idle."
], ],
"url": "/projects/item/clock/", "url": "/projects/item/clock/",
"tags": [ "tags": ["old"]
"old" },
] {
"name": "MTRUWSaDMfHTML5",
"date": 1701743828350,
"description": [
"MeowcaTheoRange's Unnecessary Windowing System and Desktop Manager for HTML5.<br />Works best outside of windowed mode."
],
"url": "/projects/item/wm/",
"tags": ["normalize", "new"]
}, },
{ {
"name": "Normalize", "name": "Normalize",
@ -76,9 +37,7 @@
"Normalize is a simple CSS library made to provide good-looking yet simple HTML element styles." "Normalize is a simple CSS library made to provide good-looking yet simple HTML element styles."
], ],
"url": "/projects/item/normalize/", "url": "/projects/item/normalize/",
"tags": [ "tags": ["normalize"]
"normalize"
]
}, },
{ {
"name": "JellyBean's Mid-Sim", "name": "JellyBean's Mid-Sim",
@ -87,13 +46,8 @@
"JellyBean's Mid-Sim, a 1K rhythm game made in the HTML5-JS game engine Kaboom." "JellyBean's Mid-Sim, a 1K rhythm game made in the HTML5-JS game engine Kaboom."
], ],
"url": "/projects/item/midsim/", "url": "/projects/item/midsim/",
"size": [ "size": [700, 400],
700, "tags": ["old"]
400
],
"tags": [
"old"
]
}, },
{ {
"name": "WozSteamGen", "name": "WozSteamGen",
@ -103,9 +57,7 @@
"It's an API-less adaptation of Steam The Woz, running completely locally." "It's an API-less adaptation of Steam The Woz, running completely locally."
], ],
"url": "/projects/item/woz/", "url": "/projects/item/woz/",
"tags": [ "tags": ["normalize"]
"normalize"
]
}, },
{ {
"name": "HexFlagGen", "name": "HexFlagGen",
@ -115,9 +67,7 @@
"I dislike DRM and support trans rights." "I dislike DRM and support trans rights."
], ],
"url": "/projects/item/hex/", "url": "/projects/item/hex/",
"tags": [ "tags": ["normalize"]
"normalize"
]
} }
] ]
} }

View file

@ -1,6 +1,5 @@
const data_get = document.getElementById("data_get"); const data_get = document.getElementById("data_get");
fetch("./public/projects.json")
fetch_ask("./public/projects.json")
.then((x) => x.json()) .then((x) => x.json())
.then((projects) => { .then((projects) => {
data_get.innerHTML = projects.items data_get.innerHTML = projects.items
@ -27,7 +26,7 @@ fetch_ask("./public/projects.json")
(html, descfragment) => html + `<p>${descfragment}</p>`, (html, descfragment) => html + `<p>${descfragment}</p>`,
"" ""
)} )}
${project.hotlink ? `` : `<p> <p>
<button <button
onclick="window.manager.createWindow('${project.url}', false${ onclick="window.manager.createWindow('${project.url}', false${
project.size?.[0] ? ", " + project.size[0] : "" project.size?.[0] ? ", " + project.size[0] : ""
@ -35,7 +34,7 @@ fetch_ask("./public/projects.json")
> >
Open Window Open Window
</button> </button>
</p>`}`, </p>`,
"" ""
); );
}); });

View file

@ -1,96 +0,0 @@
# https://seirdy.one/robots.txt
User-agent: *
Disallow: /noindex/
Disallow: /misc/
# I opt out of online advertising so malware that injects ads on my site won't get paid.
# You should do the same. my ads.txt file contains a standard placeholder to forbid any
# compliant ad networks from paying for ad placement on my domain.
User-Agent: Adsbot
Disallow: /
Allow: /ads.txt
Allow: /app-ads.txt
## IP-violation scanners ##
# The next three are borrowed from https://www.videolan.org/robots.txt
# > This robot collects content from the Internet for the sole purpose of # helping educational institutions prevent plagiarism. [...] we compare student papers against the content we find on the Internet to see if we # can find similarities. (http://www.turnitin.com/robot/crawlerinfo.html)
# --> fuck off.
User-Agent: TurnitinBot
Disallow: /
# > NameProtect engages in crawling activity in search of a wide range of brand and other intellectual property violations that may be of interest to our clients. (http://www.nameprotect.com/botinfo.html)
# --> fuck off.
User-Agent: NPBot
Disallow: /
# iThenticate is a new service we have developed to combat the piracy of intellectual property and ensure the originality of written work for# publishers, non-profit agencies, corporations, and newspapers. (http://www.slysearch.com/)
# --> fuck off.
User-Agent: SlySearch
Disallow: /
# BLEXBot assists internet marketers to get information on the link structure of sites and their interlinking on the web, to avoid any technical and possible legal issues and improve overall online experience. (http://webmeup-crawler.com/)
# --> fuck off.
User-Agent: BLEXBot
Disallow: /
# Providing Intellectual Property professionals with superior brand protection services by artfully merging the latest technology with expert analysis. (https://www.checkmarknetwork.com/spider.html/)
# "The Internet is just way to big to effectively police alone." (ACTUAL quote)
# --> fuck off.
User-agent: CheckMarkNetwork/1.0 (+https://www.checkmarknetwork.com/spider.html)
Disallow: /
# Stop trademark violations and affiliate non-compliance in paid search. Automatically monitor your partner and affiliates online marketing to protect yourself from harmful brand violations and regulatory risks. We regularly crawl websites on behalf of our clients to ensure content compliance with brand and regulatory guidelines. (https://www.brandverity.com/why-is-brandverity-visiting-me)
# --> fuck off.
User-agent: BrandVerity/1.0
Disallow: /
## Misc. icky stuff ##
# Pipl assembles online identity information from multiple independent sources to create the most complete picture of a digital identity and connect it to real people and their offline identity records. When all the fragments of online identity data are collected, connected, and corroborated, the result is a more trustworthy identity.
# --> fuck off.
User-agent: PiplBot
Disallow: /
## Gen-AI data scrapers ##
# Eat shit, OpenAI.
User-agent: ChatGPT-User
Disallow: /
User-agent: GPTBot
Disallow: /
# Official way to opt-out of Google's generative AI training:
# <https://developers.google.com/search/docs/crawling-indexing/overview-google-crawlers>
User-agent: Google-Extended
Disallow: /
# There isn't any public documentation for this AFAICT.
# Reuters thinks this works so I might as well give it a shot.
User-agent: anthropic-ai
Disallow: /
User-agent: Claude-Web
Disallow: /
# FacebookBot crawls public web pages to improve language models for our speech recognition technology.
# <https://developers.facebook.com/docs/sharing/bot/?_fb_noscript=1>
User-Agent: FacebookBot
Disallow: /
# I'm not blocking CCBot for now. It publishes a free index for anyone to use.
# Googe used this to train the initial version of Bard (now called Gemini).
# I allow CCBot since its index is also used for upstart/hobbyist search engines
# like Alexandria and for genuinely useful academic work I personally like.
# I allow Owler for similar reasons:
# <https://openwebsearch.eu/owler/#owler-opt-out>
# <https://openwebsearch.eu/common-goals-with-common-crawl/>.
# Omgilibot/Omgili is similar to CCBot, except it sells the scrape results.
# I'm not familiar enough with Omgili to make a call here.
# In the long run, my embedded robots meta-tags and headers could cover gen-AI
# I don't block cohere-ai or Perplexitybot: they don't appear to actually scrape data for LLM training purposes. The crawling powers search engines with integrated pre-trained LLMs.
# TODO: investigate whether YouBot scrapes to train its own in-house LLM.
Sitemap: https://seirdy.one/sitemap.xml

View file

@ -13,21 +13,18 @@ var accBase;
var propagateStyles = propagateStyles ?? null; var propagateStyles = propagateStyles ?? null;
const FONTS = [ const FONTS = [
["Lexend Deca", ""], ["Lexend Deca"],
["Renogare", "Renogare"], ["Renogare"],
["OpenDyslexic", "OpenDyslexic"], ["OpenDyslexic"],
// ["Mojangles", "Minecraft"], // ["Mojangles", "Minecraft"],
// ["Nintendo DS", "NDS12"], // ["Nintendo DS", "NDS12"],
[ ["System Default", `-apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto,
"System Default",
`-apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto,
Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;,
&quot;Segoe UI Symbol&quot;`, &quot;Segoe UI Symbol&quot;`],
],
["Sans-serif", "sans-serif"], ["Sans-serif", "sans-serif"],
["Serif", "serif"], ["Serif", "serif"],
["Mono", "monospace"], ["Mono", "monospace"]
]; ]
function createAccessibilityNodes() { function createAccessibilityNodes() {
document.querySelector("#accessibility").innerHTML = `<details> document.querySelector("#accessibility").innerHTML = `<details>
@ -55,10 +52,7 @@ function createAccessibilityNodes() {
</label><br /> </label><br />
<label for="acc-font">Page font: <label for="acc-font">Page font:
<select id="acc-font"> <select id="acc-font">
${FONTS.map( ${FONTS.map(([name, value]) => `<option value="${value ?? name}" selected>${name}</option>`)}
([name, value]) =>
`<option value="${value}" selected>${name}</option>`
)}
</select> </select>
</label><br /> </label><br />
<label for="acc-hue">Page hue: <label for="acc-hue">Page hue:
@ -125,42 +119,22 @@ function createAccessibilityNodes() {
accHueOverride.addEventListener("change", function (e) { accHueOverride.addEventListener("change", function (e) {
window.localStorage.setItem("acc-hue-over", e.target.checked); window.localStorage.setItem("acc-hue-over", e.target.checked);
changeColor( changeColor(accHue?.value, accSaturation?.value, e.target.checked, accSaturationOverride?.checked);
accHue?.value,
accSaturation?.value,
e.target.checked,
accSaturationOverride?.checked
);
}); });
accHue.addEventListener("input", function (e) { accHue.addEventListener("input", function (e) {
window.localStorage.setItem("acc-hue", e.target.value); window.localStorage.setItem("acc-hue", e.target.value);
changeColor( changeColor(e.target.value, accSaturation?.value, accHueOverride?.checked, accSaturationOverride?.checked);
e.target.value,
accSaturation?.value,
accHueOverride?.checked,
accSaturationOverride?.checked
);
}); });
accSaturationOverride.addEventListener("change", function (e) { accSaturationOverride.addEventListener("change", function (e) {
window.localStorage.setItem("acc-sat-over", e.target.checked); window.localStorage.setItem("acc-sat-over", e.target.checked);
changeColor( changeColor(accHue?.value, accSaturation?.value, accHueOverride?.checked, e.target.checked);
accHue?.value,
accSaturation?.value,
accHueOverride?.checked,
e.target.checked
);
}); });
accSaturation.addEventListener("input", function (e) { accSaturation.addEventListener("input", function (e) {
window.localStorage.setItem("acc-sat", e.target.value); window.localStorage.setItem("acc-sat", e.target.value);
changeColor( changeColor(accHue?.value, e.target.value, accHueOverride?.checked, accSaturationOverride?.checked);
accHue?.value,
e.target.value,
accHueOverride?.checked,
accSaturationOverride?.checked
);
}); });
accBase.addEventListener("change", function (e) { accBase.addEventListener("change", function (e) {
@ -169,20 +143,6 @@ function createAccessibilityNodes() {
}); });
} }
// For metered conenctions
async function fetch_ask(...args) {
// return new Promise(async (resolve, reject) => {
// let canFetch = window.sessionStorage.getItem("canFetch");
// if (canFetch == null) {
// canFetch = confirm("may the site fetch data for this session?");
// window.sessionStorage.setItem("canFetch", canFetch);
// }
// if (canFetch === "true") resolve(await fetch(...args));
// else reject(null);
// });
return await fetch(...args);
}
window.addEventListener("DOMContentLoaded", (e) => initializeChanges(e, true)); window.addEventListener("DOMContentLoaded", (e) => initializeChanges(e, true));
// on localStorage change // on localStorage change
@ -204,8 +164,8 @@ function initializeChanges(_, loading) {
width = window.localStorage.getItem("acc-width"); width = window.localStorage.getItem("acc-width");
} }
let font = window.localStorage.getItem("acc-font"); let font = window.localStorage.getItem("acc-font");
if (font == null || font == "Lexend Deca") { if (font == null) {
window.localStorage.setItem("acc-font", ""); window.localStorage.setItem("acc-font", "Lexend Deca");
font = window.localStorage.getItem("acc-font"); font = window.localStorage.getItem("acc-font");
} }
let hueOver = window.localStorage.getItem("acc-hue-over"); let hueOver = window.localStorage.getItem("acc-hue-over");
@ -245,11 +205,9 @@ function initializeChanges(_, loading) {
if (width != null && accWidth != null) accWidth.value = width; if (width != null && accWidth != null) accWidth.value = width;
if (font != null && accFont != null) accFont.value = font; if (font != null && accFont != null) accFont.value = font;
if (hueOver != null && accHueOverride != null) if (hueOver != null && accHueOverride != null) accHueOverride.checked = hueOver === "true";
accHueOverride.checked = hueOver === "true";
if (hue != null && accHue != null) accHue.value = hue; if (hue != null && accHue != null) accHue.value = hue;
if (satOver != null && accSaturationOverride != null) if (satOver != null && accSaturationOverride != null) accSaturationOverride.checked = satOver === "true";
accSaturationOverride.checked = satOver === "true";
if (sat != null && accSaturation != null) accSaturation.value = sat; if (sat != null && accSaturation != null) accSaturation.value = sat;
if (base != null && accBase != null) accBase.checked = base === "true"; if (base != null && accBase != null) accBase.checked = base === "true";
@ -265,25 +223,26 @@ function changeWidth(width) {
function changeFont(font) { function changeFont(font) {
document.documentElement.style.setProperty("--font-family", font); document.documentElement.style.setProperty("--font-family", font);
document.documentElement.style.setProperty("--header-font-family", font);
} }
const baseColor = getComputedStyle(document.documentElement)
.getPropertyValue("--base-color")
.split(", "); const baseColor = getComputedStyle(document.documentElement).getPropertyValue("--base-color").split(", ");
function changeColor(hue, sat, hueOver, satOver) { function changeColor(hue, sat, hueOver, satOver) {
if (accHue != null) accHue.disabled = !hueOver; if (accHue != null) accHue.disabled = !hueOver;
if (accSaturation != null) accSaturation.disabled = !satOver; if (accSaturation != null) accSaturation.disabled = !satOver;
document.documentElement.style.setProperty( document.documentElement.style.setProperty("--base-color",
"--base-color", (hueOver ? hue : baseColor[0])
(hueOver ? hue : baseColor[0]) + ", " + (satOver ? sat + "%" : baseColor[1]) + ", "
+ (satOver ? (sat + "%") : baseColor[1])
); );
if (propagateStyles != null) if (propagateStyles != null) propagateStyles(getComputedStyle(document.documentElement));
propagateStyles(getComputedStyle(document.documentElement));
} }
function changeBase(base) { function changeBase(base) {
if (base) document.documentElement.classList.add("base"); if (base) document.documentElement.classList.add("base");
else document.documentElement.classList.remove("base"); else document.documentElement.classList.remove("base");

View file

@ -1,58 +0,0 @@
const data_get = document.getElementById("data_get");
function check_uptime(event, url) {
const outElement = event.target;
const startTime = performance.now();
let status, time, timer;
outElement.disabled = true;
fetch_ask(url, { mode: "no-cors", cache: "no-cache" })
.then((res) => {
const endTime = performance.now();
time = ((endTime - startTime) / 1000).toFixed(2);
outElement.innerHTML = `Check Status (${time}s)`;
})
.catch((err) => {
const endTime = performance.now();
time = ((endTime - startTime) / 1000).toFixed(2);
outElement.innerHTML = `Check Status (${time}s - Error)`;
})
.finally(() => {
window.cancelAnimationFrame(timer);
outElement.disabled = false;
});
function timeCounter() {
const curTime = performance.now();
time = (curTime - startTime) / 1000;
if (time >= 5)
outElement.innerHTML = `Check Status (${time.toFixed(
2
)}s - Taking longer than usual)`;
else outElement.innerHTML = `Check Status (${time.toFixed(2)}s - Waiting)`;
timer = window.requestAnimationFrame(timeCounter);
}
timer = window.requestAnimationFrame(timeCounter);
}
fetch_ask("https://blog.abtmtr.link/api/collections/paste/posts/domain")
.then((x) => x.json())
.then((paste) => {
const data = JSON.parse(paste.data.body);
data_get.innerHTML = data.reduce((html, subdomain) => {
const id = `${subdomain.name.replaceAll(".", "")}-uptime`;
return (
html +
`<h2><a href="${subdomain.url}" target="_blank">${
subdomain.name
}</a></h2>
${subdomain.description.map((x) => `<p>${x}</p>`).join("")}
<img src="${subdomain.src}" alt="${subdomain.name}" title="${
subdomain.name
}" />
<p>
<button
onclick="check_uptime(event, '${subdomain.url}')"
>Check Status</button>
<span id="${id}"></span></p>`
);
}, "");
});

View file

@ -0,0 +1,77 @@
const data_whoami = document.getElementById("data_whoami");
const time = document.getElementById("time");
const weekday = document.getElementById("weekday");
const timezone = document.getElementById("timezone");
const nofetchy = document.getElementById("no-fetchy");
const about = document.getElementById("about");
const statusus = document.getElementById("status");
const buttonflags = document.getElementById("buttonflags");
const fields = document.getElementById("fields");
const URL_REGEXP =
/(([a-z0-9.-]*)(((:\/\/)([a-z0-9.-]*))((\/)([\w;,\/?:@&=+$\-_.!~*'()#]*))?)|(((mailto|tel):)([\w;,\/?:@&=+$\-_.!~*'()#]*)))/g;
fetch("https://pronouns.cc/api/v1/users/MeowcaTheoRange")
.then((x) => x.json())
.then((user) => {
data_whoami.innerHTML = `
<p>I'm <a href="https://pronouns.cc/@MeowcaTheoRange" target="_blank"><b>${
user.names[0].value
}</b></a>, also better known online as <b>${
user.name
}</b>. <small>(${user.pronouns
.filter((pronoun) => pronoun.status == "okay")
.map((pronoun) => pronoun.pronouns.split("/")[0])
.join("/")})</small></p>`;
about.innerHTML = `<h2>Bio</h2><p>MeowcaTheoRange<br />${user.bio
.replace(URL_REGEXP, (m) => `<a href="${m}" target="_blank">${m}</a>`)
.replaceAll("\n", "<br />")}</p>`;
const curTime = new Date();
timezone.innerHTML = `(UTC${user.utc_offset > 0 ? "+" : "-"}${Math.abs(
user.utc_offset / (60 * 60)
)})`;
buttonflags.onclick = () => {
user.flags.forEach((flag) => {
const props = flag.description.split(";");
initDocument(
props[0].split(","),
props[1],
props[2],
props[3],
flag.name
);
});
};
buttonflags.disabled = false;
function updateTime() {
curTime.setTime(Date.now());
time.innerHTML = curTime.toLocaleTimeString("en-us", {
timeZone: "America/Chicago",
});
weekday.innerHTML = curTime.toLocaleString("en-us", {
weekday: "long",
timeZone: "America/Chicago",
});
window.requestAnimationFrame(updateTime);
}
window.requestAnimationFrame(updateTime);
nofetchy.style.display = null;
fields.innerHTML = user.fields
.map(
(fieldset) => `
<section>
<h2>${fieldset.name}</h2>
<ul>${fieldset.entries
.map((entry) => `<li>${entry.value}</li>`)
.join("")}</ul>
</section>`
)
.join("");
});
fetch("https://local.abtmtr.link/api/v1/accounts/stat/statuses")
.then((x) => x.json())
.then((statuses) => {
console.log(statuses);
statusus.innerHTML = `<h2><a href="${statuses[0].url}" target="_blank">Current status</a></h2>${statuses[0].content}<p></p>`;
});

View file

@ -1,141 +0,0 @@
const buttonroll = document.getElementById("buttonroll");
const buttons = [
{
href: "https://jaiden.sh/",
img: "/assets/88x31/jaiden_sh.png",
alt: "jaiden.sh",
},
{
href: "https://owenzimmerman.com/",
img: "/assets/88x31/owenzimmerman_com.png",
alt: "owenzimmerman.com",
},
{
href: "https://acidicalchemist.neocities.org/",
img: "/assets/88x31/acidicalchemist_neocities_org.gif",
alt: "acidicalchemist.neocities.org",
},
{
href: "https://arimelody.me/",
img: "/assets/88x31/arimelody_me.gif",
alt: "arimelody.me",
},
{
href: "https://freeplay.floof.company/",
img: "/assets/88x31/freeplay_floof_company.png",
alt: "freeplay.floof.company",
},
{
href: "https://invoxiplaygames.uk/",
img: "/assets/88x31/invoxiplaygames_uk.png",
alt: "invoxiplaygames.uk",
},
{
href: "https://ioletsgo.gay/",
img: "/assets/88x31/ioletsgo_gay.gif",
alt: "ioletsgo.gay",
},
{
href: "https://mae.wtf/",
img: "/assets/88x31/mae_wtf.png",
alt: "mae.wtf",
},
{
href: "https://micro.pages.gay/",
img: "/assets/88x31/micro_pages_gay.png",
alt: "micro.pages.gay",
},
{
href: "https://sneexy.pages.gay/",
img: "/assets/88x31/sneexy_pages_gay.gif",
alt: "sneexy.pages.gay",
},
{
href: "https://whois.slipfox.xyz/",
img: "/assets/88x31/whois_slipfox_xyz.png",
alt: "whois.slipfox.xyz",
},
{
href: "https://moth.monster/",
img: "/assets/88x31/moth_monster.png",
alt: "moth.monster",
},
{
href: "https://translunar.academy/",
img: "/assets/88x31/translunar_academy.png",
alt: "translunar.academy",
},
{
href: "https://ultramarine-linux.org/",
img: "/assets/88x31/esoteric/gnu-linux.gif",
alt: "Made on GNU/Linux",
},
{
href: "https://abtmtr.link/projects/item/normalize",
img: "/assets/88x31/esoteric/html.gif",
alt: "<HTML> - Learn it today!",
},
{
href: "https://vivaldi.com",
img: "/assets/88x31/esoteric/vivaldi.gif",
alt: "I use Vivaldi",
},
{
href: "https://ublockorigin.com",
img: "/assets/88x31/esoteric/ublock.png",
alt: "uBlock Origin Now!",
},
{
href: "https://channelstore.roku.com/details/7da3fa0c2209746730df8a4e21e83b02",
img: "/assets/88x31/esoteric/xkcd.gif",
alt: "xkcd",
},
];
function initializeButtons() {
buttonroll.innerHTML += buttons.reduce(
(pv, cv) =>
pv +
`<a href="${cv.href}" target="_blank"><img alt="${cv.alt}" title="${cv.alt}" src="${cv.img}"></a>`,
""
);
}
buttonroll.hidden = false;
overflowButtons();
initializeButtons();
overflowButtons();
overflowButtons();
function overflowButtons() {
buttonroll.innerHTML += buttons.reduce(
(pv, cv) =>
pv +
`<a href="${cv.href}" target="_blank" class="overflow"><img alt="${cv.alt}" title="${cv.alt}" src="${cv.img}"></a>`,
""
);
}
let isHovering = false;
buttonroll.addEventListener("mouseenter", () => (isHovering = true));
buttonroll.addEventListener("mouseleave", () => (isHovering = false));
let curAnim;
let endTime = performance.now();
let unit = 88;
let gap = 8;
let scrollX = (unit + gap) * buttons.length * 2;
let speed = 25;
let max = (unit + gap) * buttons.length;
function scrollButtons(startTime) {
let deltaTime = (startTime - endTime) / 1000;
if (!isHovering) {
scrollX += speed * deltaTime;
} else scrollX = buttonroll.scrollLeft;
buttonroll.scrollLeft = (scrollX % max) + max;
endTime = startTime;
curAnim = window.requestAnimationFrame(scrollButtons);
}
curAnim = window.requestAnimationFrame(scrollButtons);

54
views/site/index.html Normal file
View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Site Information - abtmtr.link</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/style.css" />
<style>
:root {
--base-color: 125, 100%;
}
</style>
</head>
<body>
<header>
<section>
<h1>Site Information</h1>
</section>
<$ nav.html $>
<section id="accessibility" hidden></section>
</header>
<main>
<section>
<h2>What happened to the previous site?</h2>
<p>
The previous site kinda sucked. It depended on client-side React for
the most basic of things, and was just overall kind of a mess of TSX
everywhere.
</p>
<p>
This site is represented semi-statically, which means there's nothing
new being generated on the backend.<br />
This site does not use React either, so everything the frontend runs
is vanilla JavaScript.
</p>
<h2>
What's with the <b>accessibility controls</b> on every page of the
site?
</h2>
<p>
Just because. If you want to make the text bigger or get rid of the
colors, you have that panel.
</p>
<h2>What font are you using for this site?</h2>
<p>
<b>Lexend Deca</b>, or your <b>system's default sans-serif font</b>,
depending on if you're using Base style.
</p>
</section>
</main>
<script src="/scripts/accessibility.js"></script>
</body>
</html>

View file

@ -1,34 +0,0 @@
#buttonroll {
display: flex;
align-items: center;
overflow-x: scroll;
overflow-y: hidden;
gap: 8px;
max-width: 50em;
margin: auto;
padding: 8px 0;
}
#buttonroll a {
display: inline-block;
width: 88px;
height: 31px;
}
#buttonroll img {
display: inline-block;
width: 88px;
height: 31px;
max-width: none;
}
/* #buttonroll .overflow {
opacity: 0.5;
} */
#buttonroll::-webkit-scrollbar {
display: none;
}
#buttonroll {
-ms-overflow-style: none;
scrollbar-width: none;
}

View file

@ -1,53 +0,0 @@
body {
background-color: black;
margin: 0;
padding: 0;
box-sizing: border-box;
overflow: hidden;
width: 100vw;
height: 100vh;
}
div.screen {
width: 800px;
height: 600px;
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
box-shadow: 0 0 64px 4px #fff4;
border-radius: 8px;
overflow: hidden;
}
div.screen iframe {
display: inline-block;
width: 100%;
height: 100%;
border: none;
margin: 0;
padding: 0;
font-size: 16px;
}
div.screen div.crt {
display: inline-block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: repeating-linear-gradient(
0deg,
#0006 0px,
#0006 1px,
#0000 1px,
#0000 2px
);
background-repeat: repeat;
background-position: 0 0;
background-size: 2px;
user-select: none;
pointer-events: none;
z-index: 9000;
}

View file

@ -1,86 +0,0 @@
@font-face {
font-family: "Lexend Deca";
src: url("/assets/fonts/Lexend Deca/Variable.ttf");
}
@font-face {
font-family: "Minecraft";
src: url("/assets/fonts/Minecraft/Regular.otf");
}
@font-face {
font-family: "Renogare";
src: url("/assets/fonts/Renogare/Regular.otf");
}
@font-face {
font-family: "NDS12";
src: url("/assets/fonts/NDS12/Regular.otf");
}
@font-face {
font-family: "OpenDyslexic";
src: url("/assets/fonts/OpenDyslexic/Regular.otf");
}
:root {
--header-font-family: "Renogare";
--header-font-weight: 400;
--font-family: "Lexend Deca";
--border-width: calc(2rem / 16);
--base-color: 325, 50%;
--border-radius: calc(4rem / 16);
}
#accessibility {
/* position: sticky;
bottom: 1em;
left: 1em; */
padding: 0.5em;
}
#accessibility fieldset {
margin-block: 0;
}
/* * {
font-smooth: never;
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: grayscale;
} */
.nav {
max-width: 38em;
margin: auto;
}
.nav p {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 1em;
}
.nav p * {
text-align: center;
}
html.base .nav {
margin: 0;
}
html.base .nav p * {
text-align: start;
}
h1 {
border-bottom: var(--border-width) var(--border-style) var(--border-color);
padding-bottom: 0em;
}
h2 {
border-bottom: var(--border-width) var(--border-style) var(--border-color);
padding-bottom: 0.25em;
}
h1 a,
h2 a {
text-decoration: none !important;
}
.rs {
float: inline-end;
}

View file

@ -1,18 +1,63 @@
@import url("./myapp.css"); @font-face {
font-family: "Lexend Deca";
html:not(.base) { src: url("/assets/fonts/Lexend Deca/Variable.ttf");
background: url("/assets/bg.jpg"); }
background-position: center; @font-face {
background-size: cover; font-family: "Minecraft";
background-attachment: fixed; src: url("/assets/fonts/Minecraft/Regular.otf");
}
@font-face {
font-family: "Renogare";
src: url("/assets/fonts/Renogare/Regular.otf");
}
@font-face {
font-family: "NDS12";
src: url("/assets/fonts/NDS12/Regular.otf");
}
@font-face {
font-family: "OpenDyslexic";
src: url("/assets/fonts/OpenDyslexic/Regular.otf");
} }
:root { :root {
--background-color: hsla(var(--base-color), 15%, 0.9); --font-family: "Lexend Deca";
} }
@media (prefers-color-scheme: light) { #accessibility {
:root { /* position: sticky;
--background-color: hsla(var(--base-color), 80%, 0.9); bottom: 1em;
} left: 1em; */
padding: 0.5em;
}
#accessibility fieldset {
margin-block: 0;
}
/* * {
font-smooth: never;
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: grayscale;
} */
.nav {
max-width: 38em;
margin: auto;
}
.nav p {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 1em;
}
.nav p a {
text-align: center;
}
html.base .nav {
margin: 0;
}
html.base .nav p a {
text-align: start;
} }

View file

@ -27,7 +27,6 @@ div.window-object {
color: var(--accent-color-fg); color: var(--accent-color-fg);
--border-color: var(--color); --border-color: var(--color);
border: var(--border-width) var(--border-style) var(--border-color); border: var(--border-width) var(--border-style) var(--border-color);
border-radius: var(--border-radius);
resize: both; resize: both;
overflow: hidden; overflow: hidden;
padding: 0.25em; padding: 0.25em;
@ -48,7 +47,6 @@ html.base div.window-object {
background-color: var(--accent-color); background-color: var(--accent-color);
color: var(--accent-color-fg); color: var(--accent-color-fg);
border: var(--border-width) var(--border-style) var(--border-color); border: var(--border-width) var(--border-style) var(--border-color);
border-radius: var(--border-radius);
} }
@media (forced-colors: active) { @media (forced-colors: active) {
@ -110,7 +108,6 @@ div.window-object {
background-color: var(--accent-color); background-color: var(--accent-color);
color: var(--accent-color-fg); color: var(--accent-color-fg);
border: var(--border-width) var(--border-style) var(--border-color); border: var(--border-width) var(--border-style) var(--border-color);
border-radius: var(--border-radius);
} }
} }
@ -125,7 +122,6 @@ div.window-object {
background-color: var(--accent-color); background-color: var(--accent-color);
color: var(--accent-color-fg); color: var(--accent-color-fg);
border: var(--border-width) var(--border-style) var(--border-color); border: var(--border-width) var(--border-style) var(--border-color);
border-radius: var(--border-radius);
} }
} }
@ -221,7 +217,6 @@ div.window-object > div.window-manager > div > button {
margin-inline-start: 0.25em; margin-inline-start: 0.25em;
box-sizing: border-box; box-sizing: border-box;
border: var(--border-width) var(--border-style) var(--border-color); border: var(--border-width) var(--border-style) var(--border-color);
border-radius: var(--border-radius);
background-color: transparent; background-color: transparent;
color: currentColor; color: currentColor;
font-family: "Material Symbols Outlined"; font-family: "Material Symbols Outlined";
@ -236,7 +231,6 @@ div.window-object > iframe.window-content {
height: 100%; height: 100%;
width: 100%; width: 100%;
border: var(--border-width) var(--border-style) var(--border-color); border: var(--border-width) var(--border-style) var(--border-color);
border-radius: var(--border-radius);
min-height: 0; min-height: 0;
min-width: 0; min-width: 0;
} }

View file

@ -1,201 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>abtmtr.link</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/normal.css" />
<link rel="stylesheet" href="/styles/myapp.css" />
<style>
:root {
--base-color: 128, 50%;
}
</style>
</head>
<body>
<header>
<h1>TIGERRS Minecraft Server</h1>
<p>
Information regarding the Minecraft server I'm running for the TIGERRS
community.
</p>
<$ nav.html $>
<section id="accessibility" hidden></section>
</header>
<main>
<section>
<h1>Rules</h1>
<p>These are the rules of the server.</p>
<h2>Good Faith</h2>
<p>
Assume that all players are working in good faith. If someone does
something that seems ignorant or rude to you, speak up gently, not
harshly.
</p>
<h2>Sensitive Topics</h2>
<p>
When discussing sensitive topics, you can use spoiler syntax around
your text like this:
</p>
<pre>&lt;spoiler&gt;Test&lt;/spoiler&gt;</pre>
<p>
You can also use this shorter syntax to spoiler everything after the
tag:
</p>
<pre>
This will not be spoilered but &lt;spoiler&gt;all of this will be spoilered</pre
>
<p>In order to show the spoilers, hover over the text.</p>
<h2>Language</h2>
<p>
Please do not use any slurs, reclaimed or not, as we would like to keep spirits high.
</p>
<p>Just be silly :3</p>
</section>
<section>
<h1>Open Hours</h1>
<p><i>All times are in Central Time.</i></p>
<p><b>Sunday:</b> 9:00 AM10:00 PM</p>
<!-- 0 -->
<p><b>Monday:</b> 3:00 PM10:00 PM</p>
<!-- 1 -->
<p><b>Tuesday:</b> 3:00 PM12:00 AM (next day)</p>
<!-- 2 -->
<p><b>Wednesday:</b> 9:00 AM10:00 PM</p>
<!-- 3 -->
<p><b>Thursday:</b> 3:00 PM10:00 PM</p>
<!-- 4 -->
<p><b>Friday:</b> 3:00 PM12:00 AM (next day)</p>
<!-- 5 -->
<p><b>Saturday:</b> 9:00 AM12:00 AM (next day)</p>
<!-- 6 -->
</section>
<section>
<h1>Recommended Setup</h1>
<p>
While the server supports all Minecraft versions since <b>1.19.4</b>,
it is recommended that you use version 1.19.4 specifically,
<a href="https://fabricmc.net/use/installer/" target="_blank"
>especially with the Fabric mod loader</a
>.
</p>
<p>
If you're using Fabric, it's also recommended that you install these
mods:
</p>
<ul>
<li>
<a
href="https://modrinth.com/mod/clientsidenoteblocks"
target="_blank"
>Client Side Noteblocks</a
>
</li>
<li>
<a
href="https://modrinth.com/plugin/invisible-frames"
target="_blank"
>Invisible Frames</a
>
</li>
<li>
<a href="https://modrinth.com/mod/krypton" target="_blank"
>Krypton</a
>
</li>
<li>
<a href="https://modrinth.com/mod/lambdynamiclights" target="_blank"
>LambDynamicLights</a
>
</li>
<li>
<a href="https://modrinth.com/mod/serverpingerfixer" target="_blank"
>Server Pinger Fixer</a
>
</li>
<li>
<a
href="https://modrinth.com/mod/show-me-what-you-got"
target="_blank"
>Show Me What You Got</a
>
</li>
<li>
<a
href="https://modrinth.com/plugin/simple-voice-chat"
target="_blank"
>Simple Voice Chat</a
>
</li>
<li>
<a href="https://modrinth.com/mod/zume" target="_blank">Zume</a>
</li>
</ul>
<p>
Even if you don't have any mods installed, these modded actions will
still work:
</p>
<ul>
<li><i>EditSign</i> sign editing (especially if you're on 1.20+)</li>
<li><i>VeinDigging</i> vein digging</li>
<li><i>FallingTree</i> tree cutting</li>
<li><i>TabTPS</i> server performance in player menu</li>
<li><i>Couplings</i> opening double-doors simultaneously</li>
<li>
<i>Show Me What You Got</i> item chat (you cannot chat items,
however)
</li>
<li>
<i>Invisible Frames</i> item frames (you cannot make item frames
invisible, however)
</li>
<li><i>Flan</i> region claiming</li>
<li><i>WorldEdit</i> wand usage</li>
<li><i>SleepWarp</i> night skipping</li>
<li><i>StyledChat</i> chat formatting</li>
<li><i>Healthcare</i> health indicators</li>
</ul>
</section>
<section>
<h1>Server Address</h1>
<p>
The server can be found at
<b onclick="window.getSelection().selectAllChildren(this);"
>tigerrs.abtmtr.link</b
>
for Minecraft: Java Edition versions <b>1.19.4+</b>.
</p>
<p>
For Minecraft (Bedrock Edition), you should use version
<b>1.20.51</b> (latest).
</p>
<p>In order to join the server, you have to be whitelisted.</p>
<h2>Web map</h2>
<p>
A web map of the world can be found at
<a href="https://tigerrs.abtmtr.link" target="_blank"
>the same domain, on HTTPS</a
>.
</p>
<p>
If you can't join, you can at least peer inside and see what's
happening.
</p>
</section>
<section>
<h1>Is this official?</h1>
<p>
Keep in mind this server is <b>completely unofficial</b>, and is
running unaffiliated to the TIGERRS collective.
</p>
<p>
This means that none of the activities on the server are officially
moderated - instead, server rules and regulations are up to the server
admins.
</p>
</section>
</main>
<script src="/scripts/accessibility.js"></script>
<script src="./scripts/data_check_uptime.js"></script>
</body>
</html>

View file

@ -1,13 +0,0 @@
fetch_ask("https://pronouns.cc/api/v1/users/clhd8desmggedm3q1t3g")
.then((x) => x.json())
.then((data) => {
data_get.innerHTML = data.members.reduce((html, member) => (
html +
`<h2><a href="https://pronouns.cc/@${data.name}/${member.name}" target="_blank">${member.display_name}</a> <small>${member.pronouns.map(x => x.pronouns.split("/")[0]).join("/")}</small></h2>
<fieldset>
${member.names.map(x => `<p><b>${x.value}</b> (${x.status})</p>`).join("")}
</fieldset>
<p>${member.bio.replaceAll("\n", "<br />")}</p>
<p>Signature: <b>${member.name}</b></p>`
), "");
});