bloggg
This commit is contained in:
+26
-23
@@ -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 BLOG_DIR = 'blog/';
|
||||||
const MANIFEST = BLOG_DIR + 'manifest.json';
|
const MANIFEST = BLOG_DIR + 'manifest.json';
|
||||||
|
|
||||||
// ---------- helpers ----------
|
// ---------- helpers ----------
|
||||||
|
|
||||||
// crude HTML parser for directory listings (when manifest is missing)
|
|
||||||
function parseIndexHtmlList(html) {
|
function parseIndexHtmlList(html) {
|
||||||
const hrefs = [];
|
const hrefs = [];
|
||||||
const re = /href="([^"]+)"/g;
|
const re = /href="([^"]+)"/g;
|
||||||
let m;
|
let m;
|
||||||
while ((m = re.exec(html)) !== null) {
|
while ((m = re.exec(html)) !== null) {
|
||||||
const name = m[1];
|
const name = m[1];
|
||||||
// accept typical text file extensions (and no extension)
|
|
||||||
if (name.match(/\.(txt|md|text)$/i) || !name.includes('.')) {
|
if (name.match(/\.(txt|md|text)$/i) || !name.includes('.')) {
|
||||||
if (!name.endsWith('/')) hrefs.push(name);
|
if (!name.endsWith('/')) hrefs.push(name);
|
||||||
}
|
}
|
||||||
@@ -20,7 +18,6 @@ function parseIndexHtmlList(html) {
|
|||||||
return hrefs;
|
return hrefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch list of blog post filenames (manifest first, fallback to directory listing)
|
|
||||||
async function loadPostList() {
|
async function loadPostList() {
|
||||||
try {
|
try {
|
||||||
const r = await fetch(MANIFEST, { cache: 'no-cache' });
|
const r = await fetch(MANIFEST, { cache: 'no-cache' });
|
||||||
@@ -39,7 +36,6 @@ async function loadPostList() {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch a single post file
|
|
||||||
async function fetchPost(filename) {
|
async function fetchPost(filename) {
|
||||||
const res = await fetch(BLOG_DIR + filename, { cache: 'no-cache' });
|
const res = await fetch(BLOG_DIR + filename, { cache: 'no-cache' });
|
||||||
if (!res.ok) throw new Error(`Failed to fetch ${filename}`);
|
if (!res.ok) throw new Error(`Failed to fetch ${filename}`);
|
||||||
@@ -50,9 +46,7 @@ async function fetchPost(filename) {
|
|||||||
|
|
||||||
function renderPost(filename, content) {
|
function renderPost(filename, content) {
|
||||||
const lines = content.split('\n');
|
const lines = content.split('\n');
|
||||||
if (lines.length < 3) {
|
if (lines.length < 3) return null;
|
||||||
return null; // malformed
|
|
||||||
}
|
|
||||||
const dateTime = lines[0].trim();
|
const dateTime = lines[0].trim();
|
||||||
const title = lines[1].trim();
|
const title = lines[1].trim();
|
||||||
const bodyLines = lines.slice(2);
|
const bodyLines = lines.slice(2);
|
||||||
@@ -75,31 +69,41 @@ function renderPost(filename, content) {
|
|||||||
|
|
||||||
const isLong = body.length > 300;
|
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');
|
const bodyDiv = document.createElement('div');
|
||||||
bodyDiv.className = 'blog-body';
|
bodyDiv.className = 'blog-body';
|
||||||
bodyDiv.style.whiteSpace = 'pre-line';
|
bodyDiv.style.whiteSpace = 'pre-line';
|
||||||
bodyDiv.textContent = body;
|
bodyDiv.textContent = body;
|
||||||
|
wrapper.appendChild(bodyDiv);
|
||||||
|
|
||||||
if (!isLong) {
|
// If long, add collapsed class and a toggle button
|
||||||
// Short post – show body directly
|
if (isLong) {
|
||||||
postDiv.appendChild(bodyDiv);
|
wrapper.classList.add('collapsed');
|
||||||
} else {
|
|
||||||
// Long post – hide body initially, add toggle button
|
|
||||||
bodyDiv.style.display = 'none';
|
|
||||||
postDiv.appendChild(bodyDiv);
|
|
||||||
|
|
||||||
// Toggle button
|
|
||||||
const toggleBtn = document.createElement('button');
|
const toggleBtn = document.createElement('button');
|
||||||
toggleBtn.className = 'blog-toggle';
|
toggleBtn.className = 'blog-toggle';
|
||||||
toggleBtn.textContent = 'Read more';
|
toggleBtn.textContent = 'Read more';
|
||||||
postDiv.appendChild(toggleBtn);
|
|
||||||
|
|
||||||
toggleBtn.addEventListener('click', function() {
|
toggleBtn.addEventListener('click', function() {
|
||||||
const isHidden = bodyDiv.style.display === 'none';
|
const isCollapsed = wrapper.classList.contains('collapsed');
|
||||||
bodyDiv.style.display = isHidden ? 'block' : 'none';
|
if (isCollapsed) {
|
||||||
this.textContent = isHidden ? 'Show less' : 'Read more';
|
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;
|
return postDiv;
|
||||||
@@ -120,8 +124,7 @@ async function initBlog() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
files.sort(); // alphabetical -> chronological
|
files.sort();
|
||||||
|
|
||||||
feed.innerHTML = '';
|
feed.innerHTML = '';
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
|
|||||||
+28
-1
@@ -31,13 +31,39 @@ a:hover { color:#991b1b; text-decoration:underline; }
|
|||||||
.blog-title { margin:0 0 8px 0; font-size:1.5rem; }
|
.blog-title { margin:0 0 8px 0; font-size:1.5rem; }
|
||||||
.blog-meta { margin:0 0 16px 0; color:#7f1d1d; font-size:0.95rem; }
|
.blog-meta { margin:0 0 16px 0; color:#7f1d1d; font-size:0.95rem; }
|
||||||
|
|
||||||
|
/* ----- Blog body with fade-out ----- */
|
||||||
|
.blog-body-wrapper {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
/* No max-height by default (full) */
|
||||||
|
}
|
||||||
|
|
||||||
.blog-body {
|
.blog-body {
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
margin: 0 0 1em 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
.blog-body p { margin:0 0 1em 0; }
|
.blog-body p { margin:0 0 1em 0; }
|
||||||
.blog-body p:last-child { margin-bottom:0; }
|
.blog-body p:last-child { margin-bottom:0; }
|
||||||
|
|
||||||
|
/* When collapsed, limit height and show gradient fade */
|
||||||
|
.blog-body-wrapper.collapsed {
|
||||||
|
max-height: 300px; /* ~6 lines, adjust as needed */
|
||||||
|
overflow: hidden;
|
||||||
|
/* The gradient overlay is done via ::after */
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-body-wrapper.collapsed::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 80px; /* fade height */
|
||||||
|
background: linear-gradient(to bottom, transparent, #fff);
|
||||||
|
pointer-events: none; /* allow clicking through to the button */
|
||||||
|
/* If your blog background is not white, change #fff to match */
|
||||||
|
}
|
||||||
|
|
||||||
/* Toggle button */
|
/* Toggle button */
|
||||||
.blog-toggle {
|
.blog-toggle {
|
||||||
background: none;
|
background: none;
|
||||||
@@ -53,6 +79,7 @@ a:hover { color:#991b1b; text-decoration:underline; }
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Other existing styles (photography, modal, etc.) */
|
||||||
.blog-attachment-list { margin:16px 0 0 0; padding:0; list-style:none; }
|
.blog-attachment-list { margin:16px 0 0 0; padding:0; list-style:none; }
|
||||||
.blog-attachment-list li { margin:0.5em 0; }
|
.blog-attachment-list li { margin:0.5em 0; }
|
||||||
.blog-attachment-list a { color:#dc2626; text-decoration:none; }
|
.blog-attachment-list a { color:#dc2626; text-decoration:none; }
|
||||||
|
|||||||
Reference in New Issue
Block a user