mirror of
				https://github.com/Mabbs/mabbs.github.io
				synced 2025-11-04 22:27:20 +08:00 
			
		
		
		
	Update 3 files
- /_data/links.csv - /js/rss-feed-preview.js - /links.md
This commit is contained in:
		
							parent
							
								
									8c26bc57d5
								
							
						
					
					
						commit
						3bfbd78385
					
				@ -1,14 +1,14 @@
 | 
				
			|||||||
title,link,description
 | 
					title,link,feed_url,description
 | 
				
			||||||
花火学园,https://www.sayhanabi.net/,和谐融洽的ACG交流以及资源聚集地
 | 
					花火学园,https://www.sayhanabi.net/,,和谐融洽的ACG交流以及资源聚集地
 | 
				
			||||||
资源统筹局,https://gkdworld.com/,统筹保管用户分享的资源
 | 
					资源统筹局,https://gkdworld.com/,,统筹保管用户分享的资源
 | 
				
			||||||
贫困的蚊子,https://mozz.ie/,*No description*
 | 
					贫困的蚊子,https://mozz.ie/,https://mozz.ie/index.xml,*No description*
 | 
				
			||||||
极客兔兔,https://geektutu.com/,致力于分享有趣的技术实践
 | 
					极客兔兔,https://geektutu.com/,https://geektutu.com/atom.xml,致力于分享有趣的技术实践
 | 
				
			||||||
维基萌,https://www.wikimoe.com/,萌即是正义!一名热爱acg的前端设计师的小站!
 | 
					维基萌,https://www.wikimoe.com/,https://www.wikimoe.com/rss,萌即是正义!一名热爱acg的前端设计师的小站!
 | 
				
			||||||
7gugu's blog,https://www.7gugu.com/,"一个用来存放我爱好的地方,编程,摄影之类的空间"
 | 
					7gugu's blog,https://www.7gugu.com/,https://7gugu.com/index.php/feed/,"一个用来存放我爱好的地方,编程,摄影之类的空间"
 | 
				
			||||||
云游君,https://www.yunyoujun.cn/,希望能成为一个有趣的人。
 | 
					云游君,https://www.yunyoujun.cn/,https://www.yunyoujun.cn/atom.xml,希望能成为一个有趣的人。
 | 
				
			||||||
Kingfish404,https://blog.kingfish404.cn/,"Stay curious,stay naive. WUT. Jin Yu's Blog"
 | 
					Kingfish404,https://blog.kingfish404.cn/,https://blog.kingfish404.cn/index.xml,"Stay curious,stay naive. WUT. Jin Yu's Blog"
 | 
				
			||||||
FKUN,https://blog.fkun.tech/,*No description*
 | 
					FKUN,https://blog.fkun.tech/,https://blog.fkun.tech/feed/,*No description*
 | 
				
			||||||
Sinofine,https://sinofine.me/,*No description*
 | 
					Sinofine,https://sinofine.me/,https://sinofine.me/atom.xml,*No description*
 | 
				
			||||||
JiaoYuan's blog,https://yuanj.top/,思绪来得快去得也快,偶尔会在这里停留
 | 
					JiaoYuan's blog,https://yuanj.top/,https://yuanj.top/index.xml,思绪来得快去得也快,偶尔会在这里停留
 | 
				
			||||||
花生莲子粥,https://blog.hslzz.cn/,与世无争,不染于泥
 | 
					花生莲子粥,https://blog.hslzz.cn/,https://blog.hslzz.cn/atom.xml,与世无争,不染于泥
 | 
				
			||||||
南蛮子懋和,https://www.dao.js.cn/,李懋和,俗名李栋梁。书法、国画爱好者,互联网安全与前端建设者。
 | 
					南蛮子懋和,https://www.dao.js.cn/,https://www.dao.js.cn/feed.php,李懋和,俗名李栋梁。书法、国画爱好者,互联网安全与前端建设者。
 | 
				
			||||||
		
		
			
  | 
							
								
								
									
										223
									
								
								js/rss-feed-preview.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								js/rss-feed-preview.js
									
									
									
									
									
										Normal file
									
								
							@ -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 = '<p>No feed items found.</p>';
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      let html = `<h3>Latest from ${siteName}</h3><ul style="list-style: none; padding: 0; margin: 0;">`;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      items.forEach(item => {
 | 
				
			||||||
 | 
					        html += `
 | 
				
			||||||
 | 
					          <li style="margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #eee;">
 | 
				
			||||||
 | 
					            <div style="color: #24292e; font-weight: bold;">
 | 
				
			||||||
 | 
					              ${item.title}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div style="color: #586069; font-size: 12px; margin: 3px 0;">
 | 
				
			||||||
 | 
					              ${new Date(item.date).toLocaleDateString()}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					        `;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      html += '</ul>';
 | 
				
			||||||
 | 
					      previewEl.innerHTML = html;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    const positionPreview = (previewEl, event) => {
 | 
				
			||||||
 | 
					      const viewportWidth = window.innerWidth;
 | 
				
			||||||
 | 
					      const viewportHeight = window.innerHeight;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      let left = event.clientX + 20;
 | 
				
			||||||
 | 
					      let top = event.clientY + 20;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      const rect = previewEl.getBoundingClientRect();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      if (left + rect.width > viewportWidth) {
 | 
				
			||||||
 | 
					        left = event.clientX - rect.width - 20;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      if (top + rect.height > viewportHeight) {
 | 
				
			||||||
 | 
					        top = event.clientY - rect.height - 20;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      left = Math.max(10, left);
 | 
				
			||||||
 | 
					      top = Math.max(10, top);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      previewEl.style.left = `${left}px`;
 | 
				
			||||||
 | 
					      previewEl.style.top = `${top}px`;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    const initFeedPreview = () => {
 | 
				
			||||||
 | 
					      const previewEl = createPreviewElement();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      const tableLinks = document.querySelectorAll('main table tbody tr td a');
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      const feedCache = {};
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      let currentLink = null;
 | 
				
			||||||
 | 
					      let loadingTimeout = null;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      tableLinks.forEach(link => {
 | 
				
			||||||
 | 
					        link.addEventListener('mouseenter', async (event) => {
 | 
				
			||||||
 | 
					          currentLink = link;
 | 
				
			||||||
 | 
					          const url = link.getAttribute('href');
 | 
				
			||||||
 | 
					          const siteName = link.textContent;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					          previewEl.innerHTML = '<p>Checking for RSS/Atom feed...</p>';
 | 
				
			||||||
 | 
					          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();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
							
								
								
									
										4
									
								
								links.md
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								links.md
									
									
									
									
									
								
							@ -8,7 +8,7 @@ tags: [links]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
| Link | Description |
 | 
					| Link | Description |
 | 
				
			||||||
| - | - |
 | 
					| - | - |
 | 
				
			||||||
{% for item in site.data.links %}| [{{ item.title }}]({{ item.link }}) | {{ item.description }} |
 | 
					{% for item in site.data.links %}| <a href="{{ item.link }}" data-feed="{{ item.feed_url }}">{{ item.title }}</a> | {{ item.description }} |
 | 
				
			||||||
{% endfor %}
 | 
					{% endfor %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Links申请
 | 
					## Links申请
 | 
				
			||||||
@ -25,3 +25,5 @@ tags: [links]
 | 
				
			|||||||
链接:<https://mabbs.github.io>   
 | 
					链接:<https://mabbs.github.io>   
 | 
				
			||||||
头像:<https://avatars0.githubusercontent.com/u/17966333>   
 | 
					头像:<https://avatars0.githubusercontent.com/u/17966333>   
 | 
				
			||||||
Logo:<https://mabbs.github.io/favicon.ico>
 | 
					Logo:<https://mabbs.github.io/favicon.ico>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script src="/js/rss-feed-preview.js"></script>
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user