mirror of https://github.com/Mabbs/mabbs.github.io
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
5.3 KiB
214 lines
5.3 KiB
/** |
|
* 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 = $('<div>', { |
|
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, onSuccess, onError) { |
|
return $.ajax({ |
|
url: CORS_PROXY + url, |
|
type: 'GET', |
|
dataType: 'text', |
|
success: function (data) { |
|
var items = parseRSS(data); |
|
onSuccess(items); |
|
}, |
|
error: function () { |
|
onError(); |
|
} |
|
}); |
|
} |
|
|
|
function renderFeedItems(items, siteName) { |
|
if (!items || !items.length) { |
|
$previewEl.html('<p>No feed items found.</p>'); |
|
return; |
|
} |
|
|
|
var html = '<h3>Latest from ' + escapeHTML(siteName) + '</h3><ul style="list-style:none; padding:0; margin:0;">'; |
|
for (var i = 0; i < items.length; i++) { |
|
var item = items[i]; |
|
var dateStr = new Date(item.date).toLocaleDateString(); |
|
html += '<li style="margin-bottom:10px; padding-bottom:10px; border-bottom:1px solid #eee;">' + |
|
'<div style="color:#24292e; font-weight:bold;">' + escapeHTML(item.title) + '</div>' + |
|
'<div style="color:#586069; font-size:12px; margin:3px 0;">' + escapeHTML(dateStr) + '</div>' + |
|
'</li>'; |
|
} |
|
html += '</ul>'; |
|
$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; |
|
var currentRequest = null; |
|
var currentRequestId = 0; |
|
$('main table tbody').on('mouseenter mousemove mouseleave', 'tr td a', function (e) { |
|
|
|
if (e.type === 'mouseenter') { |
|
var $link = $(this); |
|
var siteName = $link.text(); |
|
var url = $link.attr('data-feed'); |
|
if (!url) |
|
return; |
|
|
|
currentLink = $link[0]; |
|
var requestId = ++currentRequestId; |
|
|
|
$previewEl.html('<p>Checking for RSS/Atom feed...</p>').show(); |
|
positionPreview(e); |
|
|
|
if (timeout) |
|
clearTimeout(timeout); |
|
timeout = setTimeout(function () { |
|
if (cache[url]) { |
|
if (currentLink === $link[0] && requestId === currentRequestId) { |
|
renderFeedItems(cache[url], siteName); |
|
positionPreview(e); |
|
} |
|
return; |
|
} |
|
|
|
currentRequest = checkFeed( |
|
url, |
|
function (items) { |
|
if (requestId !== currentRequestId || currentLink !== $link[0]) |
|
return; |
|
|
|
if (items && items.length) { |
|
cache[url] = items; |
|
renderFeedItems(items, siteName); |
|
} else { |
|
$previewEl.html('<p>No feed items found.</p>'); |
|
} |
|
|
|
positionPreview(e); |
|
}, |
|
function () { |
|
if (requestId !== currentRequestId || currentLink !== $link[0]) |
|
return; |
|
$previewEl.html('<p>Failed to load feed.</p>'); |
|
positionPreview(e); |
|
} |
|
); |
|
}, 300); |
|
} else if (e.type === 'mousemove') { |
|
if ($previewEl.is(':visible')) |
|
positionPreview(e); |
|
} else if (e.type === 'mouseleave') { |
|
clearTimeout(timeout); |
|
timeout = null; |
|
currentLink = null; |
|
|
|
if (currentRequest) { |
|
currentRequest.abort(); |
|
currentRequest = 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); |
|
} |
|
})();
|
|
|