Added assets.
51
web/assets/css/about.css
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
.service-up::after { content: '●'; }
|
||||||
|
|
||||||
|
.service-down::after { content: '○'; }
|
||||||
|
|
||||||
|
h3:is(.service-up, .service-down)::after {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-right: -.9rem;
|
||||||
|
vertical-align: text-top;
|
||||||
|
padding-left: .2rem; }
|
||||||
|
|
||||||
|
.columns {
|
||||||
|
column-count: 3;
|
||||||
|
column-fill: balance-all;
|
||||||
|
column-gap: 1rem;
|
||||||
|
column-span: none; }
|
||||||
|
|
||||||
|
.columns.figs { column-count: 2; }
|
||||||
|
|
||||||
|
.columns > div {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%; }
|
||||||
|
|
||||||
|
.columns h3 { color: var(--primary-color); }
|
||||||
|
|
||||||
|
figure figcaption { font-size: .8rem; }
|
||||||
|
|
||||||
|
figure.center,
|
||||||
|
figure figcaption { text-align: center; }
|
||||||
|
|
||||||
|
figure img { width: 100%; }
|
||||||
|
|
||||||
|
figure.center img { width: 60%; }
|
||||||
|
|
||||||
|
#contacts,
|
||||||
|
#donation {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
word-wrap: break-word; }
|
||||||
|
|
||||||
|
:is(#contacts, #donation) :is(a, span) { margin: .3rem; }
|
||||||
|
|
||||||
|
#contacts span a { margin: 0; }
|
||||||
|
|
||||||
|
.banners {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center; }
|
||||||
|
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
.columns,
|
||||||
|
.columns.figs { column-count: 1; } }
|
64
web/assets/css/articles.css
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Share Tech Mono';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('ShareTechMono'), local('ShareTechMono-Regular'),
|
||||||
|
url(/assets/fonts/ShareTechMono-Regular.ttf) format('truetype'); }
|
||||||
|
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-variant: normal;
|
||||||
|
text-align: left; }
|
||||||
|
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
text-indent: 1.5rem;
|
||||||
|
margin: 1rem 0 1rem 0; }
|
||||||
|
|
||||||
|
:is(h3, h4, h5) a { color: var(--text-color); }
|
||||||
|
|
||||||
|
:is(h3, h4, h5) a:hover { color: var(--primary-color); }
|
||||||
|
|
||||||
|
code,
|
||||||
|
pre {
|
||||||
|
font-family: 'Share Tech Mono';
|
||||||
|
font-size: 1.1rem;
|
||||||
|
letter-spacing: -1px; }
|
||||||
|
|
||||||
|
pre {
|
||||||
|
border-left: 1px solid var(--primary-color);
|
||||||
|
padding-left: .25rem;
|
||||||
|
margin: .5rem 0;
|
||||||
|
overflow: scroll;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all; }
|
||||||
|
|
||||||
|
figure { margin: 1rem 0; }
|
||||||
|
|
||||||
|
figure.center { text-align: center; }
|
||||||
|
|
||||||
|
article {
|
||||||
|
margin-top: 1rem;
|
||||||
|
max-width: 100%; }
|
||||||
|
|
||||||
|
article:last-child { margin-bottom: 1rem; }
|
||||||
|
|
||||||
|
article header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column; }
|
||||||
|
|
||||||
|
article header .menu {
|
||||||
|
display: flex;
|
||||||
|
font-size: .8rem;
|
||||||
|
justify-content: space-between; }
|
||||||
|
|
||||||
|
article header nav a { font-variant: normal; }
|
||||||
|
|
||||||
|
article header nav ol {
|
||||||
|
counter-reset: item;
|
||||||
|
list-style-type: none; }
|
||||||
|
|
||||||
|
article header nav ol > li { counter-increment: item; }
|
||||||
|
|
||||||
|
article header nav ol > li:before { content: counters(item, '.') '. '; }
|
90
web/assets/css/guestbook.css
Executable file
@ -0,0 +1,90 @@
|
|||||||
|
::placeholder { color: var(--primary-color); }
|
||||||
|
|
||||||
|
small { font-size: .8rem; }
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: grid;
|
||||||
|
gap: .5rem;
|
||||||
|
grid-template-areas:
|
||||||
|
"n w w"
|
||||||
|
"m m m"
|
||||||
|
"c a s";
|
||||||
|
grid-template-columns: 1fr 1fr 1fr; }
|
||||||
|
|
||||||
|
:is(input, textarea):focus {
|
||||||
|
outline-color: var(--primary-color);
|
||||||
|
outline-style: solid;
|
||||||
|
outline-width: 1px; }
|
||||||
|
|
||||||
|
input[name="name"] { grid-area: n; }
|
||||||
|
|
||||||
|
input[name="website"] { grid-area: w; }
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
grid-area: m;
|
||||||
|
height: 5.5rem;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 100%; }
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
textarea {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid var(--primary-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font: inherit; }
|
||||||
|
|
||||||
|
.checkboxes { grid-area: c; }
|
||||||
|
|
||||||
|
label { padding-left: .5rem; }
|
||||||
|
|
||||||
|
button.refresh:disabled {
|
||||||
|
color: gray;
|
||||||
|
cursor: progress; }
|
||||||
|
|
||||||
|
.captcha {
|
||||||
|
align-items: center;
|
||||||
|
column-gap: .5rem;
|
||||||
|
display: flex;
|
||||||
|
grid-area: a;
|
||||||
|
justify-content: space-evenly; }
|
||||||
|
|
||||||
|
.captcha img {
|
||||||
|
height: 40px;
|
||||||
|
width: 160px; }
|
||||||
|
|
||||||
|
.captcha input[type="text"] {
|
||||||
|
height: 27px;
|
||||||
|
text-align: center;
|
||||||
|
width: 4.5rem; }
|
||||||
|
|
||||||
|
input[type="submit"] {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border: none;
|
||||||
|
color: #f5f5f5;
|
||||||
|
font: inherit;
|
||||||
|
grid-area: s;
|
||||||
|
height: 2rem; }
|
||||||
|
|
||||||
|
input[type="submit"]:hover {
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
cursor: pointer; }
|
||||||
|
|
||||||
|
article:not(:last-child) { margin-bottom: 1rem; }
|
||||||
|
|
||||||
|
article header {
|
||||||
|
display: inline;
|
||||||
|
font-size: .85rem; }
|
||||||
|
|
||||||
|
article > *,
|
||||||
|
article div.reply > * { margin-left: .5rem; }
|
||||||
|
|
||||||
|
article p.quote { font-style: italic; }
|
||||||
|
|
||||||
|
#pagination a:not(:first-child) { margin-left: .5rem; }
|
||||||
|
|
||||||
|
@media screen and (max-width: 641px) {
|
||||||
|
form {
|
||||||
|
gap: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column; } }
|
22
web/assets/css/index.css
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
body {
|
||||||
|
height: 75vh;
|
||||||
|
margin-top: 25vh; }
|
||||||
|
|
||||||
|
header { position: relative; }
|
||||||
|
|
||||||
|
#logo { width: 100%; }
|
||||||
|
|
||||||
|
nav,
|
||||||
|
#services {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%; }
|
||||||
|
|
||||||
|
nav a:last-child { margin-left: .6rem; }
|
||||||
|
|
||||||
|
|
||||||
|
#services :is(a, span) { margin: .3rem; }
|
||||||
|
|
||||||
|
#services span a { margin: 0; }
|
||||||
|
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
#services { display: flex; flex-direction: column; } }
|
156
web/assets/css/main.css
Executable file
@ -0,0 +1,156 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto Condensed';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('RobotoCondensed'), local('RobotoCondensed-Regular'),
|
||||||
|
url(/assets/fonts/RobotoCondensed-Regular.ttf); }
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background-color: #0a0a0a;
|
||||||
|
--background-image: url('/assets/img/alpha1918_z.webp');
|
||||||
|
--primary-color: #cd2682;
|
||||||
|
--secondary-color: #9f2b68;
|
||||||
|
--text-color: #f5f5f5;
|
||||||
|
--text-indent: 1.6rem;
|
||||||
|
scrollbar-color: var(--primary-color) var(--background-color); }
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--background-color: #f5f5f5;
|
||||||
|
--primary-color: #9f2b68;
|
||||||
|
--secondary-color: #cd2682;
|
||||||
|
--text-color: #0a0a0a; } }
|
||||||
|
|
||||||
|
* { margin: 0; }
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
color: var(--background-color); }
|
||||||
|
|
||||||
|
a,
|
||||||
|
button {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
word-wrap: break-word; }
|
||||||
|
|
||||||
|
:is(a, button):hover {
|
||||||
|
color: var(--secondary-color);
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: .5s; }
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font: inherit;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-align: justify;
|
||||||
|
line-height: var(--text-indent);
|
||||||
|
text-indent: var(--text-indent); }
|
||||||
|
|
||||||
|
p:not(:last-child) { margin-bottom: .1rem; }
|
||||||
|
|
||||||
|
p.center {
|
||||||
|
text-align: center;
|
||||||
|
text-indent: 0; }
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
margin: 0; }
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 { font-variant: small-caps; }
|
||||||
|
|
||||||
|
h2 { font-size: 1.4rem; }
|
||||||
|
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 1rem; }
|
||||||
|
|
||||||
|
h3 { font-size: 1.05rem; }
|
||||||
|
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: .5rem; }
|
||||||
|
|
||||||
|
tr :is(th, td) {
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top; }
|
||||||
|
|
||||||
|
tr :is(th, td):not(:last-child) { padding-right: .5rem; }
|
||||||
|
|
||||||
|
.highlighted { color: var(--primary-color); }
|
||||||
|
|
||||||
|
html {
|
||||||
|
margin-left: calc(100vw - 100%);
|
||||||
|
margin-right: 0; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: 'Roboto Condensed', Roboto, sans-serif;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 960px;
|
||||||
|
width: 98%; }
|
||||||
|
|
||||||
|
@media screen and (min-width: 1280px) {
|
||||||
|
body {
|
||||||
|
background-image: var(--background-image);
|
||||||
|
background-size: min(calc((100vw - 960px)/2), 25vw);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: bottom left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between; }
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
display: block;
|
||||||
|
width: 360px; }
|
||||||
|
|
||||||
|
#logo text { fill: var(--text-color); }
|
||||||
|
|
||||||
|
#logo .logo {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-variant-caps: small-caps;
|
||||||
|
font-weight: bold; }
|
||||||
|
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
|
#logo .logo { font-size: 2.082rem; } }
|
||||||
|
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
#logo .logo { font-size: 2rem; } }
|
||||||
|
|
||||||
|
#logo .under { font-size: .88rem; }
|
||||||
|
|
||||||
|
nav { margin-top: .5rem; }
|
||||||
|
|
||||||
|
nav a { font-variant: small-caps; }
|
||||||
|
|
||||||
|
nav a:not(:first-child) { margin-left: .6rem; }
|
||||||
|
|
||||||
|
section {
|
||||||
|
margin-top: 1rem;
|
||||||
|
max-width: 100%; }
|
||||||
|
|
||||||
|
footer {
|
||||||
|
font-size: .8rem;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem 0; }
|
||||||
|
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
body > header { display: block; }
|
||||||
|
|
||||||
|
#logo { width: 100%; }
|
||||||
|
|
||||||
|
body > header nav { text-align: center; } }
|
9
web/assets/css/mindflow.css
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
.hidden { display: none; }
|
||||||
|
|
||||||
|
button:not(:last-child) { padding-right: 1rem; }
|
||||||
|
|
||||||
|
article header a { color: var(--text-color); }
|
||||||
|
|
||||||
|
article footer {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0; }
|
BIN
web/assets/fonts/ShareTechMono-Regular.ttf
Executable file
BIN
web/assets/img/acer.webp
Executable file
After Width: | Height: | Size: 478 KiB |
BIN
web/assets/img/acer_thumb.webp
Executable file
After Width: | Height: | Size: 65 KiB |
BIN
web/assets/img/alpha1918_z.webp
Executable file
After Width: | Height: | Size: 101 KiB |
BIN
web/assets/img/articles/mikrotik_please_just_dont.jpg
Executable file
After Width: | Height: | Size: 29 KiB |
BIN
web/assets/img/banner_240x60.gif
Executable file
After Width: | Height: | Size: 13 KiB |
BIN
web/assets/img/banner_88x31.gif
Executable file
After Width: | Height: | Size: 4.0 KiB |
BIN
web/assets/img/banner_dark_240x60.gif
Executable file
After Width: | Height: | Size: 13 KiB |
BIN
web/assets/img/banner_dark_88x31.gif
Executable file
After Width: | Height: | Size: 4.5 KiB |
BIN
web/assets/img/favicon.ico
Executable file
After Width: | Height: | Size: 66 KiB |
2
web/assets/img/favicon.svg
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg version="1.1" viewBox="0 0 574.17 258.67" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g fill="#cd2682" aria-label="A'sD"><path d="m131.67 206.33h-66.833l-13 49.667h-51.833l75.833-242.67h44.833l76.333 242.67h-52.333zm-56.167-40.833h45.333l-22.667-86.5z" style="font-variant-caps:small-caps"/><path d="m226 24.667-4.8333 67.5h-30.667v-92.167h35.5z" style="font-variant-caps:small-caps"/><path d="m338.5 203.83q0-9.5-5.6667-15.333-5.5-5.8333-20.833-10.667-34.167-9.3333-47.333-22.833-13.167-13.667-13.167-38.5 0-25 17.667-41.167 17.667-16.167 45.833-16.167 31.5 0 50.167 16.5 18.833 16.333 18.833 44.167h-47q0-11-6-17.833-5.8333-6.8333-15.833-6.8333-8.8333 0-13.833 5.5-5 5.5-5 14 0 8 5.5 13.167 5.6667 5.1667 20.167 10.333 32.667 8.3333 47 23.167t14.333 41-17.333 41.333q-17.333 15-48 15-14.667 0-28.5-4.3333-13.667-4.3333-23.5-13-20-17.333-20-47.333h47.333q0 16.167 6 22.667 6 6.3333 21.167 6.3333 18 0 18-19.167z" style="font-variant-caps:small-caps"/><path d="m416.5 256v-242.67h64.167q42.5 0 67.667 27 25.333 27 25.833 74v39.333q0 47.833-25.333 75.167-25.167 27.167-69.5 27.167zm49-201.83v161.17h14.667q24.5 0 34.5-12.833 10-13 10.5-44.667v-42.167q0-34-9.5-47.333-9.5-13.5-32.333-14.167z" style="font-variant-caps:small-caps"/></g></svg>
|
After Width: | Height: | Size: 1.3 KiB |
BIN
web/assets/img/favicon_128.png
Executable file
After Width: | Height: | Size: 854 B |
BIN
web/assets/img/my_cat.webp
Executable file
After Width: | Height: | Size: 218 KiB |
BIN
web/assets/img/my_cat_2.webp
Executable file
After Width: | Height: | Size: 336 KiB |
BIN
web/assets/img/my_cat_2_thumb.webp
Executable file
After Width: | Height: | Size: 86 KiB |
BIN
web/assets/img/my_cat_thumb.webp
Executable file
After Width: | Height: | Size: 50 KiB |
BIN
web/assets/img/raspi.webp
Executable file
After Width: | Height: | Size: 275 KiB |
BIN
web/assets/img/raspi_thumb.webp
Executable file
After Width: | Height: | Size: 34 KiB |
BIN
web/assets/img/ts3_banner_960.png
Executable file
After Width: | Height: | Size: 17 KiB |
40
web/assets/js/captcha_refresh.js
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
const g_captcha_timeout_seconds = 600;
|
||||||
|
|
||||||
|
const e_captcha = document.getElementsByClassName("captcha")[0];
|
||||||
|
|
||||||
|
let g_captcha_timeout_remain = g_captcha_timeout_seconds;
|
||||||
|
let g_current_captcha_id = e_captcha.children[0].value;
|
||||||
|
|
||||||
|
async function getNewCaptcha() {
|
||||||
|
const id = await fetch("/api/captcha/", { method: "POST"}).then(r => r.text());
|
||||||
|
g_current_captcha_id = e_captcha.children[0].value = id;
|
||||||
|
const style = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
|
||||||
|
setTimeout(() => { e_captcha.children[1].src = `/api/captcha/${id}/image?style=${style}` }, 600);
|
||||||
|
g_captcha_timeout_remain = g_captcha_timeout_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
e_captcha.children[3].innerHTML =
|
||||||
|
`<button id="refresh">Refresh</button>ed in <b><span id="remain">600</span></b> seconds.`;
|
||||||
|
|
||||||
|
const captcha_refresh = document.getElementById("refresh");
|
||||||
|
const captcha_remain = document.getElementById("remain");
|
||||||
|
|
||||||
|
captcha_refresh.classList.toggle("refresh");
|
||||||
|
captcha_refresh.addEventListener("click", async e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.target.disabled = true;
|
||||||
|
setTimeout(async () => {
|
||||||
|
e.target.disabled = false;
|
||||||
|
await fetch(`/api/captcha/${g_current_captcha_id}?remove`); }, 3000);
|
||||||
|
e_captcha.children[2].value = "";
|
||||||
|
await getNewCaptcha();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove unused CAPTCHA on a server.
|
||||||
|
window.addEventListener("unload", () => fetch(`/api/captcha/${g_current_captcha_id}?remove`));
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
captcha_remain.innerText = --g_captcha_timeout_remain;
|
||||||
|
if (g_captcha_timeout_remain == 0)
|
||||||
|
await getNewCaptcha();
|
||||||
|
}, 1000);
|
12
web/assets/js/mindflow.js
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
const articles = document.getElementsByTagName("article");
|
||||||
|
|
||||||
|
document.getElementById("filter").classList.remove("hidden");
|
||||||
|
|
||||||
|
function filter(e) {
|
||||||
|
for (const a of articles)
|
||||||
|
a.classList.toggle("hidden",
|
||||||
|
!(e.target.name === "all" || a.id.startsWith(e.target.name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const b of document.getElementsByTagName("button"))
|
||||||
|
b.addEventListener("click", filter);
|