This commit is contained in:
2026-07-12 10:15:22 +02:00
parent 9d7a3f6660
commit 9d3d33251a
2 changed files with 54 additions and 24 deletions
+26 -23
View File
@@ -1,18 +1,16 @@
// blog.js Loads and renders blog posts with simple "Read more" toggle for long entries
// blog.js Loads blog posts and uses a gradient fade for long entries
const BLOG_DIR = 'blog/';
const MANIFEST = BLOG_DIR + 'manifest.json';
// ---------- helpers ----------
// crude HTML parser for directory listings (when manifest is missing)
function parseIndexHtmlList(html) {
const hrefs = [];
const re = /href="([^"]+)"/g;
let m;
while ((m = re.exec(html)) !== null) {
const name = m[1];
// accept typical text file extensions (and no extension)
if (name.match(/\.(txt|md|text)$/i) || !name.includes('.')) {
if (!name.endsWith('/')) hrefs.push(name);
}
@@ -20,7 +18,6 @@ function parseIndexHtmlList(html) {
return hrefs;
}
// fetch list of blog post filenames (manifest first, fallback to directory listing)
async function loadPostList() {
try {
const r = await fetch(MANIFEST, { cache: 'no-cache' });
@@ -39,7 +36,6 @@ async function loadPostList() {
return [];
}
// fetch a single post file
async function fetchPost(filename) {
const res = await fetch(BLOG_DIR + filename, { cache: 'no-cache' });
if (!res.ok) throw new Error(`Failed to fetch ${filename}`);
@@ -50,9 +46,7 @@ async function fetchPost(filename) {
function renderPost(filename, content) {
const lines = content.split('\n');
if (lines.length < 3) {
return null; // malformed
}
if (lines.length < 3) return null;
const dateTime = lines[0].trim();
const title = lines[1].trim();
const bodyLines = lines.slice(2);
@@ -75,31 +69,41 @@ function renderPost(filename, content) {
const isLong = body.length > 300;
// Body container (always created, but hidden for long posts)
// Container for the body (with optional fade)
const wrapper = document.createElement('div');
wrapper.className = 'blog-body-wrapper';
// The actual text
const bodyDiv = document.createElement('div');
bodyDiv.className = 'blog-body';
bodyDiv.style.whiteSpace = 'pre-line';
bodyDiv.textContent = body;
wrapper.appendChild(bodyDiv);
if (!isLong) {
// Short post show body directly
postDiv.appendChild(bodyDiv);
} else {
// Long post hide body initially, add toggle button
bodyDiv.style.display = 'none';
postDiv.appendChild(bodyDiv);
// If long, add collapsed class and a toggle button
if (isLong) {
wrapper.classList.add('collapsed');
// Toggle button
const toggleBtn = document.createElement('button');
toggleBtn.className = 'blog-toggle';
toggleBtn.textContent = 'Read more';
postDiv.appendChild(toggleBtn);
toggleBtn.addEventListener('click', function() {
const isHidden = bodyDiv.style.display === 'none';
bodyDiv.style.display = isHidden ? 'block' : 'none';
this.textContent = isHidden ? 'Show less' : 'Read more';
const isCollapsed = wrapper.classList.contains('collapsed');
if (isCollapsed) {
wrapper.classList.remove('collapsed');
this.textContent = 'Show less';
} else {
wrapper.classList.add('collapsed');
this.textContent = 'Read more';
}
});
postDiv.appendChild(wrapper);
postDiv.appendChild(toggleBtn);
} else {
// Short post no collapse, no button
postDiv.appendChild(wrapper);
}
return postDiv;
@@ -120,8 +124,7 @@ async function initBlog() {
return;
}
files.sort(); // alphabetical -> chronological
files.sort();
feed.innerHTML = '';
for (const file of files) {