262 lines
6.5 KiB
PHP
262 lines
6.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
$styleNonce = base64_encode(random_bytes(16));
|
|
|
|
header("Content-Type: text/html; charset=UTF-8");
|
|
header("X-Content-Type-Options: nosniff");
|
|
header("Referrer-Policy: strict-origin-when-cross-origin");
|
|
header("Permissions-Policy: geolocation=(), microphone=(), camera=()");
|
|
header(
|
|
"Content-Security-Policy: default-src 'none'; style-src 'self' 'nonce-{$styleNonce}'; base-uri 'none'; form-action 'none'; frame-ancestors *",
|
|
);
|
|
if (function_exists("header_remove")) {
|
|
header_remove("X-Frame-Options");
|
|
}
|
|
|
|
function normalizeHost(string $value): ?string
|
|
{
|
|
$value = trim(strtolower($value));
|
|
|
|
if ($value === "") {
|
|
return null;
|
|
}
|
|
|
|
if (preg_match("#^https?://#", $value) === 1) {
|
|
$parsedHost = parse_url($value, PHP_URL_HOST);
|
|
if (!is_string($parsedHost) || $parsedHost === "") {
|
|
return null;
|
|
}
|
|
$value = strtolower($parsedHost);
|
|
}
|
|
|
|
$value = explode("/", $value, 2)[0];
|
|
|
|
if (
|
|
preg_match(
|
|
"/\A(?=.{1,253}\z)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}\z/",
|
|
$value,
|
|
) !== 1
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
$rawSites = file(
|
|
__DIR__ . "/sites.txt",
|
|
FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES,
|
|
);
|
|
if ($rawSites === false) {
|
|
http_response_code(500);
|
|
echo ";_;";
|
|
exit();
|
|
}
|
|
|
|
$sites = [];
|
|
foreach ($rawSites as $rawSite) {
|
|
$normalized = normalizeHost($rawSite);
|
|
if ($normalized !== null) {
|
|
$sites[] = $normalized;
|
|
}
|
|
}
|
|
$sites = array_values(array_unique($sites));
|
|
|
|
if (count($sites) < 2) {
|
|
http_response_code(500);
|
|
echo "did't find at least two sites to ring among";
|
|
exit();
|
|
}
|
|
|
|
$current = normalizeHost((string) ($_GET["site"] ?? ""));
|
|
if ($current === null) {
|
|
http_response_code(400);
|
|
echo "genuinely what are we doing here";
|
|
exit();
|
|
}
|
|
|
|
$index = array_search($current, $sites, true);
|
|
if ($index === false) {
|
|
http_response_code(404);
|
|
echo "This site isn't actually part of the ringularity ;-;";
|
|
exit();
|
|
}
|
|
|
|
$total = count($sites);
|
|
$prev = $sites[($index - 1 + $total) % $total];
|
|
$next = $sites[($index + 1) % $total];
|
|
?>
|
|
<div id="ring" role="navigation" aria-label="Webring navigation">
|
|
<a href="https://<?= htmlspecialchars(
|
|
$prev,
|
|
ENT_QUOTES | ENT_SUBSTITUTE,
|
|
"UTF-8",
|
|
) ?>" target="_top" rel="noopener noreferrer">← PREV</a>
|
|
<span aria-hidden="true" class="specialtxt">The Ringularity</span>
|
|
<a href="https://<?= htmlspecialchars(
|
|
$next,
|
|
ENT_QUOTES | ENT_SUBSTITUTE,
|
|
"UTF-8",
|
|
) ?>" target="_top" rel="noopener noreferrer">NEXT →</a>
|
|
</div>
|
|
|
|
<style nonce="<?= htmlspecialchars(
|
|
$styleNonce,
|
|
ENT_QUOTES | ENT_SUBSTITUTE,
|
|
"UTF-8",
|
|
) ?>">
|
|
:root {
|
|
font-family: 'IBM Plex Sans', sans-serif;
|
|
--bg-1: #1d2021;
|
|
--bg0: #282828;
|
|
--bg1: #3c3836;
|
|
--bg2: #504945;
|
|
--bg3: #665c54;
|
|
--bg4: #7c6f64;
|
|
|
|
--gray0: #928374;
|
|
--gray1: #a89984;
|
|
|
|
--fg0: #fbf1c7;
|
|
--fg1: #ebdbb2;
|
|
--fg2: #d5c4a1;
|
|
--fg3: #bdae93;
|
|
|
|
--red0: #cc241d;
|
|
--red1: #fb4934;
|
|
|
|
--green0: #98971a;
|
|
--green1: #b8bb26;
|
|
|
|
--yellow0: #d79921;
|
|
--yellow1: #fabd2f;
|
|
|
|
--blue0: #458588;
|
|
--blue1: #83a598;
|
|
|
|
--purple0: #b16286;
|
|
--purple1: #d3869b;
|
|
|
|
--aqua0: #689d6a;
|
|
--aqua1: #8ec07c;
|
|
|
|
--orange0: #d65d0e;
|
|
--orange1: #fe8019;
|
|
}
|
|
|
|
a {
|
|
color: var(--blue1);
|
|
}
|
|
|
|
a:visited {
|
|
color: var(--purple1);
|
|
}
|
|
|
|
p {
|
|
text-align: justify;
|
|
hyphens: auto;
|
|
word-break: break-word;
|
|
}
|
|
|
|
#ring {
|
|
border-radius: var(--brad);
|
|
background-color: var(--bg2);
|
|
color: var(--blue1);
|
|
transition: scale 0.2s linear;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
flex-wrap: nowrap;
|
|
height: 1em;
|
|
padding: 1em;
|
|
align-items: center;
|
|
}
|
|
|
|
#ring:hover a {
|
|
transition: transform 1.3s ease-in-out;
|
|
transform: scaleX(4) scaleY(1.5);
|
|
animation: wheee 0.2s linear infinite;
|
|
animation-delay: 1.3s;
|
|
}
|
|
|
|
#ring:hover .specialtxt {
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
position: relative;
|
|
animation: bob 1.8s infinite ease-in-out;
|
|
background: linear-gradient(
|
|
90deg,
|
|
var(--red1),
|
|
var(--orange1),
|
|
var(--yellow1)
|
|
);
|
|
-webkit-background-clip: text;
|
|
color: transparent;
|
|
}
|
|
|
|
#ring:hover .specialtxt::before,
|
|
#ring:hover .specialtxt::after {
|
|
content: "The Ringularity";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(
|
|
90deg,
|
|
var(--red0),
|
|
var(--orange0),
|
|
var(--yellow0)
|
|
);
|
|
-webkit-background-clip: text;
|
|
color: transparent;
|
|
z-index: -1;
|
|
transform: scaleY(1);
|
|
opacity: 0.5;
|
|
animation: smear 1.8s infinite ease-in-out;
|
|
}
|
|
|
|
#ring:hover .specialtxt::after {
|
|
filter: blur(8px);
|
|
opacity: 0.8;
|
|
}
|
|
|
|
@keyframes bob {
|
|
0%,
|
|
100% {
|
|
transform: translateY(8px) scaleX(1.2) scaleY(0.8);
|
|
}
|
|
50% {
|
|
transform: translateY(-8px) scaleX(0.8) scaleY(1.2);
|
|
}
|
|
}
|
|
|
|
@keyframes smear {
|
|
0%,
|
|
100%,
|
|
50% {
|
|
transform: scaleY(1) translateY(0px);
|
|
opacity: 0.5;
|
|
}
|
|
25% {
|
|
transform: scaleY(1.5) translateY(2px);
|
|
opacity: 0.7;
|
|
}
|
|
75% {
|
|
transform: scaleY(1.5) translateY(-2px);
|
|
opacity: 0.7;
|
|
}
|
|
}
|
|
|
|
@keyframes wheee {
|
|
from {
|
|
transform: rotate(0deg) scaleX(4) scaleY(1.5);
|
|
}
|
|
to {
|
|
transform: rotate(360deg) scaleX(4) scaleY(1.5);
|
|
}
|
|
}
|
|
</style>
|