From 265538a974e9166ca0a177d0177c759611409328 Mon Sep 17 00:00:00 2001 From: organic-CPU Date: Sat, 11 Jul 2026 22:53:47 +0200 Subject: [PATCH] test --- blog/manifest.json | 5 +- images/manifest.json | 159 +++++++++++++++++++++++++++ js/blog.js | 251 ++++++++++++++----------------------------- 3 files changed, 241 insertions(+), 174 deletions(-) create mode 100644 images/manifest.json diff --git a/blog/manifest.json b/blog/manifest.json index d6fc449..187f310 100644 --- a/blog/manifest.json +++ b/blog/manifest.json @@ -1,5 +1,4 @@ [ - "2026-06-06.text", - "2026-07-09-welcome.txt", - "2026-07-10-attachment-test.txt" + "10072026.text", + "the first post.text" ] \ No newline at end of file diff --git a/images/manifest.json b/images/manifest.json new file mode 100644 index 0000000..7d0ee00 --- /dev/null +++ b/images/manifest.json @@ -0,0 +1,159 @@ +[ + "_DSC2592.jpg", + "_DSC2563.jpg", + "_DSC2536.jpg", + "_DSC2534.jpg", + "_DSC2532.jpg", + "_DSC2527.jpg", + "_DSC2627.jpg", + "_DSC2600.jpg", + "_DSC5088.jpg", + "_DSC5082.jpg", + "_DSC5070.jpg", + "_DSC5060.jpg", + "_DSC5058.jpg", + "_DSC5037.jpg", + "_DSC5031.jpg", + "_DSC5014.jpg", + "_DSC5005.jpg", + "_DSC4996.jpg", + "_DSC4984.jpg", + "_DSC4935.jpg", + "_DSC4930.jpg", + "_DSC4927.jpg", + "_DSC4907.jpg", + "_DSC4859.jpg", + "_DSC4850.jpg", + "_DSC4824.jpg", + "_DSC4814.jpg", + "_DSC4784.jpg", + "_DSC4774.jpg", + "_DSC4760.jpg", + "_DSC4689.jpg", + "_DSC4686.jpg", + "_DSC4670.jpg", + "_DSC4640.jpg", + "_DSC4565.jpg", + "_DSC4561.jpg", + "_DSC4558.jpg", + "_DSC4537.jpg", + "_DSC4532.jpg", + "_DSC4523.jpg", + "_DSC4522.jpg", + "_DSC4478.jpg", + "_DSC4447.jpg", + "_DSC4416.jpg", + "_DSC4408.jpg", + "_DSC4373.jpg", + "_DSC4343.jpg", + "_DSC4332.jpg", + "_DSC4322.jpg", + "_DSC5220.jpg", + "_DSC5112.jpg", + "_DSC5109.jpg", + "_DSC5096.jpg", + "_DSC1049.jpg", + "_DSC1042.jpg", + "_DSC1037.jpg", + "_DSC1030.jpg", + "_DSC1028.jpg", + "_DSC1021.jpg", + "_DSC1016.jpg", + "_DSC1015.jpg", + "_DSC1009.jpg", + "_DSC1007.jpg", + "_DSC1102.jpg", + "_DSC1098.jpg", + "_DSC1052.jpg", + "_DSC1468.jpg", + "_DSC1464.jpg", + "_DSC1559.jpg", + "_DSC1550.jpg", + "_DSC1543.jpg", + "_DSC1542.jpg", + "_DSC1539.jpg", + "_DSC1527.jpg", + "_DSC1516.jpg", + "_DSC1498.jpg", + "_DSC1495.jpg", + "_DSC1487.jpg", + "_DSC1471.jpg", + "_DSC1152.jpg", + "_DSC1150.jpg", + "_DSC1396.jpg", + "_DSC1341.jpg", + "_DSC1330.jpg", + "_DSC1315.jpg", + "_DSC1282.jpg", + "_DSC1174.jpg", + "_DSC1164.jpg", + "_DSC0779.jpg", + "_DSC0762.jpg", + "_DSC0732.jpg", + "_DSC0707.jpg", + "_DSC0701.jpg", + "_DSC0699.jpg", + "_DSC0697.jpg", + "_DSC0683.jpg", + "_DSC0678.jpg", + "_DSC0677.jpg", + "_DSC0671.jpg", + "_DSC0669.jpg", + "_DSC0665.jpg", + "_DSC0649.jpg", + "_DSC0646.jpg", + "_DSC0624.jpg", + "_DSC0621.jpg", + "_DSC0601.jpg", + "_DSC0590.jpg", + "_DSC0582.jpg", + "_DSC0866.jpg", + "_DSC0861.jpg", + "_DSC0858.jpg", + "_DSC0854.jpg", + "_DSC0850.jpg", + "_DSC0846.jpg", + "_DSC0841.jpg", + "_DSC0837.jpg", + "_DSC0833.jpg", + "_DSC0823.jpg", + "_DSC0817.jpg", + "_DSC0784.jpg", + "_DSC0782.jpg", + "DSC_0337.jpg", + "DSC_0335.jpg", + "DSC_0319.jpg", + "DSC_0297.jpg", + "DSC_0278.jpg", + "DSC_0257.jpg", + "DSC_0234.jpg", + "DSC_0224.jpg", + "DSC_0214.jpg", + "DSC_0207.jpg", + "DSC_0499.jpg", + "DSC_0470.jpg", + "DSC_0455.jpg", + "DSC_0450.jpg", + "DSC_0446.jpg", + "DSC_0439.jpg", + "DSC_0430.jpg", + "DSC_0404.jpg", + "DSC_0402.jpg", + "DSC_0389.jpg", + "DSC_0387.jpg", + "DSC_0378.jpg", + "DSC_0368.jpg", + "DSC_0036.jpg", + "DSC_0122.jpg", + "DSC_0114.jpg", + "DSC_0089.jpg", + "DSC_0087.jpg", + "DSC_0084.jpg", + "DSC_0083.jpg", + "DSC_0081.jpg", + "DSC_0068.jpg", + "DSC_0054.jpg", + "DSC_0048.jpg", + "DSC_0045.jpg", + "DSC_0042.jpg" +] \ No newline at end of file diff --git a/js/blog.js b/js/blog.js index 930ae75..5817b6a 100644 --- a/js/blog.js +++ b/js/blog.js @@ -1,127 +1,3 @@ -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; - -function escapeHtml(value) { - return value - .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 `${escapeHtml(safeName)}`; - }); - const lines = text.split('\n'); - p.innerHTML = lines.join('
'); - return p; - }); -} - function renderBlogEntries(entries) { const container = document.querySelector('.blog-feed'); container.innerHTML = ''; @@ -141,78 +17,111 @@ function renderBlogEntries(entries) { const meta = document.createElement('div'); meta.className = 'blog-meta'; - meta.textContent = entry.date ? formatDate(entry.date) : entry.dateLabel || 'Date not provided'; + meta.textContent = entry.date + ? formatDate(entry.date) + : entry.dateLabel || 'Date not provided'; + + + const createBody = () => { + const body = document.createElement('div'); + body.className = 'blog-body'; + + const paragraphs = createParagraphs(entry.body); + + if (!paragraphs.length) { + const empty = document.createElement('p'); + empty.textContent = 'No content yet.'; + body.appendChild(empty); + } else { + paragraphs.forEach((p) => body.appendChild(p)); + } + + return body; + }; + + + let bodyContainer; + + if (entry.body.length > BLOG_COLLAPSE_THRESHOLD) { + const details = document.createElement('details'); + details.className = 'blog-collapse'; + + const summary = document.createElement('summary'); + + // Create preview + const preview = document.createElement('div'); + preview.className = 'blog-preview'; + + const previewText = entry.body + .split('\n') + .filter(line => line.trim()) + .slice(0, BLOG_PREVIEW_LINES) + .join('\n'); + + preview.textContent = previewText; + + + summary.appendChild(preview); + + const moreText = document.createElement('span'); + moreText.className = 'read-more-text'; + moreText.textContent = 'Read more'; + + summary.appendChild(moreText); + + + details.appendChild(summary); + details.appendChild(createBody()); + + + details.addEventListener('toggle', () => { + moreText.textContent = details.open + ? 'Show less' + : 'Read more'; + + preview.style.display = details.open + ? 'none' + : 'block'; + }); + + + bodyContainer = details; - const body = document.createElement('div'); - body.className = 'blog-body'; - const paragraphs = createParagraphs(entry.body); - if (!paragraphs.length) { - const empty = document.createElement('p'); - empty.textContent = 'No content yet.'; - body.appendChild(empty); } else { - paragraphs.forEach((p) => body.appendChild(p)); + bodyContainer = createBody(); } + if (entry.attachments && entry.attachments.length) { const attachTitle = document.createElement('div'); attachTitle.className = 'blog-meta'; attachTitle.textContent = 'Attachments:'; + const list = document.createElement('ul'); list.className = 'blog-attachment-list'; + entry.attachments.forEach((attachment) => { const item = document.createElement('li'); + const link = document.createElement('a'); link.href = ATTACHMENT_DIR + encodeURI(attachment); link.target = '_blank'; link.rel = 'noopener noreferrer'; link.textContent = attachment; + item.appendChild(link); list.appendChild(item); }); + article.appendChild(attachTitle); article.appendChild(list); } + article.appendChild(title); article.appendChild(meta); - article.appendChild(body); + article.appendChild(bodyContainer); + container.appendChild(article); }); -} - -function sortEntries(entries) { - return entries.slice().sort((a, b) => { - if (a.date && b.date) return b.date - a.date; - if (a.date) return -1; - if (b.date) return 1; - return a.filename.localeCompare(b.filename); - }); -} - -async function initBlog() { - const fileNames = await loadBlogFiles(); - if (!fileNames.length) { - renderBlogEntries([]); - return; - } - - const promises = fileNames - .filter((name) => TXT_EXT.test(name)) - .map(async (name) => { - try { - const response = await fetch(BLOG_DIR + name, { cache: 'no-cache' }); - if (!response.ok) throw new Error('Failed to load ' + name); - const text = await response.text(); - return parseBlogText(text, name); - } catch (error) { - console.warn(error); - return null; - } - }); - - const entries = (await Promise.all(promises)).filter(Boolean); - renderBlogEntries(sortEntries(entries)); -} - -document.addEventListener('DOMContentLoaded', initBlog); +} \ No newline at end of file