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.
		
		
		
		
		
			
		
			
				
					
					
						
							197 lines
						
					
					
						
							4.6 KiB
						
					
					
				
			
		
		
	
	
							197 lines
						
					
					
						
							4.6 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, 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('<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; | 
						|
 | 
						|
    $('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('<p>Checking for RSS/Atom feed...</p>').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); | 
						|
  } | 
						|
})();
 | 
						|
 |