Three sites, one directory each

flux.vision, tobias.raayoni-last.com and randogoth.com. They were one site until
October 2026, serving four audiences from a single dated list, which is the only
format that can hold unrelated things at once.

flux.vision is recovered from the Wayback Machine, where it had sat since being
deleted in 2021: 90 articles, closed and maintained rather than updated.
tobias.raayoni-last.com is the professional record, 97 articles back to 1999.
randogoth.com is the technical work, 41 articles across six areas, served as an
ANSI bulletin board.

History starts here deliberately. The development history, the 266-row inventory
that records why each article went where it did, the Wayback pipelines, the
pruned articles, the source archives and the full-size images are kept beside
the repo rather than in it, so a clone is the web presence and not the argument
that produced it. REFACTOR.md is gitignored for the same reason. See README.md
for where that material lives, and keep it: the sort is not reproducible from
these three directories alone.

Each site documents itself in its own README, including the traps that cost time
and look like nothing when you get them wrong.
This commit is contained in:
randogoth 2026-10-05 20:54:17 +03:00
commit 8505b7e9e0
1003 changed files with 13289 additions and 0 deletions

View file

@ -0,0 +1,14 @@
Copyright (c) 2015 Michael Cardell Widerkrantz <mc@hack.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View file

@ -0,0 +1,16 @@
VERSION=20150208
DIST=mc-pelican-$(VERSION)
DISTFILES=README.txt LICENSE Makefile static templates
$(DIST).tar.bz2:
mkdir $(DIST)
cp -r $(DISTFILES) $(DIST)/
tar cf $(DIST).tar --exclude .git $(DIST)
bzip2 -9 $(DIST).tar
$(RM) -rf $(DIST)
dist: $(DIST).tar.bz2
clean:
$(RM) -f $(DIST).tar.bz2

View file

@ -0,0 +1,28 @@
A minimalist theme for the Pelican static blog generator:
http://blog.getpelican.com/
It's based on the built-in simple theme with some small changes to the
templates and some very minimalist CSS.
Install like this:
# pelican-themes -i mc-pelican
Then, in your pelicanconf.py:
THEME = 'mc-pelican'
You add menu items with MENUITEMS like this:
MENUITEMS = (
('Homepage', '/mc/'),
('About MC', '/mc/bio.html'),
('Contact', '/mc/contact.html'),
('Archives', '{0}/archives.html'.format(SITEURL)), )
If you want a blog description in the footer of all pages, define
DESCRIPTION.
Happy blogging,
MC.

Binary file not shown.

View file

@ -0,0 +1,268 @@
@font-face {
font-family: 'Classic Console Neue';
src: local('Classic Console Neue'), local('ClassicConsoleNeue'),
url('clacon2.woff2') format('woff2')
}
/* CSS Reset */
html {
margin: 0;
padding: 0;
text-align: left;
text-rendering: optimizeLegibility;
}
/* HTML5 tag defaults. */
header, footer, aside, nav, article
{
display: block;
}
h1, h2, h3, h4, h5 {
font-weight: normal;
text-align: left;
font-size: 1em;
text-transform: uppercase;
}
body {
font-family: 'Classic Console Neue';
padding-top: 2em;
font-size: 1.4em;
line-height: 1.2em;
text-align: left;
background: #000;
color: #bbb;
height: 100vh;
overflow-x: hidden;
overflow-y: auto;
perspective: 2px;
margin: auto;
max-width: 40em;
}
ul, ul li {
margin:0;
padding:0;
list-style:none
}
ul li::before {
content: '*\00a0';
}
ul li a {
display: inline;
}
/* Use a thin border around all images. */
img
{
border: 1px solid;
max-width: 40em;
width: 100%;
}
/* Navigation bar. */
nav li::before {
content: none;
}
#menu {
top: 0;
left: 0;
right: 0;
}
#menu {
color: #000;
}
#menu a {
background-color: transparent;
color: #000;
padding: 0;
}
#menu ul li:not(:first-child) a::first-letter {
color: rgb(168, 0, 0);
}
/*
* Use a border around tables, but don't draw lines around individual
* cells.
*/
table
{
border-style: solid;
border-collapse: collapse;
border-width: thin;
font-size: 100%;
}
th, td
{
border-style: solid;
border-width: thin;
}
a {
background-color: #0aa;
/* color:#000; */
padding: 0.2em 0.3em 0em 0.3em;
}
a.translation {
background-color: #333;
}
/* for unvisited links: blue */
a:link
{
text-decoration: none;
}
/* for visited links: purple */
a:visited
{
text-decoration: none;
}
/* when mouse is over link or the link is active*/
a:hover, a:active
{
text-decoration: none;
}
/* Very clean ruler with no shadows. */
hr
{
border: none;
}
@media screen and (min-width: 941px) {
hr::after {
content: '--------------------------------------------------------------------------------';
}
}
@media screen and (max-width: 940px) {
hr {
border-top: 2px dashed #bbb;
}
}
.hentry::after {
content: '\00a0'
}
abbr {
display: inline;
text-decoration: none;
padding: 0;
float:left;
}
.hentry .entry-summary p {
margin: 0;
}
.hentry h2 {
margin: 0;
}
.post-info abbr {
float: none;
}
.author {
font-style: normal;
padding: 0;
}
@keyframes cursor-blink {
0% {
opacity: 0;
}
}
#content-window {
background-color: #00f;
display: block;
margin-top:.7em;
}
#root {
display: block;
margin-top: 2em;
margin-bottom: 2em;
}
[contenteditable="true"]:active,
[contenteditable="true"]:focus{
border:none;
outline:none;
caret-color: transparent;
}
[contenteditable="true"]:active:after,
[contenteditable="true"]:focus:after {
content: "\02588";
animation: cursor-blink 1.5s steps(2) infinite;
}
.para {
border: 1px solid;
max-width: 40em;
display: block;
background-attachment: fixed;
background-position: center;
background-size: contain;
}
iframe {
border: 1px solid;
aspect-ratio: 16 / 9;
width: 100%;
}
blockquote {
border: 1px solid;
margin: 0;
padding: 0 1em;
background: #555;
-webkit-hyphens: auto;
-moz-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
word-wrap: break-word;
}
em, strong {
font-style: normal;
color: white;
}
.overlay {
height: 100%;
width: 100%;
position: fixed;
z-index: 1;
left: 0;
overflow-x: hidden;
pointer-events: none;
}
.vignette {
background-image: radial-gradient(ellipse at center, rgba(0,0,0,0) 50%, rgba(0,0,0,0.25) 100%);
}
.scanline {
background-image: linear-gradient(0deg, rgba(0,0,0,0) 25%, rgba(0,0,0,0.33) 25%, rgba(0,0,0,0.33) 50%, rgba(0,0,0,0) 50%, rgba(0,0,0,0) 75%, rgba(0,0,0,0.33) 75%, rgba(0,0,0,0.33) 100%);
background-size: 4px 4px;
}
.tui-fieldset legend {
margin-left: 50px;
padding: 0 5px 0 5px;
}

Binary file not shown.

View file

@ -0,0 +1,62 @@
/* Syntax highlighting. */
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #60a0b0; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #007020 } /* Comment.Preproc */
.highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */
.highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #808080 } /* Generic.Output */
.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0040D0 } /* Generic.Traceback */
.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #007020 } /* Keyword.Pseudo */
.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #902000 } /* Keyword.Type */
.highlight .m { color: #40a070 } /* Literal.Number */
.highlight .s { color: #4070a0 } /* Literal.String */
.highlight .na { color: #4070a0 } /* Name.Attribute */
.highlight .nb { color: #007020 } /* Name.Builtin */
.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
.highlight .no { color: #60add5 } /* Name.Constant */
.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #007020 } /* Name.Exception */
.highlight .nf { color: #06287e } /* Name.Function */
.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #bb60d5 } /* Name.Variable */
.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #40a070 } /* Literal.Number.Float */
.highlight .mh { color: #40a070 } /* Literal.Number.Hex */
.highlight .mi { color: #40a070 } /* Literal.Number.Integer */
.highlight .mo { color: #40a070 } /* Literal.Number.Oct */
.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
.highlight .sc { color: #4070a0 } /* Literal.String.Char */
.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
.highlight .sx { color: #c65d09 } /* Literal.String.Other */
.highlight .sr { color: #235388 } /* Literal.String.Regex */
.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
.highlight .ss { color: #517918 } /* Literal.String.Symbol */
.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */

View file

@ -0,0 +1,173 @@
function pickColors() {
const highlights = [
"#000000", // Black
"#0000AA", // Blue
"#00AA00", // Green
"#00AAAA", // Cyan
"#AA00AA", // Magenta
"#AA5500", // Brown
"#AA0000", // Red
"#555555", // Dark Gray
"#5555FF", // Bright Blue
"#55FF55", // Bright Green
"#55FFFF", // Bright Cyan
"#FF5555", // Bright Red
"#FF55FF", // Bright Magenta
"#FFFF55", // Bright Yellow
];
return {
// background: backgrounds[Math.floor(Math.random() * backgrounds.length)],
highlight: highlights[Math.floor(Math.random() * highlights.length)],
};
}
const { background } = pickColors();
document.querySelector('.home').style.backgroundColor = background;
const blogTitle = document.querySelector('#blog-title').firstElementChild;
blogTitle.style.color = "#FFFF55"; // Bright Yellow
// setInterval(() => {
// blogTitle.style.color = pickColors().highlight;
// }, 120);
let scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
let paras = document.getElementsByClassName("para");
for (let i = 0; i < paras.length; i++) {
var imageSrc = paras[i].style.backgroundImage.replace(/url\((['"])?(.*?)\1\)/gi, '$2').split(',')[0];
var image = new Image();
image.src = imageSrc;
if ( screen.width > screen.height ) {
paras[i].setAttribute("offset", paras[i].getBoundingClientRect().bottom - image.height * 0.5);
} else {
paras[i].parentNode.insertBefore(image, paras[i].nextSibling);
paras[i].style.display = 'none';
}
}
window.addEventListener('scroll', () => {
let { scrollY } = window;
if ( screen.width > screen.height ) {
for (let i = 0; i < paras.length; i++) {
paras[i].style.backgroundPositionY = paras[i].getAttribute("offset") * 0.1 + -0.1 * scrollY + 'px';
}
}
});
// //components
// //console component
// class Console{
// comps=[];
// input_received=false;
// blocking_interval=null;
// text_color="#bbb";
// constructor(root){
// this.elem=document.createElement("span");
// this.elem.className="console";
// root.appendChild(this.elem);
// this.resetTextColor();
// }
// appendComponent(comp){
// this.comps.push(comp);
// this.elem.appendChild(comp.elem);
// }
// writeLine(txt){
// let text=new Text(txt,this.text_color);
// this.appendComponent(text);
// }
// async readLine(txt){
// let text=new Text(txt,this.text_color);
// this.appendComponent(text);
// let input=new Input(this.text_color);
// this.appendComponent(input);
// input.elem.focus({ preventScroll: true});
// return new Promise(resolve=>{
// this.blocking_interval=setInterval(()=>{
// if(this.input_received){
// this.input_received=false;
// clearInterval(this.blocking_interval);
// resolve(input.elem.innerText);
// }
// },500);
// })
// }
// setTextColor(color){
// this.text_color=color;
// }
// resetTextColor(){
// this.text_color="#bbb";
// }
// //helpers
// get lastComp(){return this.comps[this.comps.length-1]};
// }
// //text component
// class Text{
// constructor(txt,color){
// this.elem=document.createElement("span");
// this.elem.className="text";
// this.elem.style.color=color;
// this.elem.innerText=txt;
// }
// }
// //input component
// class Input{
// constructor(color){
// this.elem=document.createElement("span");
// this.elem.style.color=color;
// this.elem.contentEditable=true;
// this.elem.addEventListener('keydown',function(e){
// let key=e.key;
// if(key=="Enter"){
// e.preventDefault();
// cmd.input_received=true;
// cmd.lastComp.elem.contentEditable=false;
// cmd.lastComp.elem.style.borderWidth="0px";
// }
// });
// }
// }
// /************************************/
// let root=document.getElementById("root");
// let cmd=new Console(root);
// let prompt = "C:\\>"
// async function execute(){
// let command= await cmd.readLine(prompt);
// if ( command == "projects" ) window.location = "/current.html";
// else if ( command == "blog" ) window.location = "/blog";
// else if ( command == "archive" ) window.location = "/archive";
// else if ( command == "dir" ) window.location = "/archive";
// else if ( command == "home" ) window.location = "/";
// else if ( command == "connect" ) window.location = "/connect.html";
// else if ( command == "reboot" ) location.reload();
// else if ( command == "a:" ) {
// prompt = "A:\\>";
// cmd.writeLine("\n");
// cmd=new Console(root);
// execute();
// }
// else if ( command == "b:" ) {
// prompt = "B:\\>";
// cmd.writeLine("\n");
// cmd=new Console(root);
// execute();
// }
// else if ( command == "c:" ) {
// prompt = "C:\\>";
// cmd.writeLine("\n");
// cmd=new Console(root);
// execute();
// }
// else {
// cmd.writeLine("\nSyntax error\n");
// cmd=new Console(root);
// execute();
// }
// }
// execute();

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,256 @@
/**
* Replacement for jQuery's $(document).ready() function.
* This is handy in making sure stuff fires after the DOM is ready to be touched.
* Stolen from:https://stackoverflow.com/a/53601942/344028
*
* @param fn Callback.
*/
function domReady(fn) {
// If we're early to the party
document.addEventListener('DOMContentLoaded', fn);
// If late; I mean on time.
if (document.readyState === 'interactive' || document.readyState === 'complete') {
fn();
}
}
/**
* TuiTabs controller
*/
function tabsController() {
// Get all the tab elements (typically li tags).
const tabs = document.getElementsByClassName('tui-tab');
if (!tabs.length) {
// No tabs found, return early and save a couple CPU cycles.
return;
}
for (const tab of tabs) {
// Add click listeners to them.
tab.addEventListener('click', function (e) {
// Check if the clicked tab is disabled
if(e.target.classList.contains("disabled")) {
return;
}
// Remove the 'active' class from any and all tabs.
for (const otherTab of tabs) {
otherTab.classList.remove('active');
}
// Get the content element.
const tabContents = document.getElementsByClassName('tui-tab-content');
if (tabContents) {
for (const tabContent of tabContents) {
// Hide all tab contents.
tabContent.style.display = 'none';
}
} else {
throw 'No tab content elements found.'
}
// Get the id of the tab contents we want to show.
const tabContentId = e.target.getAttribute('data-tab-content');
if (tabContentId) {
const tabContent = document.getElementById(tabContentId);
if (tabContent) {
// Show the tab contents.
tabContent.style.display = 'block';
} else {
throw 'No tab content element with id "' + tabContentId + '" found.';
}
}
// We are not going to throw an error here, since we could make the tab do something else that a tab
// normally wouldn't do.
// Set the clicked tab to have the 'active' class so we can use it in the next part.
e.target.classList.add('active');
});
}
// Grab the first tab with the active class.
const activeTab = document.querySelector('.tui-tab.active');
if (activeTab) {
// Now click it 'click' it.
activeTab.click();
} else {
// Nothing found, just click the first tab foud.
tabs[0].click()
}
}
/**
* Date/time field controller
*/
function datetimeController() {
// Get date/time elements.
const clocks = document.getElementsByClassName('tui-datetime');
if (!clocks.length) {
// No date time elements found, return early and save a couple CPU cycles.
return;
}
// Kick off our clock interval stuff.
datetimeInterval();
// Synchronize time and set interval to control the clocks
setTimeout(() => {
setInterval(datetimeInterval, 1000);
}, 1000 - new Date().getMilliseconds());
function datetimeInterval() {
for (const clock of clocks) {
if (clock === null) {
continue;
}
// Get the format we want to display in the element.
let format = clock.getAttribute('data-format');
// parse out the date and time into constants.
const today = new Date();
const month = (today.getMonth() + 1).toString().padStart(2, '0');
const day = today.getDate().toString().padStart(2, '0');
const dayOfWeek = (today.getDay() + 1).toString().padStart(2, '0');
const year = today.getFullYear().toString();
const hour = today.getHours().toString().padStart(2, '0');
const hour12 = (parseInt(hour) + 24) % '12' || '12';
const minute = today.getMinutes().toString().padStart(2, '0');
const second = today.getSeconds().toString().padStart(2, '0');
const ampm = parseInt(hour) >= 12 ? 'PM' : 'AM';
// Replace based on the format.
format = format.replace('M', month);
format = format.replace('d', day);
format = format.replace('e', dayOfWeek);
format = format.replace('y', year);
format = format.replace('H', hour);
format = format.replace('h', hour12);
format = format.replace('m', minute);
format = format.replace('s', second);
format = format.replace('a', ampm);
// Show it in the element.
clock.innerHTML = format;
}
}
}
/**
* Sidenav Controller
* There should only side navigation element at the moment.
*/
function sidenavController() {
// Get the side navigation button (there should be only one, but if not, we are getting the first one).
const sideNavButton = document.querySelector('.tui-sidenav-button');
if (!sideNavButton) {
// No side navigation button found, return early and save a couple CPU cycles.
return;
}
// Add the click event listener to the buttons.
sideNavButton.addEventListener('click', () => {
// Get the side navigation element (there should be only one, but if not, we are getting the first one).
const sideNav = document.querySelector('.tui-sidenav');
if (sideNav) {
if (sideNav.classList.contains('active')) {
sideNav.classList.remove('active');
} else {
sideNav.classList.add('active');
}
} else {
throw 'No sidenav element found.'
}
});
}
/**
* Modal controller
*/
function modalController() {
// Get the overlap (overlay) element (there should be only one, but if not, we are getting the first one).
const tuiOverlap = document.querySelector('.tui-overlap');
if (!tuiOverlap) {
// No overlap found element, return early and save a couple CPU cycles.
return;
}
// Find modal buttons.
const modalButtons = document.getElementsByClassName('tui-modal-button');
for (const modalButton of modalButtons) {
// Add the click event listener to the buttons.
modalButton.addEventListener('click', (e) => {
// Show the overlap.
tuiOverlap.classList.add('active');
// Get the display element for the modal.
const modalId = e.target.getAttribute('data-modal');
if (modalId) {
const modal = document.getElementById(modalId);
if (modal) {
// Show it.
modal.classList.add('active');
} else {
throw 'No modal element with id of "' + modalId + '" found.';
}
} else {
throw 'Modal close button data-modal attribute is empty or not set.'
}
});
}
// Find the close modal buttons.
const modalCloseButtons = document.getElementsByClassName('tui-modal-close-button');
if (modalButtons.length > 0 && !modalCloseButtons.length) {
// A modal without a close button, is a bad modal.
throw 'No modal close buttons found.'
}
for (const modalCloseButton of modalCloseButtons) {
// Add the click event listener to the buttons.
modalCloseButton.addEventListener('click', (e) => {
// Hide the the overlap.
tuiOverlap.classList.remove('active');
// Get the display element id for the modal.
const modalId = e.target.getAttribute('data-modal');
if (modalId) {
// Get the modal element.
const modal = document.getElementById(modalId);
if (modal) {
// Hide it.
modal.classList.remove('active');
} else {
throw 'No modal element with id of "' + modalId + '" found.';
}
} else {
throw 'Modal close button data-modal attribute is empty or not set.'
}
});
}
}
/**
* Init: This is at the bottom to make sure it is fired correctly.
*/
domReady(function () {
tabsController();
datetimeController();
sidenavController();
modalController();
});

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
function domReady(t){document.addEventListener("DOMContentLoaded",t),"interactive"!==document.readyState&&"complete"!==document.readyState||t()}function tabsController(){const t=document.getElementsByClassName("tui-tab");if(!t.length)return;for(const e of t)e.addEventListener("click",function(e){if(e.target.classList.contains("disabled"))return;for(const e of t)e.classList.remove("active");const o=document.getElementsByClassName("tui-tab-content");if(!o)throw"No tab content elements found.";for(const t of o)t.style.display="none";const n=e.target.getAttribute("data-tab-content");if(n){const t=document.getElementById(n);if(!t)throw'No tab content element with id "'+n+'" found.';t.style.display="block"}e.target.classList.add("active")});const e=document.querySelector(".tui-tab.active");e?e.click():t[0].click()}function datetimeController(){const t=document.getElementsByClassName("tui-datetime");function e(){for(const e of t){if(null===e)continue;let t=e.getAttribute("data-format");const o=new Date,n=(o.getMonth()+1).toString().padStart(2,"0"),a=o.getDate().toString().padStart(2,"0"),c=(o.getDay()+1).toString().padStart(2,"0"),s=o.getFullYear().toString(),i=o.getHours().toString().padStart(2,"0"),l=(parseInt(i)+24)%"12"||"12",r=o.getMinutes().toString().padStart(2,"0"),d=o.getSeconds().toString().padStart(2,"0"),u=parseInt(i)>=12?"PM":"AM";t=(t=(t=(t=(t=(t=(t=(t=(t=t.replace("M",n)).replace("d",a)).replace("e",c)).replace("y",s)).replace("H",i)).replace("h",l)).replace("m",r)).replace("s",d)).replace("a",u),e.innerHTML=t}}t.length&&(e(),setTimeout(()=>{setInterval(e,1e3)},1e3-(new Date).getMilliseconds()))}function sidenavController(){const t=document.querySelector(".tui-sidenav-button");t&&t.addEventListener("click",()=>{const t=document.querySelector(".tui-sidenav");if(!t)throw"No sidenav element found.";t.classList.contains("active")?t.classList.remove("active"):t.classList.add("active")})}function modalController(){const t=document.querySelector(".tui-overlap");if(!t)return;const e=document.getElementsByClassName("tui-modal-button");for(const o of e)o.addEventListener("click",e=>{t.classList.add("active");const o=e.target.getAttribute("data-modal");if(!o)throw"Modal close button data-modal attribute is empty or not set.";{const t=document.getElementById(o);if(!t)throw'No modal element with id of "'+o+'" found.';t.classList.add("active")}});const o=document.getElementsByClassName("tui-modal-close-button");if(e.length>0&&!o.length)throw"No modal close buttons found.";for(const e of o)e.addEventListener("click",e=>{t.classList.remove("active");const o=e.target.getAttribute("data-modal");if(!o)throw"Modal close button data-modal attribute is empty or not set.";{const t=document.getElementById(o);if(!t)throw'No modal element with id of "'+o+'" found.';t.classList.remove("active")}})}domReady(function(){tabsController(),datetimeController(),sidenavController(),modalController()});

View file

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="english">
<head>
<title>404</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/theme/local.css" rel="stylesheet">
<style>
body {
color: white;
background-color: blue;
display: table;
height: 100vh;
max-width: 100%;
overflow: hidden;
margin: 0;
}
a {
color: blue
}
div {
display: table-cell;
height: 100%;
vertical-align: middle;
}
</style>
</head>
<body id="index">
<div id="404">
<center><a>Windows</a></center>
<p>An exception 404 has occurred. The page could not be found. It may be possible to continue normally.</p>
<ul>
<li>Press the backspace key to attempt to continue.</li>
<li>Press CTRL+ALT+DEL to restart your computer. You will<br />
lose any unsaved information in all applications.</li>
</ul>
</div>
</body>
</html>

View file

@ -0,0 +1,34 @@
{% extends "base.html" %}
{% block content %}
<section id="content">
{% block content_title %}
{% endblock %}
{% set icon_map = {
'gonzo': 'nf-md-typewriter',
'place': 'nf-fa-map',
'random': 'nf-fa-dice_d20',
'txt': 'nf-cod-terminal',
'machines': 'nf-cod-circuit_board',
'endtimes': 'nf-md-radioactive',
'infra': 'nf-fa-network_wired',
'odds': 'nf-cod-folder'
} %}
{% for article in dates %}
<article class="hentry">
<header>
<h2 class="entry-title">
{% set icon_class = icon_map.get(article.category|string|lower) %}
{% if icon_class %}<i class="nf {{ icon_class }}"></i>{% endif %}
<a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title|striptags }}">{{ article.title }}</a>
</h2>
<abbr class="published" title="{{ article.date.isoformat() }}">[{{ article.locale_date }}]&nbsp;</abbr>
<span class="entry-summary">{{ article.summary }}</span>
</header>
</article>
{% endfor %}
</section><!-- /#content -->
{% endblock content %}

View file

@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="{{ DEFAULT_LANG }}">
<head>
<base target="_top">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ SITENAME }}</title>
<link rel="shortcut icon" type="image/x-icon" href="/images/favicon.ico" />
<link rel="stylesheet" href="{{ SITEURL }}/theme/local.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=" crossorigin=""/>
<style>
html, body { height: 100%; margin: 0; padding: 0; }
#map {
height: 100%;
width: 100vw;
}
#map img {
border: none
}
.info {
padding: 6px 8px;
background: white;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h4 {
font-size: 1.6em;
margin: 0 0 5px;
color: #777;
}
.info span {
line-height: 18px;
vertical-align: top;
}
</style>
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js" integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=" crossorigin=""></script>
</head>
<body>
<script>
var mapArticles = {
"type": "FeatureCollection",
"title": "{{ SITENAME }}",
"features": [
{% for article in dates %}
{% if article.metadata['lat'] and article.metadata['lon'] %}
{
"type": "Feature",
"id": "{{ article.slug }}",
"geometry": {
"type": "Point",
"coordinates": [{{ article.metadata['lon'] }}, {{ article.metadata['lat'] }}]
},
"properties": {
"title": "{{ article.title }}",
"url": "{{ SITEURL }}/{{ article.url }}"
},
},
{% endif %}
{% endfor %}
]
};
</script>
<div id='map'></div>
<script>
const showArticles = L.geoJSON(mapArticles, {
pointToLayer(feature, latlng) { return L.marker(latlng);},
onEachFeature
});
const map = L.map('map').fitBounds(showArticles.getBounds());
const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
showArticles.addTo(map);
function onEachFeature(feature, layer) {
let popupContent = ``;
if (feature.properties && feature.properties.title) {
popupContent += '<a href="'+feature.properties.url+'">' + feature.properties.title + '</a>';
}
layer.bindPopup(popupContent);
}
var info = L.control({position: 'topright'});
var download = L.control({position: 'bottomleft'});
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this._div.innerHTML = '<h4>' + mapArticles["title"] + '</h4>';
return this._div;
};
download.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this._div.innerHTML = 'Download <a href="{{ SITEURL }}/articles.gpx">GPX</a> <a href="{{ SITEURL }}/articles.kml">KML</a>';
return this._div;
};
info.addTo(map);
download.addTo(map);
</script>
</body>
</html>

View file

@ -0,0 +1,57 @@
{% extends "base.html" %}
{% block head %}
{{ super() }}
{% for keyword in article.keywords %}
<meta name="keywords" contents="{{keyword}}" />
{% endfor %}
{% for description in article.description %}
<meta name="description" contents="{{description}}" />
{% endfor %}
{% for tag in article.tags %}
<meta name="tags" contents="{{tag}}" />
{% endfor %}
{% endblock %}
{% set icon_map = {
'performance': 'nf-md-projector',
'cinema': 'nf-md-theater',
'event': 'nf-fa-masks_theater',
'audio': 'nf-fa-music',
'cooperation': 'nf-fa-group',
'software': 'nf-md-code_braces',
'workshop': 'nf-md-school',
'blog': 'nf-fa-newspaper',
'installation': 'nf-fa-cubes',
'hardware': 'nf-cod-circuit_board'
} %}
{% block legend %}{{ article.title|upper }}{% endblock %}
{% block content %}
<section id="content" class="body">
<header>
<h2 class="entry-title">
{% set icon_class = icon_map.get(article.category|string|lower) %}
{% if icon_class %}<i class="nf {{ icon_class }}"></i>{% endif %}
<a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark"
title="Permalink to {{ article.title|striptags }}">{{ article.title }}</a></h2>
{% import 'translations.html' as translations with context %}
{{ translations.translations_for(article) }}
</header>
<footer class="post-info">
<abbr class="published" title="{{ article.date.isoformat() }}">
{{ article.locale_date }}
</abbr>
{% if article.modified %}
<abbr class="modified" title="{{ article.modified.isoformat() }}">
{{ article.locale_modified }}
</abbr>
{% endif %}
</footer><!-- /.post-info -->
<div class="entry-content">
{{ article.content }}
</div><!-- /.entry-content -->
</section>
{% endblock %}

View file

@ -0,0 +1,7 @@
{% extends "index.html" %}
{% block title %}{{ SITENAME }} - Articles by {{ author }}{% endblock %}
{% block content_title %}
<h2>Articles by {{ author }}</h2>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block title %}{{ SITENAME }} - Authors{% endblock %}
{% block content %}
<h1>Authors on {{ SITENAME }}</h1>
<ul>
{%- for author, articles in authors|sort %}
<li><a href="{{ SITEURL }}/{{ author.url }}">{{ author }}</a> ({{ articles|count }})</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang="{{ DEFAULT_LANG }}">
<head>
{% block head %}
<title>{% block title %}{{ SITENAME }}{% endblock title %}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% if FEED_ALL_ATOM %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Full Atom Feed" />
{% endif %}
{% if FEED_ALL_RSS %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Full RSS Feed" />
{% endif %}
{% if FEED_ATOM %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Atom Feed" />
{% endif %}
{% if FEED_RSS %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
{% endif %}
{% if CATEGORY_FEED_ATOM and category %}
<link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_ATOM.format(slug=category.slug) }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Categories Atom Feed" />
{% endif %}
{% if CATEGORY_FEED_RSS and category %}
<link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_RSS.format(slug=category.slug) }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Categories RSS Feed" />
{% endif %}
{% if TAG_FEED_ATOM and tag %}
<link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_ATOM.format(slug=tag.slug) }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Tags Atom Feed" />
{% endif %}
{% if TAG_FEED_RSS and tag %}
<link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_RSS.format(slug=tag.slug) }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Tags RSS Feed" />
{% endif %}
<link href="{{ SITEURL }}/theme/tuicss/tuicss.min.css" rel="stylesheet">
<link href="{{ SITEURL }}/theme/local.css" rel="stylesheet">
<link href="{{ SITEURL }}/theme/pygments.css" rel="stylesheet">
<link href="{{ SITEURL }}/theme/symbols.css" rel="stylesheet">
<link rel="me" href="https://mas.to/@randogoth">
{% endblock head %}
</head>
<body id="index" class="home">
<nav id="menu" class="tui-nav">
<span class="tui-datetime" data-format="h:m:s a"></span>
<ul>
<li id="blog-title"><a href="{{ SITEURL }}/">{{ SITENAME }}</a></li>
{% for title, link in MENUITEMS %}
<li><a href="{{ link }}">{{ title }}</a></li>
{% endfor %}
{% if DISPLAY_PAGES_ON_MENU %}
{% for p in PAGES %}
<li{% if p == page %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ p.url }}">{{ p.title }}</a></li>
{% endfor %}
{% else %}
{% if DISPLAY_CATEGORIES_ON_MENU %}
{% for cat, null in categories %}
<li{% if cat == category %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li>
{% endfor %}
{% endif %}
{% endif %}
{% if FEED_ALL_RSS %}
<li><a href="{{ FEED_DOMAIN }}/{{ FEED_ALL_RSS }}">RSS</a></li>
{% endif %}
{% if FEED_ALL_ATOM %}
<li><a href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}">Atom</a></li>
{% endif %}
</ul>
</nav><!-- /#menu -->
<div class="tui-window full-width tui-no-shadow" id="content-window">
<fieldset class="tui-fieldset">
<legend class="center">{% block legend %}{{ SITENAME }}{% endblock %}</legend>
<span class="tui-fieldset-button"><span class="green-255-text">↕</span></span>
<span class="tui-fieldset-button left"><span class="green-255-text">■</span></span>
{% block content %}
{% endblock %}
</fieldset>
</div>
{% if DESCRIPTION %}
<footer id="contentinfo" class="body">
<hr>
{{ DESCRIPTION }}
</footer><!-- /#contentinfo -->
{% endif %}
<span id="root">&nbsp;</span>
<script src="{{ SITEURL }}/theme/tuicss/tuicss.min.js"></script>
{% if JS_OVERRIDE %}
<!-- Script specified by the user -->
{% for js in JS_OVERRIDE %}
<script type="text/javascript" src="{{ SITEURL }}/{{ js }}"></script>
{% endfor %}
{% endif %}
</body>
</html>

View file

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% set icon_map = {
'gonzo': 'nf-md-typewriter',
'place': 'nf-fa-map',
'random': 'nf-fa-dice_d20',
'txt': 'nf-cod-terminal',
'machines': 'nf-cod-circuit_board',
'endtimes': 'nf-md-radioactive',
'infra': 'nf-fa-network_wired',
'odds': 'nf-cod-folder'
} %}
{% block content %}
<ul>
{% for category, articles in categories %}
<li>
{% set icon_class = icon_map.get(category|string|lower) %}
{% if icon_class %}<i class="nf {{ icon_class }}"></i>{% endif %}
<a href="{{ SITEURL }}/{{ category.url }}">{{ category }}</a>
</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,5 @@
{% extends "index.html" %}
{% block content_title %}
<h2>{{ category }}</h2>
{% endblock %}

View file

@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block content %}
<section id="content">
{% block content_title %}
{% endblock %}
{% set icon_map = {
'gonzo': 'nf-md-typewriter',
'place': 'nf-fa-map',
'random': 'nf-fa-dice_d20',
'txt': 'nf-cod-terminal',
'machines': 'nf-cod-circuit_board',
'endtimes': 'nf-md-radioactive',
'infra': 'nf-fa-network_wired',
'odds': 'nf-cod-folder'
} %}
{% for article in articles_page.object_list %}
<article class="hentry">
<header>
<h2 class="entry-title">
{% set icon_class = icon_map.get(article.category|string|lower) %}
{% if icon_class %}<i class="nf {{ icon_class }}"></i>{% endif %}
<a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title|striptags }}">{{ article.title }}</a>
</h2>
<abbr class="published" title="{{ article.date.isoformat() }}">[{{ article.locale_date }}]&nbsp;</abbr>
<span class="entry-summary">{{ article.summary }}</span>
</header>
</article>
{% endfor %}
{% include 'pagination.html' %}
</section><!-- /#content -->
{% endblock content %}

View file

@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block title %}{{ page.title }}{%endblock%}
{% block legend %}{{ page.title|upper }}{% endblock %}
{% block content %}
<h1>{{ page.title }}</h1>
{% import 'translations.html' as translations with context %}
{{ translations.translations_for(page) }}
{{ page.content }}
{% if page.modified %}
<p>
Last updated: {{ page.locale_modified }}
</p>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,11 @@
{% if DEFAULT_PAGINATION and articles_page and articles_paginator.num_pages > 1 %}
<p class="paginator">
{% if articles_page.has_previous() %}
<a href="{{ SITEURL }}/{{ articles_previous_page.url }}">&laquo;</a>
{% endif %}
Page {{ articles_page.number }} / {{ articles_paginator.num_pages }}
{% if articles_page.has_next() %}
<a href="{{ SITEURL }}/{{ articles_next_page.url }}">&raquo;</a>
{% endif %}
</p>
{% endif %}

View file

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block content %}
<h1>Archives for {{ period | reverse | join(' ') }}</h1>
<dl>
{% for article in dates %}
<dt>{{ article.locale_date }}</dt>
<dd><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></dd>
{% endfor %}
</dl>
{% endblock %}

View file

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block title %}{{ SITENAME }} - Tags{% endblock %}
{% block content %}
<h1>Tags for {{ SITENAME }}</h1>
{%- for tag, articles in tags|sort %}
<li><a href="{{ SITEURL }}/{{ tag.url }}">{{ tag }}</a> ({{ articles|count }})</li>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,8 @@
{% macro translations_for(article) %}
{% if article.translations %}
{% for translation in article.translations %}
<a class="translation" href="{{ SITEURL }}/{{ translation.url }}">{{ translation.lang }}</a>
{% endfor %}
{% endif %}
{% endmacro %}