oops fixed
This commit is contained in:
+126
@@ -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, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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 = '';
|
||||
|
||||
@@ -56,3 +56,9 @@ a:hover { color:#991b1b; text-decoration:underline; }
|
||||
.modal-prev { left:20px; top:50%; transform:translateY(-50%); }
|
||||
.modal-next { right:20px; top:50%; transform:translateY(-50%); }
|
||||
|
||||
.blog-collapse summary { cursor: pointer; list-style: none; }
|
||||
.blog-collapse summary::-webkit-details-marker { display: none; }
|
||||
.blog-preview { white-space: pre-line; margin-bottom: 0.5rem;}
|
||||
.read-more-text {display: inline-block; font-weight: bold; margin-top: 0.5rem; }
|
||||
.read-more-text:hover { text-decoration: underline; }
|
||||
.blog-collapse[open] .read-more-text { margin-bottom: 1rem; }
|
||||
Reference in New Issue
Block a user