From 3bfbd783857f4e79d43ee846d5ffe9422897fffa Mon Sep 17 00:00:00 2001 From: mayx Date: Mon, 7 Apr 2025 12:27:45 +0000 Subject: Update 3 files - /_data/links.csv - /js/rss-feed-preview.js - /links.md--- js/rss-feed-preview.js | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 js/rss-feed-preview.js (limited to 'js') diff --git a/js/rss-feed-preview.js b/js/rss-feed-preview.js new file mode 100644 index 0000000..8d74709 --- /dev/null +++ b/js/rss-feed-preview.js @@ -0,0 +1,223 @@ +/** + * RSS/Atom Feed Preview for Links Table + */ + +(function() { + const existingPreviews = document.querySelectorAll('#rss-feed-preview'); + existingPreviews.forEach(el => el.remove()); + + const CORS_PROXY = 'https://cors-anywhere.mayx.eu.org/?'; + + const createPreviewElement = () => { + const existingPreview = document.getElementById('rss-feed-preview'); + if (existingPreview) { + return existingPreview; + } + + const previewEl = document.createElement('div'); + previewEl.id = 'rss-feed-preview'; + previewEl.style.cssText = ` + position: fixed; + display: none; + width: 300px; + max-height: 400px; + overflow-y: auto; + background-color: white; + border: 1px solid #ccc; + border-radius: 5px; + padding: 10px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + z-index: 1000; + font-size: 14px; + line-height: 1.4; + `; + document.body.appendChild(previewEl); + return previewEl; + }; + + const parseRSS = (xmlText) => { + const parser = new DOMParser(); + const xml = parser.parseFromString(xmlText, 'text/xml'); + + const rssItems = xml.querySelectorAll('item'); + if (rssItems.length > 0) { + return Array.from(rssItems).slice(0, 5).map(item => { + return { + title: item.querySelector('title')?.textContent || 'No title', + date: item.querySelector('pubDate')?.textContent || 'No date', + }; + }); + } + + const atomItems = xml.querySelectorAll('entry'); + if (atomItems.length > 0) { + return Array.from(atomItems).slice(0, 5).map(item => { + return { + title: item.querySelector('title')?.textContent || 'No title', + date: item.querySelector('updated')?.textContent || 'No date', + }; + }); + } + + return null; + }; + + const checkFeed = async (url) => { + try { + const response = await fetch(CORS_PROXY + url); + if (!response.ok) { + return null; + } + + const text = await response.text(); + return parseRSS(text); + } catch (error) { + return null; + } + }; + + const findFeedUrl = async (siteUrl, linkElement) => { + if (linkElement && linkElement.hasAttribute('data-feed')) { + const dataFeedUrl = linkElement.getAttribute('data-feed'); + if (dataFeedUrl) { + const feedItems = await checkFeed(dataFeedUrl); + if (feedItems) { + return { url: dataFeedUrl, items: feedItems }; + } + } + } + + return null; + }; + + const renderFeedItems = (previewEl, items, siteName) => { + if (!items || items.length === 0) { + previewEl.innerHTML = '
No feed items found.
'; + return; + } + + let html = `Checking for RSS/Atom feed...
'; + previewEl.style.display = 'block'; + positionPreview(previewEl, event); + + if (loadingTimeout) { + clearTimeout(loadingTimeout); + } + + loadingTimeout = setTimeout(async () => { + if (feedCache[url]) { + renderFeedItems(previewEl, feedCache[url].items, siteName); + positionPreview(previewEl, event); // Reposition after content is loaded + return; + } + + const feedData = await findFeedUrl(url, link); + + if (currentLink === link) { + if (feedData) { + feedCache[url] = feedData; + renderFeedItems(previewEl, feedData.items, siteName); + positionPreview(previewEl, event); // Reposition after content is loaded + } else { + previewEl.style.display = 'none'; + } + } + }, 300); + }); + + link.addEventListener('mousemove', (event) => { + if (previewEl.style.display === 'block') { + window.requestAnimationFrame(() => { + positionPreview(previewEl, event); + }); + } + }); + + link.addEventListener('mouseleave', () => { + if (loadingTimeout) { + clearTimeout(loadingTimeout); + loadingTimeout = null; + } + + currentLink = null; + previewEl.style.display = 'none'; + }); + }); + + document.addEventListener('click', (event) => { + if (!previewEl.contains(event.target)) { + previewEl.style.display = 'none'; + } + }); + }; + + if (!window.rssFeedPreviewInitialized) { + window.rssFeedPreviewInitialized = true; + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initFeedPreview); + } else { + initFeedPreview(); + } + } + })(); + \ No newline at end of file -- cgit 1.4.1-2-gfad0