Mayx's Home Page https://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

  1. /**
  2. * RSS/Atom Feed Preview for Links Table
  3. */
  4. (function () {
  5. if (window.rssFeedPreviewInitialized)
  6. return;
  7. window.rssFeedPreviewInitialized = true;
  8. var CORS_PROXY = 'https://cors-anywhere.mayx.eu.org/?';
  9. var $previewEl = $('<div>', {
  10. id: 'rss-feed-preview'
  11. }).css({
  12. position: 'fixed',
  13. display: 'none',
  14. width: '300px',
  15. maxHeight: '400px',
  16. overflowY: 'auto',
  17. backgroundColor: 'white',
  18. border: '1px solid #ccc',
  19. borderRadius: '5px',
  20. padding: '10px',
  21. fontSize: '14px',
  22. lineHeight: '1.4',
  23. zIndex: 1000,
  24. boxShadow: '0 2px 10px rgba(0,0,0,0.1)'
  25. });
  26. $('body').append($previewEl);
  27. function escapeHTML(str) {
  28. return String(str).replace(/[&<>"']/g, function (c) {
  29. return {
  30. '&': '&amp;',
  31. '<': '&lt;',
  32. '>': '&gt;',
  33. '"': '&quot;',
  34. "'": '&#39;'
  35. }[c];
  36. });
  37. }
  38. function parseRSS(xmlText) {
  39. var xml;
  40. try {
  41. xml = $.parseXML(xmlText);
  42. } catch (e) {
  43. return [];
  44. }
  45. var $xml = $(xml);
  46. var $items = $xml.find('item');
  47. if (!$items.length)
  48. $items = $xml.find('entry');
  49. var result = [];
  50. $items.slice(0, 5).each(function () {
  51. var $el = $(this);
  52. result.push({
  53. title: $el.find('title').text() || 'No title',
  54. date: $el.find('pubDate, updated').text() || 'No date'
  55. });
  56. });
  57. return result;
  58. }
  59. function checkFeed(url, onSuccess, onError) {
  60. return $.ajax({
  61. url: CORS_PROXY + url,
  62. type: 'GET',
  63. dataType: 'text',
  64. success: function (data) {
  65. var items = parseRSS(data);
  66. onSuccess(items);
  67. },
  68. error: function () {
  69. onError();
  70. }
  71. });
  72. }
  73. function renderFeedItems(items, siteName) {
  74. if (!items || !items.length) {
  75. $previewEl.html('<p>No feed items found.</p>');
  76. return;
  77. }
  78. var html = '<h3>Latest from ' + escapeHTML(siteName) + '</h3><ul style="list-style:none; padding:0; margin:0;">';
  79. for (var i = 0; i < items.length; i++) {
  80. var item = items[i];
  81. var dateStr = new Date(item.date).toLocaleDateString();
  82. html += '<li style="margin-bottom:10px; padding-bottom:10px; border-bottom:1px solid #eee;">' +
  83. '<div style="color:#24292e; font-weight:bold;">' + escapeHTML(item.title) + '</div>' +
  84. '<div style="color:#586069; font-size:12px; margin:3px 0;">' + escapeHTML(dateStr) + '</div>' +
  85. '</li>';
  86. }
  87. html += '</ul>';
  88. $previewEl.html(html);
  89. }
  90. function positionPreview(e) {
  91. e = e || window.event;
  92. var x = e.clientX;
  93. var y = e.clientY;
  94. var offsetWidth = $previewEl.outerWidth();
  95. var offsetHeight = $previewEl.outerHeight();
  96. var left = x + 20;
  97. var top = y + 20;
  98. if (left + offsetWidth > $(window).width()) {
  99. left = x - offsetWidth - 20;
  100. }
  101. if (top + offsetHeight > $(window).height()) {
  102. top = y - offsetHeight - 20;
  103. }
  104. $previewEl.css({
  105. left: Math.max(10, left),
  106. top: Math.max(10, top)
  107. });
  108. }
  109. function init() {
  110. var cache = {};
  111. var currentLink = null;
  112. var timeout = null;
  113. var currentRequest = null;
  114. var currentRequestId = 0;
  115. $('main table tbody').on('mouseenter mousemove mouseleave', 'tr td a', function (e) {
  116. if (e.type === 'mouseenter') {
  117. var $link = $(this);
  118. var siteName = $link.text();
  119. var url = $link.attr('data-feed');
  120. if (!url)
  121. return;
  122. currentLink = $link[0];
  123. var requestId = ++currentRequestId;
  124. $previewEl.html('<p>Checking for RSS/Atom feed...</p>').show();
  125. positionPreview(e);
  126. if (timeout)
  127. clearTimeout(timeout);
  128. timeout = setTimeout(function () {
  129. if (cache[url]) {
  130. if (currentLink === $link[0] && requestId === currentRequestId) {
  131. renderFeedItems(cache[url], siteName);
  132. positionPreview(e);
  133. }
  134. return;
  135. }
  136. currentRequest = checkFeed(
  137. url,
  138. function (items) {
  139. if (requestId !== currentRequestId || currentLink !== $link[0])
  140. return;
  141. if (items && items.length) {
  142. cache[url] = items;
  143. renderFeedItems(items, siteName);
  144. } else {
  145. $previewEl.html('<p>No feed items found.</p>');
  146. }
  147. positionPreview(e);
  148. },
  149. function () {
  150. if (requestId !== currentRequestId || currentLink !== $link[0])
  151. return;
  152. $previewEl.html('<p>Failed to load feed.</p>');
  153. positionPreview(e);
  154. }
  155. );
  156. }, 300);
  157. } else if (e.type === 'mousemove') {
  158. if ($previewEl.is(':visible'))
  159. positionPreview(e);
  160. } else if (e.type === 'mouseleave') {
  161. clearTimeout(timeout);
  162. timeout = null;
  163. currentLink = null;
  164. if (currentRequest) {
  165. currentRequest.abort();
  166. currentRequest = null;
  167. }
  168. $previewEl.hide();
  169. }
  170. });
  171. $(document).on('click', function (e) {
  172. if (!$(e.target).closest('#rss-feed-preview').length) {
  173. $previewEl.hide();
  174. }
  175. });
  176. }
  177. if (document.readyState === 'complete' || document.readyState === 'interactive') {
  178. init();
  179. } else {
  180. $(document).ready(init);
  181. }
  182. })();

Powered by TurnKey Linux.