/** * RSS/Atom Feed Preview for Links Table */ (function () { if (window.rssFeedPreviewInitialized) return; window.rssFeedPreviewInitialized = true; var CORS_PROXY = 'https://cors-anywhere.mayx.eu.org/?'; var $previewEl = $('
', { id: 'rss-feed-preview' }).css({ position: 'fixed', display: 'none', width: '300px', maxHeight: '400px', overflowY: 'auto', backgroundColor: 'white', border: '1px solid #ccc', borderRadius: '5px', padding: '10px', fontSize: '14px', lineHeight: '1.4', zIndex: 1000, boxShadow: '0 2px 10px rgba(0,0,0,0.1)' }); $('body').append($previewEl); function escapeHTML(str) { return String(str).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); } function parseRSS(xmlText) { var xml; try { xml = $.parseXML(xmlText); } catch (e) { return []; } var $xml = $(xml); var $items = $xml.find('item'); if (!$items.length) $items = $xml.find('entry'); var result = []; $items.slice(0, 5).each(function () { var $el = $(this); result.push({ title: $el.find('title').text() || 'No title', date: $el.find('pubDate, updated').text() || 'No date' }); }); return result; } function checkFeed(url, callback) { $.ajax({ url: CORS_PROXY + url, type: 'GET', dataType: 'text', success: function (data) { var items = parseRSS(data); callback(items); }, error: function () { callback(null); } }); } function renderFeedItems(items, siteName) { if (!items || !items.length) { $previewEl.html('

No feed items found.

'); return; } var html = '

Latest from ' + escapeHTML(siteName) + '

'; $previewEl.html(html); } function positionPreview(e) { e = e || window.event; var x = e.clientX; var y = e.clientY; var offsetWidth = $previewEl.outerWidth(); var offsetHeight = $previewEl.outerHeight(); var left = x + 20; var top = y + 20; if (left + offsetWidth > $(window).width()) { left = x - offsetWidth - 20; } if (top + offsetHeight > $(window).height()) { top = y - offsetHeight - 20; } $previewEl.css({ left: Math.max(10, left), top: Math.max(10, top) }); } function init() { var cache = {}; var currentLink = null; var timeout = null; $('main table tbody tr td a').each(function () { var $link = $(this); $link.on('mouseenter', function (e) { currentLink = this; var siteName = $link.text(); var url = $link.attr('data-feed'); if (!url) return; $previewEl.html('

Checking for RSS/Atom feed...

').show(); positionPreview(e); if (timeout) clearTimeout(timeout); timeout = setTimeout(function () { if (cache[url]) { renderFeedItems(cache[url], siteName); positionPreview(e); return; } if (url) { checkFeed(url, function (items) { if (currentLink === $link[0] && items) { cache[url] = items; renderFeedItems(items, siteName); positionPreview(e); } else { $previewEl.hide(); } }); } else { $previewEl.hide(); } }, 300); }); $link.on('mousemove', function (e) { if ($previewEl.is(':visible')) positionPreview(e); }); $link.on('mouseleave', function () { clearTimeout(timeout); timeout = null; currentLink = null; $previewEl.hide(); }); }); $(document).on('click', function (e) { if (!$(e.target).closest('#rss-feed-preview').length) { $previewEl.hide(); } }); } if (document.readyState === 'complete' || document.readyState === 'interactive') { init(); } else { $(document).ready(init); } })();