oops fixed

This commit is contained in:
2026-07-11 23:01:25 +02:00
parent 265538a974
commit 37200da79e
2 changed files with 132 additions and 0 deletions
+126
View File
@@ -1,3 +1,129 @@
const BLOG_DIR = 'blog/';
const ATTACHMENT_DIR = BLOG_DIR + 'files/';
const MANIFEST = BLOG_DIR + 'manifest.json';
const TXT_EXT = /\.(txt|text)$/i;
const ATTACHMENT_RE = /\[\[(?:file|attachment):\s*([^\]]+)\]\]/gi;
const BLOG_COLLAPSE_THRESHOLD = 300; // characters before collapsing
const BLOG_PREVIEW_LINES = 3; // number of lines shown in preview
function escapeHtml(value) {
return value
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function parseIndexHtmlList(text) {
const hrefs = [];
const re = /href="([^"]+)"/g;
let m;
while ((m = re.exec(text)) !== null) {
const name = m[1];
if (TXT_EXT.test(name)) hrefs.push(name);
}
return hrefs;
}
async function loadBlogFiles() {
try {
const response = await fetch(MANIFEST, { cache: 'no-cache' });
if (response.ok) {
return await response.json();
}
} catch (e) {
// manifest missing or unreadable
}
try {
const response = await fetch(BLOG_DIR, { cache: 'no-cache' });
if (response.ok) {
const text = await response.text();
return parseIndexHtmlList(text);
}
} catch (e) {
console.warn('Could not read blog directory listing', e);
}
return [];
}
function parseDateLine(rawDate) {
if (!rawDate) return null;
let value = rawDate.trim();
if (value.toLowerCase().startsWith('date:')) {
value = value.slice(5).trim();
}
if (!value) return null;
const normalized = value.replace(' ', 'T');
const date = new Date(normalized);
return Number.isNaN(date.getTime()) ? null : date;
}
function extractAttachments(body) {
const attachments = new Set();
body.replace(ATTACHMENT_RE, (_, filename) => {
const safeName = filename.trim();
if (safeName) attachments.add(safeName);
return '';
});
return Array.from(attachments);
}
function parseBlogText(text, filename) {
const lines = text.replace(/\r/g, '').split('\n');
let index = 0;
while (index < lines.length && !lines[index].trim()) index += 1;
const rawDate = index < lines.length ? lines[index].trim() : '';
if (rawDate.toLowerCase().startsWith('date:')) {
index += 1;
} else {
index += 1;
}
while (index < lines.length && !lines[index].trim()) index += 1;
const title = index < lines.length ? lines[index].trim() : 'Untitled';
index += 1;
while (index < lines.length && !lines[index].trim()) index += 1;
const body = lines.slice(index).join('\n').trim();
return {
filename,
title: title || 'Untitled',
date: parseDateLine(rawDate) || null,
dateLabel: rawDate || '',
body: body || '',
attachments: extractAttachments(body),
};
}
function formatDate(date) {
if (!date) return 'Unknown date';
return new Intl.DateTimeFormat('en-US', {
year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit'
}).format(date);
}
function createParagraphs(body) {
return body
.split(/\n{2,}/)
.filter(Boolean)
.map((paragraph) => {
const p = document.createElement('p');
const text = escapeHtml(paragraph).replace(ATTACHMENT_RE, (match, filename) => {
const safeName = filename.trim();
const url = ATTACHMENT_DIR + encodeURI(safeName);
return `<a href="${url}" target="_blank" rel="noopener noreferrer">${escapeHtml(safeName)}</a>`;
});
const lines = text.split('\n');
p.innerHTML = lines.join('<br>');
return p;
});
}
function renderBlogEntries(entries) {
const container = document.querySelector('.blog-feed');
container.innerHTML = '';