move to zs generator
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Pavel 2023-11-12 22:41:53 +03:00
parent 854b2dbd96
commit bf721ec8b7
40 changed files with 3642 additions and 1608 deletions

30
.drone.yml Normal file
View File

@ -0,0 +1,30 @@
kind: pipeline
type: docker
name: default
steps:
- name: generate
image: golang:1.19-alpine
commands:
- apk add --no-cache curl make bash
- make all
- name: release
image: alpine:latest
commands:
- apk add --no-cache git
- cd .pub
- git clone --filter=tree:0 https://gitea.neur0tx.site/Neur0toxine/neur0tx.site.git
- cd neur0tx.site
- git checkout gitea-pages || git checkout -b gitea-pages
- cd ..
- mv neur0tx.site/.git .
- rm -rf neur0tx.site
- git add .
- git commit -a -m "`date +'%Y-%m-%d %R:%S'` > deploy"
- git push -u origin gitea-pages
- cd ..
- rm -rf .pub
trigger:
branch:
- master

7
.gitignore vendored
View File

@ -1,3 +1,4 @@
html/*
!html/.gitkeep
node_modules
*~
*.bak
**/.DS_Store
/.pub

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"cSpell.words": [
"insideme",
"Shiprock",
"wikilinks"
]
}

17
.zs/config.yml Normal file
View File

@ -0,0 +1,17 @@
---
title: neur0tx.site
description: neur0tx.site contents
extensions:
- anchor
- d2
- definitionlist
- embed
- fences
- linkify
- footnote
- strikethrough
- table
- tasklist
- typography
- wikilink

7
.zs/editthispage Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
GITEA_BASE_URL="https://gitea.neur0tx.site/Neur0toxine/neur0tx.site/_edit/master"
url="${GITEA_BASE_URL}/${ZS_FILE}"
printf "<a href=\"%s\" rel=\"nofollow\" target=\"_blank\">✍️ Edit this page on Gitea.</a>" "$url"

14
.zs/include Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
set -e
if [ ! $# = 1 ]; then
printf "Usage: %s <file>\n" "$(basename "$0")"
exit 0
fi
if [ -f "$1" ]; then
cat "$1"
else
echo "error: file not found $1"
fi

34
.zs/layout.html Normal file
View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{{ styles }}
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<title>{{ title }}</title>
<meta name="title" content="{{ title }}" />
<meta name="description" content="{{ description }}" />
<meta name="keywords" content="{{ keywords }}" />
</head>
<body>
<nav>
<a id="brand" href="/">home 🏠</a>
<a href="https://proxy.neur0tx.site">proxy 💻</a>
</nav>
<article>{{ content }}</article>
{{ editthispage }}
<footer>
Copyright &copy; Neur0toxine ·
Last modified <time datetime="{{ date +%Y-%m-%dT%H:%M:%SZ%:z }}">{{ date }}</time> ·
</footer>
{{ scripts }}
<script>
hljs.highlightAll();
</script>
</body>
</html>

29
.zs/list Executable file
View File

@ -0,0 +1,29 @@
#!/bin/sh
set -e
if [ ! $# = 1 ]; then
printf >&2 "Usage: %s <path> [ext]\n" "$(basename "$0")"
exit 0
fi
p="$1"
t="${2:-md}"
if [ ! -d "$p" ]; then
printf >&2 "error: path %s not found\n" "$p"
exit 1
fi
find "$p" -type f -name "*.$t" | while read -r file; do
name="${file#"$p"}"
name="${name#"/"}"
name="${name%.*}"
title="$(zs vars "$file" title)"
if [ -z "$title" ]; then
title="$name"
fi
echo "- [$title](${p}/${name}.html)"
done

17
.zs/posthook Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
set -e
minify_assets() {
p="$1"
t="$2"
find "$p" -type f -name "*.$t" | while read -r file; do
name="${file#"$p"}"
name="${name#"/"}"
minify -o "${p}/${name}" "$file"
done
}
minify_assets "$ZS_OUTDIR" "css"
minify_assets "$ZS_OUTDIR" "js"

3
.zs/prehook Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
exit 0

14
.zs/scripts Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
set -e
JS="highlight umbrella"
# Load live.js for non-production builds for faster development
if [ -z "$ZS_PRODUCTION" ]; then
JS="$JS live"
fi
for js in $JS; do
printf "<script type=\"application/javascript\" src=\"/assets/js/%s.js\"></script>\n" "$js"
done

9
.zs/styles Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
set -e
CSS="bahunya board highlight site"
for css in $CSS; do
printf "<link rel=\"stylesheet\" href=\"/assets/css/%s.css\">\n" "$css"
done

4
.zsignore Normal file
View File

@ -0,0 +1,4 @@
*~
*.bak
Makefile
README.md

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Pavel Kovalenko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

20
Makefile Normal file
View File

@ -0,0 +1,20 @@
.PHONY: deps dev build clean
GOCMD=go
TAG := latest
all: deps build
deps:
@$(GOCMD) install go.mills.io/zs@latest
@$(GOCMD) install github.com/tdewolff/minify/v2/cmd/minify@latest
dev : DEBUG=1
dev : build
@zs serve
build:
@zs build
clean:
@git clean -f -d -X

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# neur0tx.site
Site contents.

BIN
android-chrome-192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
android-chrome-512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

995
assets/css/bahunya.css Normal file
View File

@ -0,0 +1,995 @@
:root {
--background-body: #0d1117;
--background: rgb(22, 27, 34);
--background-bright: rgba(240, 246, 252, 0.15);
--background-alt: #1a242f;
--selection: #1c76c5;
--text-main: rgb(201, 209, 217);
--text-bright: rgb(227, 233, 240);
--text-muted: rgb(139, 148, 158);
--links: #e3bc5e;
--focus: rgb(56, 139, 253);
--focus-background: rgb(12, 45, 107);
--border: rgb(33, 37, 44);
--border-muted: rgb(33, 38, 45);
--border-bright: rgb(139, 148, 158);
--button-hover: #324759;
--form-placeholder: #a9a9a9;
--select-arrow: svg-load("./assets/select-arrow.svg", fill: #efefef);
/* Fonts */
--monospace: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
--sans-serif: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
"Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
"Helvetica Neue", "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji",
sans-serif;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
"Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
"Helvetica Neue", "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji",
sans-serif;
font-family: var(--sans-serif);
line-height: 1.7;
max-width: 50rem;
margin: 6rem auto;
padding: 0 10px;
word-wrap: break-word;
color: rgb(201, 209, 217);
color: var(--text-main);
background: #0d1117;
background: var(--background-body);
text-rendering: optimizeLegibility;
}
h1,
h2,
h3,
h4,
h5 {
margin: 3rem 0 1.38rem;
line-height: 1.3;
}
h1 {
font-size: 2.488rem;
}
h2 {
font-size: 2.074rem;
}
h3 {
font-size: 1.728rem;
}
h4 {
font-size: 1.44rem;
}
h5 {
font-size: 1.2rem;
}
h6 {
font-size: 1rem;
}
small {
font-size: 0.833rem;
}
h1 {
color: rgb(227, 233, 240);
color: var(--text-bright);
}
h2 {
color: rgb(227, 233, 240);
color: var(--text-bright);
}
h3 {
color: rgb(227, 233, 240);
color: var(--text-bright);
}
h4 {
color: rgb(227, 233, 240);
color: var(--text-bright);
}
h5 {
color: rgb(227, 233, 240);
color: var(--text-bright);
}
h6 {
color: rgb(227, 233, 240);
color: var(--text-bright);
}
strong {
color: rgb(227, 233, 240);
color: var(--text-bright);
}
blockquote {
border-left: 4px solid rgb(56, 139, 253);
border-left: 4px solid var(--focus);
margin: 1.5em 0;
padding: 0em 1em;
}
blockquote> :first-child {
margin-top: 0;
}
blockquote> :last-child {
margin-bottom: 0;
}
blockquote>footer {
border: 0;
}
address {
font-style: normal;
}
a[href^="mailto\:"]::before {
content: "📧 ";
}
a[href^="tel\:"]::before {
content: "📞 ";
}
a[href^="sms\:"]::before {
content: "💬 ";
}
mark {
background-color: #e3bc5e;
background-color: var(--links);
border-radius: 2px;
padding: 0 2px 0 2px;
}
ul,
ol {
padding-left: 2em;
}
aside {
width: 40%;
padding-left: .5rem;
margin-left: .5rem;
float: right;
border-left: 2px solid rgb(56, 139, 253);
border-left: 2px solid var(--focus);
font-style: italic;
}
aside>p {
margin: .5rem;
}
button,
select,
input[type="submit"],
input[type="reset"],
input[type="button"],
input[type="checkbox"],
input[type="range"],
input[type="radio"] {
cursor: pointer;
}
input:not([type="checkbox"]):not([type="radio"]),
select {
display: block;
}
input {
color: rgb(227, 233, 240);
color: var(--text-bright);
background-color: rgb(22, 27, 34);
background-color: var(--background);
font-family: inherit;
font-size: inherit;
margin-right: 6px;
margin-bottom: 6px;
padding: 10px;
border: none;
border-radius: 6px;
outline: none;
border: 1px solid rgb(33, 38, 45);
border: 1px solid var(--border-muted);
}
textarea {
color: rgb(227, 233, 240);
color: var(--text-bright);
background-color: rgb(22, 27, 34);
background-color: var(--background);
font-family: inherit;
font-size: inherit;
margin-right: 6px;
margin-bottom: 6px;
padding: 10px;
border: none;
border-radius: 6px;
outline: none;
border: 1px solid rgb(33, 38, 45);
border: 1px solid var(--border-muted);
}
select {
color: rgb(227, 233, 240);
color: var(--text-bright);
background-color: rgb(22, 27, 34);
background-color: var(--background);
font-family: inherit;
font-size: inherit;
margin-right: 6px;
margin-bottom: 6px;
padding: 10px;
border: none;
border-radius: 6px;
outline: none;
border: 1px solid rgb(33, 38, 45);
border: 1px solid var(--border-muted);
}
input[type="color"] {
min-height: 2rem;
padding: 8px;
cursor: pointer;
}
input[type="checkbox"],
input[type="radio"] {
height: 1.2em;
width: 1em;
}
input[type="radio"] {
border-radius: 100%;
}
input {
vertical-align: top;
}
label {
vertical-align: middle;
margin-bottom: 4px;
display: inline-block;
}
input:not([type="checkbox"]):not([type="radio"]),
input[type="range"],
select,
button,
textarea {
-webkit-appearance: none;
}
textarea {
display: block;
margin-right: 0;
resize: vertical;
width: 100%;
}
textarea:not([cols]) {
width: 100%;
}
textarea:not([rows]) {
min-height: 40px;
height: 140px;
}
select {
background: rgb(22, 27, 34) svg-load("./assets/select-arrow.svg", fill: #efefef) calc(100% - 12px) 50% / 12px no-repeat;
background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat;
padding-right: 35px;
}
select::-ms-expand {
display: none;
}
select[multiple] {
padding-right: 10px;
background-image: none;
overflow-y: auto;
}
input:focus {
border: 1px solid rgb(56, 139, 253);
border: 1px solid var(--focus);
box-shadow: 0 0 0 3px rgb(12, 45, 107);
box-shadow: 0 0 0 3px var(--focus-background);
}
select:focus {
border: 1px solid rgb(56, 139, 253);
border: 1px solid var(--focus);
box-shadow: 0 0 0 3px rgb(12, 45, 107);
box-shadow: 0 0 0 3px var(--focus-background);
}
textarea:focus {
border: 1px solid rgb(56, 139, 253);
border: 1px solid var(--focus);
box-shadow: 0 0 0 3px rgb(12, 45, 107);
box-shadow: 0 0 0 3px var(--focus-background);
}
input:disabled,
select:disabled,
button:disabled,
textarea:disabled {
cursor: not-allowed;
opacity: 0.5;
}
::-moz-placeholder {
color: #a9a9a9;
color: var(--form-placeholder);
}
:-ms-input-placeholder {
color: #a9a9a9;
color: var(--form-placeholder);
}
::placeholder {
color: #a9a9a9;
color: var(--form-placeholder);
}
fieldset {
border: 1px rgb(33, 37, 44) solid;
border: 1px var(--border) solid;
border-radius: 6px;
margin: 0;
margin-bottom: 12px;
padding: 10px 20px;
}
legend {
font-size: 0.9em;
font-weight: 600;
}
input[type="range"] {
margin: 10px 0;
padding: 10px 0;
background: transparent;
}
input[type="range"]:focus {
outline: none;
}
input[type="range"]::-webkit-slider-runnable-track {
width: 100%;
height: 9.5px;
-webkit-transition: 0.2s;
transition: 0.2s;
background: rgb(22, 27, 34);
background: var(--background);
border-radius: 3px;
}
input[type="range"]::-webkit-slider-thumb {
box-shadow: 0 1px 1px #000, 0 0 1px #0d0d0d;
height: 20px;
width: 20px;
border-radius: 50%;
background: rgb(33, 37, 44);
background: var(--border);
-webkit-appearance: none;
margin-top: -7px;
}
input[type="range"]:focus::-webkit-slider-runnable-track {
background: rgb(22, 27, 34);
background: var(--background);
}
input[type="range"]::-moz-range-track {
width: 100%;
height: 9.5px;
-moz-transition: 0.2s;
transition: 0.2s;
background: rgb(22, 27, 34);
background: var(--background);
border-radius: 3px;
}
input[type="range"]::-moz-range-thumb {
box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d;
height: 20px;
width: 20px;
border-radius: 50%;
background: rgb(33, 37, 44);
background: var(--border);
}
input[type="range"]::-ms-track {
width: 100%;
height: 9.5px;
background: transparent;
border-color: transparent;
border-width: 16px 0;
color: transparent;
}
input[type="range"]::-ms-fill-lower {
background: rgb(22, 27, 34);
background: var(--background);
border: 0.2px solid #010101;
border-radius: 3px;
box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d;
}
input[type="range"]::-ms-fill-upper {
background: rgb(22, 27, 34);
background: var(--background);
border: 0.2px solid #010101;
border-radius: 3px;
box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d;
}
input[type="range"]::-ms-thumb {
box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d;
border: 1px solid #000;
height: 20px;
width: 20px;
border-radius: 50%;
background: rgb(33, 37, 44);
background: var(--border);
}
input[type="range"]:focus::-ms-fill-lower {
background: rgb(22, 27, 34);
background: var(--background);
}
input[type="range"]:focus::-ms-fill-upper {
background: rgb(22, 27, 34);
background: var(--background);
}
a {
text-decoration: none;
color: #e3bc5e;
color: var(--links);
}
a:hover {
text-decoration: underline;
}
code {
font-size: 85%;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
font-family: var(--monospace);
}
samp {
font-size: 85%;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
font-family: var(--monospace);
}
tt {
font-size: 85%;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
font-family: var(--monospace);
}
var {
font-size: 85%;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
font-family: var(--monospace);
}
kbd {
font-size: 85%;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
font-family: var(--monospace);
}
code,
samp,
tt,
kbd {
padding: 0.2em 0.4em;
border-radius: 0.4em;
}
code {
background: rgba(240, 246, 252, 0.15);
background: var(--background-bright);
}
samp {
background: rgba(240, 246, 252, 0.15);
background: var(--background-bright);
}
tt {
background: rgba(240, 246, 252, 0.15);
background: var(--background-bright);
}
pre {
display: block;
overflow-x: auto;
}
pre>code {
background: rgb(22, 27, 34);
background: var(--background);
padding: 1em;
display: block;
overflow-x: auto;
}
var {
color: rgb(56, 139, 253);
color: var(--focus);
font-style: normal;
}
kbd {
border: 1px solid rgb(139, 148, 158);
border: 1px solid var(--border-bright);
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
font-family: var(--monospace);
}
img,
video {
max-width: 100%;
height: auto;
}
hr {
border: none;
border-top: 1px solid rgb(139, 148, 158);
border-top: 1px solid var(--border-bright);
margin: 1em 0;
}
table {
border-collapse: collapse;
margin-bottom: 10px;
width: 100%;
table-layout: fixed;
overflow-x: auto;
display: block;
}
table caption {
text-align: left;
}
td,
th {
padding: 6px;
text-align: left;
vertical-align: top;
word-wrap: break-word;
}
thead {
border-bottom: 1px solid rgb(139, 148, 158);
border-bottom: 1px solid var(--border-bright);
}
tfoot {
border-top: 1px solid rgb(139, 148, 158);
border-top: 1px solid var(--border-bright);
}
tbody tr:nth-child(even) {
background-color: rgb(22, 27, 34);
background-color: var(--background);
}
tbody tr:nth-child(even) button {
background-color: #1a242f;
background-color: var(--background-alt);
}
tbody tr:nth-child(even) button:hover {
background-color: #0d1117;
background-color: var(--background-body);
}
::-moz-selection {
background-color: #1c76c5;
background-color: var(--selection);
color: rgb(227, 233, 240);
color: var(--text-bright);
}
::selection {
background-color: #1c76c5;
background-color: var(--selection);
color: rgb(227, 233, 240);
color: var(--text-bright);
}
details summary {
padding: 0.5rem 0;
border-top: 1px solid rgb(33, 37, 44);
border-top: 1px solid var(--border);
cursor: pointer;
font-size: 1.2rem;
outline: none;
/* font-weight: 300; */
/* list-style: none; */
}
/* details summary:after {
content: "+";
color: white;
position: absolute;
font-size: 1.75rem;
line-height: 0;
margin-top: 0.75rem;
right: 0;
font-weight: 200;
transform-origin: center;
transition: 200ms linear;
}
details[open] summary:after {
transform: rotate(45deg);
font-size: 2rem;
} */
summary:hover,
summary:focus {
text-decoration: underline;
}
details> :not(summary) {
/* margin-top: 0; */
}
summary::-webkit-details-marker {
/* color: var(--text-main); */
}
dialog {
background-color: #0d1117;
background-color: var(--background-body);
color: rgb(201, 209, 217);
color: var(--text-main);
border: none;
border-radius: 6px;
border: 1px solid rgb(33, 37, 44);
border: 1px solid var(--border);
padding: 10px 30px;
}
dialog>header:first-child {
border-radius: 6px 6px 0 0;
margin: -10px -30px 10px;
padding: 10px;
text-align: center;
border-bottom: 1px solid rgb(33, 37, 44);
border-bottom: 1px solid var(--border);
}
dialog::-webkit-backdrop {
background: #0000009c;
-webkit-backdrop-filter: blur(4px);
backdrop-filter: blur(4px);
}
dialog::backdrop {
background: #0000009c;
-webkit-backdrop-filter: blur(4px);
backdrop-filter: blur(4px);
}
footer {
border-top: 1px solid rgb(33, 37, 44);
border-top: 1px solid var(--border);
padding-top: 10px;
color: rgb(139, 148, 158);
color: var(--text-muted);
}
body>footer {
margin-top: 40px;
}
@media print {
body,
pre,
code,
summary,
details,
button,
input,
textarea {
background-color: #fff;
}
button,
input,
textarea {
border: 1px solid #000;
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
code,
button,
input,
textarea,
footer,
summary,
strong {
color: #000;
}
summary::marker {
color: #000;
}
summary::-webkit-details-marker {
color: #000;
}
tbody tr:nth-child(even) {
background-color: #f2f2f2;
}
a {
color: #00f;
text-decoration: underline;
}
}
body nav:first-of-type {
position: fixed;
top: 0;
left: 0;
width: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
padding: 0 calc((100vw - 50rem) / 2);
display: flex;
align-items: center;
width: 100%;
z-index: 999;
}
body nav:first-of-type:before {
/* https://stackoverflow.com/questions/63907743/parent-element-backdrop-filter-does-not-apply-for-its-child */
backdrop-filter: saturate(180%) blur(5px);
-webkit-backdrop-filter: saturate(180%) blur(5px);
content: "";
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
body nav:first-of-type #brand {
margin-right: auto;
font-size: 1.1rem;
}
body nav:first-of-type a {
margin: 10px;
display: inline-block;
}
body nav:first-of-type ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
display: inline;
}
body nav:first-of-type>ul>li {
float: left;
}
body nav:first-of-type ul li ul {
display: none;
position: absolute;
background: rgba(0, 0, 0, 0.5);
/* z-index: 1; */
-webkit-backdrop-filter: saturate(180%) blur(5px);
backdrop-filter: saturate(180%) blur(5px);
}
body nav:first-of-type ul li:hover ul {
display: block;
}
section {
margin: 20px 0;
}
article {
border: 1px solid rgb(33, 37, 44);
border: 1px solid var(--border);
padding: 20px;
margin: 10px 0;
border-radius: 0.4em;
}
article> :first-child {
margin-top: 0;
}
article> :first-child> :first-child {
margin-top: 0;
}
article> :last-child {
margin-bottom: 0;
}
article> :last-child> :last-child {
margin-bottom: 0;
}
input[type="submit"] {
background: rgba(240, 246, 252, 0.15);
background: var(--background-bright);
/* border: 1px solid rgb(48, 54, 61); */
border: 1px solid rgb(48, 54, 61);
color: rgb(227, 233, 240);
color: var(--text-bright);
font-family: inherit;
font-size: inherit;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
padding: 5px 16px;
border-radius: 6px;
outline: none;
font-size: 87.5%;
}
input[type="reset"] {
background: rgba(240, 246, 252, 0.15);
background: var(--background-bright);
/* border: 1px solid rgb(48, 54, 61); */
border: 1px solid rgb(48, 54, 61);
color: rgb(227, 233, 240);
color: var(--text-bright);
font-family: inherit;
font-size: inherit;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
padding: 5px 16px;
border-radius: 6px;
outline: none;
font-size: 87.5%;
}
input[type="button"] {
background: rgba(240, 246, 252, 0.15);
background: var(--background-bright);
/* border: 1px solid rgb(48, 54, 61); */
border: 1px solid rgb(48, 54, 61);
color: rgb(227, 233, 240);
color: var(--text-bright);
font-family: inherit;
font-size: inherit;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
padding: 5px 16px;
border-radius: 6px;
outline: none;
font-size: 87.5%;
}
button {
background: rgba(240, 246, 252, 0.15);
background: var(--background-bright);
/* border: 1px solid rgb(48, 54, 61); */
border: 1px solid rgb(48, 54, 61);
color: rgb(227, 233, 240);
color: var(--text-bright);
font-family: inherit;
font-size: inherit;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
padding: 5px 16px;
border-radius: 6px;
outline: none;
font-size: 87.5%;
}
input[type="submit"]:hover,
input[type="reset"]:hover,
input[type="button"]:hover,
button:hover {
/* background-color: rgb(48, 54, 61); */
border-color: rgb(139, 148, 158);
filter: brightness(115%);
}
input[type="submit"]:focus,
input[type="reset"]:focus,
input[type="button"]:focus,
button:focus {
border: 1px solid rgb(139, 148, 158);
box-shadow: none;
}
input[type="submit"]:active,
input[type="reset"]:active,
input[type="button"]:active,
button:active {
background: none;
}
input[type="submit"]:disabled,
input[type="reset"]:disabled,
input[type="button"]:disabled,
button:disabled {
border-color: rgb(48, 54, 61);
}
/*# sourceMappingURL=bahunya.css.map */
iframe {
width: 100%;
border: none;
}
audio,
video,
embed,
object {
width: 100%;
}

35
assets/css/board.css Normal file
View File

@ -0,0 +1,35 @@
.board {
display: grid;
column-gap: 50px;
justify-items: center;
}
/* Display long board titles without extending first grid column. Will display at least 6 columns */
.board h1 {
grid-column: 1/6;
justify-self: left;
padding-left: 5%;
}
.board h2 {
grid-row: 2;
}
/* Disable anchor hover effect */
.board h2:hover .anchor:before {
display: none;
}
.board ul {
grid-row: 3;
padding-left: 0;
}
.board li {
display: block;
border: 1px solid;
padding: 15px;
margin-bottom: 30px;
text-align: center;
border-radius: 25px;
}

9
assets/css/highlight.css Normal file
View File

@ -0,0 +1,9 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
Theme: GitHub Dark Dimmed
Description: Dark dimmed theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Colors taken from GitHub's CSS
*/.hljs{color:#adbac7;background:#22272e}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#f47067}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#dcbdfb}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#6cb6ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#96d0ff}.hljs-built_in,.hljs-symbol{color:#f69d50}.hljs-code,.hljs-comment,.hljs-formula{color:#768390}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#8ddb8c}.hljs-subst{color:#adbac7}.hljs-section{color:#316dca;font-weight:700}.hljs-bullet{color:#eac55f}.hljs-emphasis{color:#adbac7;font-style:italic}.hljs-strong{color:#adbac7;font-weight:700}.hljs-addition{color:#b4f1b4;background-color:#1b4721}.hljs-deletion{color:#ffd8d3;background-color:#78191b}

26
assets/css/site.css Normal file
View File

@ -0,0 +1,26 @@
/* Anchors */
h1:hover .anchor:before,
h2:hover .anchor:before,
h3:hover .anchor:before,
h4:hover .anchor:before,
h5:hover .anchor:before,
h6:hover .anchor:before {
width: 16px;
height: 16px;
content: ' ';
display: inline-block;
background-color: currentColor;
-webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
}
/* Wider site */
body {
max-width: 60rem;
}
body nav:first-of-type {
padding: 0 calc((100vw - 60rem)/2);
}

1202
assets/js/highlight.js Normal file

File diff suppressed because one or more lines are too long

233
assets/js/live.js Normal file
View File

@ -0,0 +1,233 @@
/*
Live.js - One script closer to Designing in the Browser
Written for Handcraft.com by Martin Kool (@mrtnkl).
Version 4.
Recent change: Made stylesheet and mimetype checks case insensitive.
http://livejs.com
http://livejs.com/license (MIT)
@livejs
Include live.js#css to monitor css changes only.
Include live.js#js to monitor js changes only.
Include live.js#html to monitor html changes only.
Mix and match to monitor a preferred combination such as live.js#html,css
By default, just include live.js to monitor all css, js and html changes.
Live.js can also be loaded as a bookmarklet. It is best to only use it for CSS then,
as a page reload due to a change in html or css would not re-include the bookmarklet.
To monitor CSS and be notified that it has loaded, include it as: live.js#css,notify
*/
(function () {
var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 },
resources = {},
pendingRequests = {},
currentLinkElements = {},
oldLinkElements = {},
interval = 1000,
loaded = false,
active = { "html": 1, "css": 1, "js": 1 };
var Live = {
// performs a cycle per interval
heartbeat: function () {
if (document.body) {
// make sure all resources are loaded on first activation
if (!loaded) Live.loadresources();
Live.checkForChanges();
}
setTimeout(Live.heartbeat, interval);
},
// loads all local css and js resources upon first activation
loadresources: function () {
// helper method to assert if a given url is local
function isLocal(url) {
var loc = document.location,
reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host);
return url.match(reg);
}
// gather all resources
var scripts = document.getElementsByTagName("script"),
links = document.getElementsByTagName("link"),
uris = [];
// track local js urls
for (var i = 0; i < scripts.length; i++) {
var script = scripts[i], src = script.getAttribute("src");
if (src && isLocal(src))
uris.push(src);
if (src && src.match(/\blive.js#/)) {
for (var type in active)
active[type] = src.match("[#,|]" + type) != null
if (src.match("notify"))
alert("Live.js is loaded.");
}
}
if (!active.js) uris = [];
if (active.html) uris.push(document.location.href);
// track local css urls
for (var i = 0; i < links.length && active.css; i++) {
var link = links[i], rel = link.getAttribute("rel"), href = link.getAttribute("href", 2);
if (href && rel && rel.match(new RegExp("stylesheet", "i")) && isLocal(href)) {
uris.push(href);
currentLinkElements[href] = link;
}
}
// initialize the resources info
for (var i = 0; i < uris.length; i++) {
var url = uris[i];
Live.getHead(url, function (url, info) {
resources[url] = info;
});
}
// add rule for morphing between old and new css files
var head = document.getElementsByTagName("head")[0],
style = document.createElement("style"),
rule = "transition: all .3s ease-out;"
css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join('');
style.setAttribute("type", "text/css");
head.appendChild(style);
style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css));
// yep
loaded = true;
},
// check all tracking resources for changes
checkForChanges: function () {
for (var url in resources) {
if (pendingRequests[url])
continue;
Live.getHead(url, function (url, newInfo) {
var oldInfo = resources[url],
hasChanged = false;
resources[url] = newInfo;
for (var header in oldInfo) {
// do verification based on the header type
var oldValue = oldInfo[header],
newValue = newInfo[header],
contentType = newInfo["Content-Type"];
switch (header.toLowerCase()) {
case "etag":
if (!newValue) break;
// fall through to default
default:
hasChanged = oldValue != newValue;
break;
}
// if changed, act
if (hasChanged) {
Live.refreshResource(url, contentType);
break;
}
}
});
}
},
// act upon a changed url of certain content type
refreshResource: function (url, type) {
switch (type.toLowerCase()) {
// css files can be reloaded dynamically by replacing the link element
case "text/css":
var link = currentLinkElements[url],
html = document.body.parentNode,
head = link.parentNode,
next = link.nextSibling,
newLink = document.createElement("link");
html.className = html.className.replace(/\s*livejs\-loading/gi, '') + ' livejs-loading';
newLink.setAttribute("type", "text/css");
newLink.setAttribute("rel", "stylesheet");
newLink.setAttribute("href", url + "?now=" + new Date() * 1);
next ? head.insertBefore(newLink, next) : head.appendChild(newLink);
currentLinkElements[url] = newLink;
oldLinkElements[url] = link;
// schedule removal of the old link
Live.removeoldLinkElements();
break;
// check if an html resource is our current url, then reload
case "text/html":
if (url != document.location.href)
return;
// local javascript changes cause a reload as well
case "text/javascript":
case "application/javascript":
case "application/x-javascript":
document.location.reload();
}
},
// removes the old stylesheet rules only once the new one has finished loading
removeoldLinkElements: function () {
var pending = 0;
for (var url in oldLinkElements) {
// if this sheet has any cssRules, delete the old link
try {
var link = currentLinkElements[url],
oldLink = oldLinkElements[url],
html = document.body.parentNode,
sheet = link.sheet || link.styleSheet,
rules = sheet.rules || sheet.cssRules;
if (rules.length >= 0) {
oldLink.parentNode.removeChild(oldLink);
delete oldLinkElements[url];
setTimeout(function () {
html.className = html.className.replace(/\s*livejs\-loading/gi, '');
}, 100);
}
} catch (e) {
pending++;
}
if (pending) setTimeout(Live.removeoldLinkElements, 50);
}
},
// performs a HEAD request and passes the header info to the given callback
getHead: function (url, callback) {
pendingRequests[url] = true;
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp");
xhr.open("HEAD", url, true);
xhr.onreadystatechange = function () {
delete pendingRequests[url];
if (xhr.readyState == 4 && xhr.status != 304) {
xhr.getAllResponseHeaders();
var info = {};
for (var h in headers) {
var value = xhr.getResponseHeader(h);
// adjust the simple Etag variant to match on its significant part
if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, '');
if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1");
info[h] = value;
}
callback(url, info);
}
}
xhr.send();
}
};
// start listening
if (document.location.protocol != "file:") {
if (!window.liveJsLoaded)
Live.heartbeat();
window.liveJsLoaded = true;
}
else if (window.console)
console.log("Live.js doesn't support the file protocol. It needs http.");
})();

806
assets/js/umbrella.js Normal file
View File

@ -0,0 +1,806 @@
// Umbrella JS http://umbrellajs.com/
// -----------
// Small, lightweight jQuery alternative
// @author Francisco Presencia Fandos https://francisco.io/
// @inspiration http://youmightnotneedjquery.com/
// Initialize the library
var u = function (parameter, context) {
// Make it an instance of u() to avoid needing 'new' as in 'new u()' and just
// use 'u().bla();'.
// @reference http://stackoverflow.com/q/24019863
// @reference http://stackoverflow.com/q/8875878
if (!(this instanceof u)) {
return new u(parameter, context);
}
// No need to further processing it if it's already an instance
if (parameter instanceof u) {
return parameter;
}
// Parse it as a CSS selector if it's a string
if (typeof parameter === 'string') {
parameter = this.select(parameter, context);
}
// If we're referring a specific node as in on('click', function(){ u(this) })
// or the select() function returned a single node such as in '#id'
if (parameter && parameter.nodeName) {
parameter = [parameter];
}
// Convert to an array, since there are many 'array-like' stuff in js-land
this.nodes = this.slice(parameter);
};
// Map u(...).length to u(...).nodes.length
u.prototype = {
get length () {
return this.nodes.length;
}
};
// This made the code faster, read "Initializing instance variables" in
// https://developers.google.com/speed/articles/optimizing-javascript
u.prototype.nodes = [];
// Add class(es) to the matched nodes
u.prototype.addClass = function () {
return this.eacharg(arguments, function (el, name) {
el.classList.add(name);
});
};
// [INTERNAL USE ONLY]
// Add text in the specified position. It is used by other functions
u.prototype.adjacent = function (html, data, callback) {
if (typeof data === 'number') {
if (data === 0) {
data = [];
} else {
data = new Array(data).join().split(',').map(Number.call, Number);
}
}
// Loop through all the nodes. It cannot reuse the eacharg() since the data
// we want to do it once even if there's no "data" and we accept a selector
return this.each(function (node, j) {
var fragment = document.createDocumentFragment();
// Allow for data to be falsy and still loop once
u(data || {}).map(function (el, i) {
// Allow for callbacks that accept some data
var part = (typeof html === 'function') ? html.call(this, el, i, node, j) : html;
if (typeof part === 'string') {
return this.generate(part);
}
return u(part);
}).each(function (n) {
this.isInPage(n)
? fragment.appendChild(u(n).clone().first())
: fragment.appendChild(n);
});
callback.call(this, node, fragment);
});
};
// Add some html as a sibling after each of the matched elements.
u.prototype.after = function (html, data) {
return this.adjacent(html, data, function (node, fragment) {
node.parentNode.insertBefore(fragment, node.nextSibling);
});
};
// Add some html as a child at the end of each of the matched elements.
u.prototype.append = function (html, data) {
return this.adjacent(html, data, function (node, fragment) {
node.appendChild(fragment);
});
};
// [INTERNAL USE ONLY]
// Normalize the arguments to an array of strings
// Allow for several class names like "a b, c" and several parameters
u.prototype.args = function (args, node, i) {
if (typeof args === 'function') {
args = args(node, i);
}
// First flatten it all to a string http://stackoverflow.com/q/22920305
// If we try to slice a string bad things happen: ['n', 'a', 'm', 'e']
if (typeof args !== 'string') {
args = this.slice(args).map(this.str(node, i));
}
// Then convert that string to an array of not-null strings
return args.toString().split(/[\s,]+/).filter(function (e) {
return e.length;
});
};
// Merge all of the nodes that the callback return into a simple array
u.prototype.array = function (callback) {
callback = callback;
var self = this;
return this.nodes.reduce(function (list, node, i) {
var val;
if (callback) {
val = callback.call(self, node, i);
if (!val) val = false;
if (typeof val === 'string') val = u(val);
if (val instanceof u) val = val.nodes;
} else {
val = node.innerHTML;
}
return list.concat(val !== false ? val : []);
}, []);
};
// [INTERNAL USE ONLY]
// Handle attributes for the matched elements
u.prototype.attr = function (name, value, data) {
data = data ? 'data-' : '';
// This will handle those elements that can accept a pair with these footprints:
// .attr('a'), .attr('a', 'b'), .attr({ a: 'b' })
return this.pairs(name, value, function (node, name) {
return node.getAttribute(data + name);
}, function (node, name, value) {
if (value) {
node.setAttribute(data + name, value);
} else {
node.removeAttribute(data + name);
}
});
};
// Add some html before each of the matched elements.
u.prototype.before = function (html, data) {
return this.adjacent(html, data, function (node, fragment) {
node.parentNode.insertBefore(fragment, node);
});
};
// Get the direct children of all of the nodes with an optional filter
u.prototype.children = function (selector) {
return this.map(function (node) {
return this.slice(node.children);
}).filter(selector);
};
/**
* Deep clone a DOM node and its descendants.
* @return {[Object]} Returns an Umbrella.js instance.
*/
u.prototype.clone = function () {
return this.map(function (node, i) {
var clone = node.cloneNode(true);
var dest = this.getAll(clone);
this.getAll(node).each(function (src, i) {
for (var key in this.mirror) {
if (this.mirror[key]) {
this.mirror[key](src, dest.nodes[i]);
}
}
});
return clone;
});
};
/**
* Return an array of DOM nodes of a source node and its children.
* @param {[Object]} context DOM node.
* @param {[String]} tag DOM node tagName.
* @return {[Array]} Array containing queried DOM nodes.
*/
u.prototype.getAll = function getAll (context) {
return u([context].concat(u('*', context).nodes));
};
// Store all of the operations to perform when cloning elements
u.prototype.mirror = {};
/**
* Copy all JavaScript events of source node to destination node.
* @param {[Object]} source DOM node
* @param {[Object]} destination DOM node
* @return {[undefined]]}
*/
u.prototype.mirror.events = function (src, dest) {
if (!src._e) return;
for (var type in src._e) {
src._e[type].forEach(function (ref) {
u(dest).on(type, ref.callback);
});
}
};
/**
* Copy select input value to its clone.
* @param {[Object]} src DOM node
* @param {[Object]} dest DOM node
* @return {[undefined]}
*/
u.prototype.mirror.select = function (src, dest) {
if (u(src).is('select')) {
dest.value = src.value;
}
};
/**
* Copy textarea input value to its clone
* @param {[Object]} src DOM node
* @param {[Object]} dest DOM node
* @return {[undefined]}
*/
u.prototype.mirror.textarea = function (src, dest) {
if (u(src).is('textarea')) {
dest.value = src.value;
}
};
// Find the first ancestor that matches the selector for each node
u.prototype.closest = function (selector) {
return this.map(function (node) {
// Keep going up and up on the tree. First element is also checked
do {
if (u(node).is(selector)) {
return node;
}
} while ((node = node.parentNode) && node !== document);
});
};
// Handle data-* attributes for the matched elements
u.prototype.data = function (name, value) {
return this.attr(name, value, true);
};
// Loops through every node from the current call
u.prototype.each = function (callback) {
// By doing callback.call we allow "this" to be the context for
// the callback (see http://stackoverflow.com/q/4065353 precisely)
this.nodes.forEach(callback.bind(this));
return this;
};
// [INTERNAL USE ONLY]
// Loop through the combination of every node and every argument passed
u.prototype.eacharg = function (args, callback) {
return this.each(function (node, i) {
this.args(args, node, i).forEach(function (arg) {
// Perform the callback for this node
// By doing callback.call we allow "this" to be the context for
// the callback (see http://stackoverflow.com/q/4065353 precisely)
callback.call(this, node, arg);
}, this);
});
};
// Remove all children of the matched nodes from the DOM.
u.prototype.empty = function () {
return this.each(function (node) {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
});
};
// .filter(selector)
// Delete all of the nodes that don't pass the selector
u.prototype.filter = function (selector) {
// The default function if it's a CSS selector
// Cannot change name to 'selector' since it'd mess with it inside this fn
var callback = function (node) {
// Make it compatible with some other browsers
node.matches = node.matches || node.msMatchesSelector || node.webkitMatchesSelector;
// Check if it's the same element (or any element if no selector was passed)
return node.matches(selector || '*');
};
// filter() receives a function as in .filter(e => u(e).children().length)
if (typeof selector === 'function') callback = selector;
// filter() receives an instance of Umbrella as in .filter(u('a'))
if (selector instanceof u) {
callback = function (node) {
return (selector.nodes).indexOf(node) !== -1;
};
}
// Just a native filtering function for ultra-speed
return u(this.nodes.filter(callback));
};
// Find all the nodes children of the current ones matched by a selector
u.prototype.find = function (selector) {
return this.map(function (node) {
return u(selector || '*', node);
});
};
// Get the first of the nodes
u.prototype.first = function () {
return this.nodes[0] || false;
};
// [INTERNAL USE ONLY]
// Generate a fragment of HTML. This irons out the inconsistences
u.prototype.generate = function (html) {
// Table elements need to be child of <table> for some f***ed up reason
if (/^\s*<tr[> ]/.test(html)) {
return u(document.createElement('table')).html(html).children().children().nodes;
} else if (/^\s*<t(h|d)[> ]/.test(html)) {
return u(document.createElement('table')).html(html).children().children().children().nodes;
} else if (/^\s*</.test(html)) {
return u(document.createElement('div')).html(html).children().nodes;
} else {
return document.createTextNode(html);
}
};
// Change the default event for the callback. Simple decorator to preventDefault
u.prototype.handle = function () {
var args = this.slice(arguments).map(function (arg) {
if (typeof arg === 'function') {
return function (e) {
e.preventDefault();
arg.apply(this, arguments);
};
}
return arg;
}, this);
return this.on.apply(this, args);
};
// Find out whether the matched elements have a class or not
u.prototype.hasClass = function () {
// Check if any of them has all of the classes
return this.is('.' + this.args(arguments).join('.'));
};
// Set or retrieve the html from the matched node(s)
u.prototype.html = function (text) {
// Needs to check undefined as it might be ""
if (text === undefined) {
return this.first().innerHTML || '';
}
// If we're attempting to set some text
// Loop through all the nodes
return this.each(function (node) {
// Set the inner html to the node
node.innerHTML = text;
});
};
// Check whether any of the nodes matches the selector
u.prototype.is = function (selector) {
return this.filter(selector).length > 0;
};
/**
* Internal use only. This function checks to see if an element is in the page's body. As contains is inclusive and determining if the body contains itself isn't the intention of isInPage this case explicitly returns false.
https://developer.mozilla.org/en-US/docs/Web/API/Node/contains
* @param {[Object]} node DOM node
* @return {Boolean} The Node.contains() method returns a Boolean value indicating whether a node is a descendant of a given node or not.
*/
u.prototype.isInPage = function isInPage (node) {
return (node === document.body) ? false : document.body.contains(node);
};
// Get the last of the nodes
u.prototype.last = function () {
return this.nodes[this.length - 1] || false;
};
// Merge all of the nodes that the callback returns
u.prototype.map = function (callback) {
return callback ? u(this.array(callback)).unique() : this;
};
// Delete all of the nodes that equals the filter
u.prototype.not = function (filter) {
return this.filter(function (node) {
return !u(node).is(filter || true);
});
};
// Removes the callback to the event listener for each node
u.prototype.off = function (events, cb, cb2) {
var cb_filter_off = (cb == null && cb2 == null);
var sel = null;
var cb_to_be_removed = cb;
if (typeof cb === 'string') {
sel = cb;
cb_to_be_removed = cb2;
}
return this.eacharg(events, function (node, event) {
u(node._e ? node._e[event] : []).each(function (ref) {
if (cb_filter_off || (ref.orig_callback === cb_to_be_removed && ref.selector === sel)) {
node.removeEventListener(event, ref.callback);
}
});
});
};
// Attach a callback to the specified events
u.prototype.on = function (events, cb, cb2) {
function overWriteCurrent (e, value) {
try {
Object.defineProperty(e, 'currentTarget', {
value: value,
configurable: true
});
} catch (err) {}
}
var selector = null;
var orig_callback = cb;
if (typeof cb === 'string') {
selector = cb;
orig_callback = cb2;
cb = function (e) {
var args = arguments;
u(e.currentTarget)
.find(selector)
.each(function (target) {
// The event is triggered either in the correct node, or a child
// of the node that we are interested in
// Note: .contains() will also check itself (besides children)
if (!target.contains(e.target)) return;
// If e.g. a child of a link was clicked, but we are listening
// to the link, this will make the currentTarget the link itself,
// so it's the "delegated" element instead of the root target. It
// makes u('.render a').on('click') and u('.render').on('click', 'a')
// to have the same currentTarget (the 'a')
var curr = e.currentTarget;
overWriteCurrent(e, target);
cb2.apply(target, args);
// Need to undo it afterwards, in case this event is reused in another
// callback since otherwise u(e.currentTarget) above would break
overWriteCurrent(e, curr);
});
};
}
var callback = function (e) {
return cb.apply(this, [e].concat(e.detail || []));
};
return this.eacharg(events, function (node, event) {
node.addEventListener(event, callback);
// Store it so we can dereference it with `.off()` later on
node._e = node._e || {};
node._e[event] = node._e[event] || [];
node._e[event].push({
callback: callback,
orig_callback: orig_callback,
selector: selector
});
});
};
// [INTERNAL USE ONLY]
// Take the arguments and a couple of callback to handle the getter/setter pairs
// such as: .css('a'), .css('a', 'b'), .css({ a: 'b' })
u.prototype.pairs = function (name, value, get, set) {
// Convert it into a plain object if it is not
if (typeof value !== 'undefined') {
var nm = name;
name = {};
name[nm] = value;
}
if (typeof name === 'object') {
// Set the value of each one, for each of the { prop: value } pairs
return this.each(function (node, i) {
for (var key in name) {
if (typeof name[key] === 'function') {
set(node, key, name[key](node, i));
} else {
set(node, key, name[key]);
}
}
});
}
// Return the style of the first one
return this.length ? get(this.first(), name) : '';
};
// [INTERNAL USE ONLY]
// Parametize an object: { a: 'b', c: 'd' } => 'a=b&c=d'
u.prototype.param = function (obj) {
return Object.keys(obj).map(function (key) {
return this.uri(key) + '=' + this.uri(obj[key]);
}.bind(this)).join('&');
};
// Travel the matched elements one node up
u.prototype.parent = function (selector) {
return this.map(function (node) {
return node.parentNode;
}).filter(selector);
};
// Add nodes at the beginning of each node
u.prototype.prepend = function (html, data) {
return this.adjacent(html, data, function (node, fragment) {
node.insertBefore(fragment, node.firstChild);
});
};
// Delete the matched nodes from the DOM
u.prototype.remove = function () {
// Loop through all the nodes
return this.each(function (node) {
// Perform the removal only if the node has a parent
if (node.parentNode) {
node.parentNode.removeChild(node);
}
});
};
// Removes a class from all of the matched nodes
u.prototype.removeClass = function () {
// Loop the combination of each node with each argument
return this.eacharg(arguments, function (el, name) {
// Remove the class using the native method
el.classList.remove(name);
});
};
// Replace the matched elements with the passed argument.
u.prototype.replace = function (html, data) {
var nodes = [];
this.adjacent(html, data, function (node, fragment) {
nodes = nodes.concat(this.slice(fragment.children));
node.parentNode.replaceChild(fragment, node);
});
return u(nodes);
};
// Scroll to the first matched element
u.prototype.scroll = function () {
this.first().scrollIntoView({ behavior: 'smooth' });
return this;
};
// [INTERNAL USE ONLY]
// Select the adecuate part from the context
u.prototype.select = function (parameter, context) {
// Allow for spaces before or after
parameter = parameter.replace(/^\s*/, '').replace(/\s*$/, '');
if (/^</.test(parameter)) {
return u().generate(parameter);
}
return (context || document).querySelectorAll(parameter);
};
// Convert forms into a string able to be submitted
// Original source: http://stackoverflow.com/q/11661187
u.prototype.serialize = function () {
var self = this;
// Store the class in a variable for manipulation
return this.slice(this.first().elements).reduce(function (query, el) {
// We only want to match enabled elements with names, but not files
if (!el.name || el.disabled || el.type === 'file') return query;
// Ignore the checkboxes that are not checked
if (/(checkbox|radio)/.test(el.type) && !el.checked) return query;
// Handle multiple selects
if (el.type === 'select-multiple') {
u(el.options).each(function (opt) {
if (opt.selected) {
query += '&' + self.uri(el.name) + '=' + self.uri(opt.value);
}
});
return query;
}
// Add the element to the object
return query + '&' + self.uri(el.name) + '=' + self.uri(el.value);
}, '').slice(1);
};
// Travel the matched elements at the same level
u.prototype.siblings = function (selector) {
return this.parent().children(selector).not(this);
};
// Find the size of the first matched element
u.prototype.size = function () {
return this.first().getBoundingClientRect();
};
// [INTERNAL USE ONLY]
// Force it to be an array AND also it clones them
// http://toddmotto.com/a-comprehensive-dive-into-nodelists-arrays-converting-nodelists-and-understanding-the-dom/
u.prototype.slice = function (pseudo) {
// Check that it's not a valid object
if (!pseudo ||
pseudo.length === 0 ||
typeof pseudo === 'string' ||
pseudo.toString() === '[object Function]') return [];
// Accept also a u() object (that has .nodes)
return pseudo.length ? [].slice.call(pseudo.nodes || pseudo) : [pseudo];
};
// [INTERNAL USE ONLY]
// Create a string from different things
u.prototype.str = function (node, i) {
return function (arg) {
// Call the function with the corresponding nodes
if (typeof arg === 'function') {
return arg.call(this, node, i);
}
// From an array or other 'weird' things
return arg.toString();
};
};
// Set or retrieve the text content from the matched node(s)
u.prototype.text = function (text) {
// Needs to check undefined as it might be ""
if (text === undefined) {
return this.first().textContent || '';
}
// If we're attempting to set some text
// Loop through all the nodes
return this.each(function (node) {
// Set the text content to the node
node.textContent = text;
});
};
// Activate/deactivate classes in the elements
u.prototype.toggleClass = function (classes, addOrRemove) {
/* jshint -W018 */
// Check if addOrRemove was passed as a boolean
if (!!addOrRemove === addOrRemove) {
return this[addOrRemove ? 'addClass' : 'removeClass'](classes);
}
/* jshint +W018 */
// Loop through all the nodes and classes combinations
return this.eacharg(classes, function (el, name) {
el.classList.toggle(name);
});
};
// Call an event manually on all the nodes
u.prototype.trigger = function (events) {
var data = this.slice(arguments).slice(1);
return this.eacharg(events, function (node, event) {
var ev;
// Allow the event to bubble up and to be cancelable (as default)
var opts = { bubbles: true, cancelable: true, detail: data };
try {
// Accept different types of event names or an event itself
ev = new window.CustomEvent(event, opts);
} catch (e) {
ev = document.createEvent('CustomEvent');
ev.initCustomEvent(event, true, true, data);
}
node.dispatchEvent(ev);
});
};
// [INTERNAL USE ONLY]
// Removed duplicated nodes, used for some specific methods
u.prototype.unique = function () {
return u(this.nodes.reduce(function (clean, node) {
var istruthy = node !== null && node !== undefined && node !== false;
return (istruthy && clean.indexOf(node) === -1) ? clean.concat(node) : clean;
}, []));
};
// [INTERNAL USE ONLY]
// Encode the different strings https://gist.github.com/brettz9/7147458
u.prototype.uri = function (str) {
return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
};
u.prototype.wrap = function (selector) {
function findDeepestNode (node) {
while (node.firstElementChild) {
node = node.firstElementChild;
}
return u(node);
}
// 1) Construct dom node e.g. u('<a>'),
// 2) clone the currently matched node
// 3) append cloned dom node to constructed node based on selector
return this.map(function (node) {
return u(selector).each(function (n) {
findDeepestNode(n)
.append(node.cloneNode(true));
node
.parentNode
.replaceChild(n, node);
});
});
};
// Export it for webpack
if (typeof module === 'object' && module.exports) {
// Avoid breaking it for `import { u } from ...`. Add `import u from ...`
module.exports = u;
module.exports.u = u;
}

View File

@ -1,6 +0,0 @@
# Sitemap
- [gitea](https://gitea.neur0tx.site)
- [proxy](https://proxy.neur0tx.site)
- [goplay](https://goplay.neur0tx.site)
- [minecraft](https://mc.neur0tx.site)

BIN
favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

BIN
favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

5
index.md Normal file
View File

@ -0,0 +1,5 @@
# Links
- [gitea](https://gitea.neur0tx.site)
- [drone CI](https://drone.neur0tx.site)
- [proxy](https://proxy.neur0tx.site)

View File

@ -1,108 +0,0 @@
/*
Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmail.com>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
/* background: #fdf6e3; */
color: #657b83;
-webkit-text-size-adjust: none;
}
.hljs-comment,
.diff .hljs-header,
.hljs-doctype,
.hljs-pi,
.lisp .hljs-string,
.hljs-javadoc {
color: #93a1a1;
}
/* Solarized Green */
.hljs-keyword,
.hljs-winutils,
.method,
.hljs-addition,
.css .hljs-tag,
.hljs-request,
.hljs-status,
.nginx .hljs-title {
color: #859900;
}
/* Solarized Cyan */
.hljs-number,
.hljs-command,
.hljs-string,
.hljs-tag .hljs-value,
.hljs-rules .hljs-value,
.hljs-phpdoc,
.hljs-dartdoc,
.tex .hljs-formula,
.hljs-regexp,
.hljs-hexcolor,
.hljs-link_url {
color: #2aa198;
}
/* Solarized Blue */
.hljs-title,
.hljs-localvars,
.hljs-chunk,
.hljs-decorator,
.hljs-built_in,
.hljs-identifier,
.vhdl .hljs-literal,
.hljs-id,
.css .hljs-function {
color: #268bd2;
}
/* Solarized Yellow */
.hljs-attribute,
.hljs-variable,
.lisp .hljs-body,
.smalltalk .hljs-number,
.hljs-constant,
.hljs-class .hljs-title,
.hljs-parent,
.hljs-type,
.hljs-link_reference {
color: #b58900;
}
/* Solarized Orange */
.hljs-preprocessor,
.hljs-preprocessor .hljs-keyword,
.hljs-pragma,
.hljs-shebang,
.hljs-symbol,
.hljs-symbol .hljs-string,
.diff .hljs-change,
.hljs-special,
.hljs-attr_selector,
.hljs-subst,
.hljs-cdata,
.css .hljs-pseudo,
.hljs-header {
color: #cb4b16;
}
/* Solarized Red */
.hljs-deletion,
.hljs-important {
color: #dc322f;
}
/* Solarized Violet */
.hljs-link_label {
color: #6c71c4;
}
.tex .hljs-formula {
background: #eee8d5;
}

View File

@ -1,42 +0,0 @@
h1,
h2,
h3,
h4,
h5,
h6 {
position: relative;
}
h1:hover .header-link:before,
h2:hover .header-link:before,
h3:hover .header-link:before,
h4:hover .header-link:before,
h5:hover .header-link:before,
h6:hover .header-link:before {
content: "\00B6";/* pilcrow */
color: #888;
font-size: smaller;
}
.header-link {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
position: absolute;
top: 0;
left: -0.7em;
display: block;
padding-right: 1em;
}
h1:hover .header-link,
h2:hover .header-link,
h3:hover .header-link,
h4:hover .header-link,
h5:hover .header-link,
h6:hover .header-link {
display: inline-block;
text-decoration: none;
}

View File

@ -1,306 +0,0 @@
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
audio,
canvas,
video {
display: inline-block;
}
audio:not([controls]) {
display: none;
height: 0;
}
[hidden] {
display: none;
}
html {
font-family: sans-serif;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
body {
margin: 0;
line-height: 1.5rem;
}
a:focus {
outline: thin dotted;
}
a:active,
a:hover {
outline: 0;
}
h1 {
font-size: 2em;
}
abbr[title] {
border-bottom: 1px dotted;
}
b,
strong {
font-weight: bold;
}
dfn {
font-style: italic;
}
mark {
background: #ff0;
color: #000;
}
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
pre {
white-space: pre-wrap;
word-wrap: break-word;
}
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
small {
font-size: 80%;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
img {
border: 0;
}
svg:not(:root) {
overflow: hidden;
}
figure {
margin: 0;
}
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
legend {
border: 0;
padding: 0;
}
button,
input,
select,
textarea {
font-family: inherit;
font-size: 100%;
margin: 0;
}
button,
input {
line-height: normal;
}
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button;
cursor: pointer;
}
button[disabled],
input[disabled] {
cursor: default;
}
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box;
padding: 0;
}
input[type="search"] {
-webkit-appearance: textfield;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
textarea {
overflow: auto;
vertical-align: top;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
@import url(//fonts.googleapis.com/css?family=PT+Sans);
@import url(//fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700);
html {
font-family: 'PT Sans', sans-serif;
}
pre,
code {
font-family: monospace, sans-serif;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: 'PT Sans Narrow', sans-serif;
font-weight: 700;
}
html {
background-color: #002b36;
color: #839496;
margin: 1em;
}
code {
background-color: #073642;
padding: 2px;
}
a {
color: #b58900;
}
a:visited {
color: #cb4b16;
}
a:hover {
color: #cb4b16;
}
h1 {
color: #d33682;
}
h2,
h3,
h4,
h5,
h6 {
color: #859900;
}
pre {
background-color: #002b36;
color: #839496;
border: 1pt solid #586e75;
padding: 1em;
box-shadow: 5pt 5pt 8pt #073642;
}
pre code {
background-color: #002b36;
}
h1 {
font-size: 2.8em;
}
h2 {
font-size: 2.4em;
}
h3 {
font-size: 1.8em;
}
h4 {
font-size: 1.4em;
}
h5 {
font-size: 1.3em;
}
h6 {
font-size: 1.15em;
}
.tag {
background-color: #073642;
color: #d33682;
padding: 0 0.2em;
}
.todo,
.next,
.done {
color: #002b36;
background-color: #dc322f;
padding: 0 0.2em;
}
.tag {
-webkit-border-radius: 0.35em;
-moz-border-radius: 0.35em;
border-radius: 0.35em;
}
.TODO {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #2aa198;
}
.NEXT {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #268bd2;
}
.ACTIVE {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #268bd2;
}
.DONE {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
background-color: #859900;
}
.WAITING {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
foreground-color: #cb4b16;
}
.HOLD {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
foreground-color: #d33682;
}
.NOTE {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
foreground-color: #d33682;
}
.CANCELLED {
-webkit-border-radius: 0.2em;
-moz-border-radius: 0.2em;
border-radius: 0.2em;
foreground-color: #859900;
}
#container {
max-width: 900px;
height: 100%;
margin: 0 auto;
}
hr {
border: 1px solid #657b83;
background: #657b83;
box-sizing: border-box;
}

View File

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="icon" href="{{asset 'favicon.ico'}}" type="image/x-icon"/>
<link type="text/css" rel="stylesheet" href="{{asset 'style.css'}}" />
<link type="text/css" rel="stylesheet" href="{{asset 'pilcrow.css'}}" />
<link type="text/css" rel="stylesheet" href="{{asset 'hljs-solarized-dark.min.css'}}"/>
</head>
<body>
<div id="container">
{{~> content}}
</div>
</body>
</html>

1109
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
{
"name": "neur0tx.site",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"doc": "docs"
},
"scripts": {
"build": "npx generate-md --layout ./layout --input ./docs --output ./html"
},
"author": "Neur0toxine",
"license": "MIT",
"dependencies": {
"markdown-styles": "^3.2.0"
}
}

49
proxy.md Normal file
View File

@ -0,0 +1,49 @@
EN | [RU](proxy_ru.html)
___
# How to use the service
## Easiest method
Add this to your `~/.bashrc` or `~/.zshrc`:
```sh
http () {
ssh -p 2222 -oHostKeyAlgorithms=+ssh-rsa -R 80:localhost:"$1" ssh.neur0tx.site
}
httpa () {
ssh -p 2222 -oHostKeyAlgorithms=+ssh-rsa -R "$1":80:localhost:"$2" ssh.neur0tx.site
}
```
and then run `source ~/.bashrc` or `source ~/.zshrc`.
Now you can forward your services like this:
* `http 80` to forward a service on port `:80` with random subdomain. Service will be available at `<something>.proxy.neur0tx.site`.
* `httpa domain 80` to forward a service on port `:80` with subdomain `domain`. Service will be available at `domain.proxy.neur0tx.site` (if this domain is not taken yet).
## All methods
* Forward service on port `:80` with random subdomain:
```sh
$ ssh -p 2222 -R 80:localhost:80 ssh.neur0tx.site
```
* Forward service on port `:8080` with custom subdomain:
```sh
$ ssh -p 2222 -R subdomain:80:localhost:80 ssh.neur0tx.site
```
* For Fedora you can add `-oHostKeyAlgorithms=+ssh-rsa` before `ssh -p 2222` to prevent any problems with the key compatibility.
* Function for `bash` / `zsh` to use as an alias (usage: `http 80`):
```sh
http () {
ssh -p 2222 -oHostKeyAlgorithms=+ssh-rsa -R 80:localhost:"$1" ssh.neur0tx.site
}
```
* Same function but with custom subdomain (usage: `httpa subdomain 80`):
```sh
httpa () {
ssh -p 2222 -oHostKeyAlgorithms=+ssh-rsa -R "$1":80:localhost:"$2" ssh.neur0tx.site
}
```

49
proxy_ru.md Normal file
View File

@ -0,0 +1,49 @@
[EN](proxy.html) | RU
___
# Инструкция по использованию сервиса
## Самый простой метод
Добавьте код ниже в `~/.bashrc` или `~/.zshrc`:
```sh
http () {
ssh -p 2222 -oHostKeyAlgorithms=+ssh-rsa -R 80:localhost:"$1" ssh.neur0tx.site
}
httpa () {
ssh -p 2222 -oHostKeyAlgorithms=+ssh-rsa -R "$1":80:localhost:"$2" ssh.neur0tx.site
}
```
и выполните `source ~/.bashrc` или `source ~/.zshrc`.
Теперь вы можете проксировать сервисы вот так:
* `http 80` чтобы проксировать сервис на порту `:80` со случайным поддоменом. Сервис будет доступен на `<something>.proxy.neur0tx.site`.
* `httpa domain 80` чтобы проксировать сервис на порту `:80` с поддоменом `domain`. Сервис будет доступен на `domain.proxy.neur0tx.site` (если поддомен еще свободен).
## Все методы
* Проксировать сервис на порту `:80` на случайный поддомен:
```sh
$ ssh -p 2222 -R 80:localhost:80 ssh.neur0tx.site
```
* Проксировать сервис на порту `:8080` на заранее определенный поддомен:
```sh
$ ssh -p 2222 -R subdomain:80:localhost:80 ssh.neur0tx.site
```
* Для Fedora можно использовать аргумент `-oHostKeyAlgorithms=+ssh-rsa` перед `ssh -p 2222` чтобы предотвратить проблемы совместимости с ключом.
* Функции для `bash` / `zsh` чтобы использовать как alias (пример: `http 80`):
```sh
http () {
ssh -p 2222 -oHostKeyAlgorithms=+ssh-rsa -R 80:localhost:"$1" ssh.neur0tx.site
}
```
* Та же функция с кастомным поддоменом (пример: `httpa subdomain 80`):
```sh
httpa () {
ssh -p 2222 -oHostKeyAlgorithms=+ssh-rsa -R "$1":80:localhost:"$2" ssh.neur0tx.site
}
```