- // ==UserScript==
- // @name 显示图片
- // @version 0.6
- // @description 论坛列表显示图片(适配空间
- // @author M&U
- // @match https://www.gamemale.com/*
- // @exclude https://www.gamemale.com/forum.php
- // @grant GM_addStyle
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_xmlhttpRequest
- // ==/UserScript==
- (function () {
- 'use strict';
- const TYPE_HANDLERS = [
- {
- name: "discuz",
- articleListSelector: 'tbody[id^="normalthread_"]:not([data-enhanced])',
- articleLinkSelector: '.icn a',
- postContentSelector: 'div[id^="post_"] .plc',
- postImageLinkCallback: function (element) {
- return element.getAttribute('file') || element.getAttribute('src');
- }
- },
- {
- name: "discuz_search",
- articleListSelector: '.slst.mtw li.pbw:not([data-enhanced])',
- articleLinkSelector: 'h3.xs3 a',
- postContentSelector: 'div[id^="post_"] .plc',
- postImageLinkCallback: function (element) {
- return element.getAttribute('file') || element.getAttribute('src');
- }
- },
- {
- name: "discuz_user_threads",
- articleListSelector: '#delform table tbody tr:not(.th):not([data-enhanced])',
- articleLinkSelector: 'th a:first-child',
- postContentSelector: 'div[id^="post_"] .plc',
- postImageLinkCallback: function (element) {
- return element.getAttribute('file') || element.getAttribute('src');
- }
- }
- ];
- const IGNORE_IMAGES = [
- /smile|avatar|icon|face|emoji|emoticon/i,
- /uc_server|static\/image|data\/avatar/i,
- /\.gif(\?|$)/i
- ];
- let previewEnabled = typeof GM_getValue !== 'undefined' ? GM_getValue('previewEnabled', true) : true;
- let loadedPosts = new Set();
- const toggleButton = document.createElement('button');
- toggleButton.textContent = previewEnabled ? '关闭预览' : '开启预览';
- toggleButton.style.position = 'fixed';
- toggleButton.style.bottom = '20px';
- toggleButton.style.right = '20px';
- toggleButton.style.zIndex = '9999';
- toggleButton.style.padding = '5px 10px';
- toggleButton.style.background = previewEnabled ? '#4CAF50' : '#f44336';
- toggleButton.style.color = 'white';
- toggleButton.style.border = 'none';
- toggleButton.style.borderRadius = '3px';
- document.body.appendChild(toggleButton);
- GM_addStyle(`
- .image-row-container {
- width: 100%;
- clear: both;
- margin: 10px 0;
- }
- .image-row {
- display: flex;
- width: 100%;
- flex-wrap: nowrap;
- justify-content: flex-start;
- gap: 10px;
- }
- .image-item {
- flex: 0 0 auto;
- height: 150px;
- }
- .preview-image {
- height: 100%;
- width: auto;
- max-width: 300px;
- object-fit: contain;
- cursor: pointer;
- border: 1px solid #ddd;
- background: #f5f5f5;
- border-radius: 3px;
- }
- .image-modal {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0,0,0,0.8);
- display: none;
- justify-content: center;
- align-items: center;
- z-index: 10000;
- }
- .image-modal.active {
- display: flex;
- }
- .image-modal img {
- max-width: 90%;
- max-height: 90%;
- object-fit: contain;
- }
- `);
- const modal = document.createElement('div');
- modal.className = 'image-modal';
- document.body.appendChild(modal);
- function isPostHidden(post) {
- return post.style.display === 'none';
- }
- toggleButton.addEventListener('click', function() {
- previewEnabled = !previewEnabled;
- if (typeof GM_setValue !== 'undefined') {
- GM_setValue('previewEnabled', previewEnabled);
- }
- toggleButton.textContent = previewEnabled ? '关闭预览' : '开启预览';
- toggleButton.style.background = previewEnabled ? '#4CAF50' : '#f44336';
- document.querySelectorAll('.image-row-container').forEach(container => {
- container.remove();
- });
- document.querySelectorAll('[data-enhanced]').forEach(post => {
- post.removeAttribute('data-enhanced');
- });
- loadedPosts.clear();
- if (previewEnabled) {
- init();
- }
- });
- function init() {
- if (!previewEnabled) {
- document.querySelectorAll('.image-row-container').forEach(container => {
- container.remove();
- });
- return;
- }
- if (document.getElementById('delform') && document.querySelector('#delform table tbody tr th a')) {
- handleForum('discuz_user_threads');
- } else if (location.href.includes('forum') && !location.href.includes('search')) {
- handleForum('discuz');
- } else if (location.href.includes('search')) {
- handleForum('discuz_search');
- }
- }
- function handleForum(type) {
- if (!previewEnabled) return;
- const handler = TYPE_HANDLERS.find(h => h.name === type);
- if (!handler) return;
- document.querySelectorAll(handler.articleListSelector).forEach(post => {
- if (post.hasAttribute('data-enhanced')) return;
- if (isPostHidden(post)) return;
- post.setAttribute('data-enhanced', 'true');
- const link = post.querySelector(handler.articleLinkSelector)?.href;
- if (link) loadImages(link, handler, post);
- });
- }
- function shouldIgnoreImage(src) {
- return IGNORE_IMAGES.some(regex => regex.test(src));
- }
- async function loadImages(url, handler, post) {
- if (!previewEnabled) return;
- const postId = url.split('tid=')[1] || url;
- if (loadedPosts.has(postId)) return;
- try {
- const html = await fetch(url).then(r => r.text());
- const doc = new DOMParser().parseFromString(html, 'text/html');
- const content = doc.querySelector(handler.postContentSelector);
- if (!content) return;
- const existingContainer = post.nextElementSibling?.classList?.contains('image-row-container')
- ? post.nextElementSibling
- : null;
- if (existingContainer) return;
- const container = document.createElement('div');
- container.className = 'image-row-container';
- const row = document.createElement('div');
- row.className = 'image-row';
- const uniqueImages = new Set();
- const images = Array.from(content.querySelectorAll('img'))
- .map(img => handler.postImageLinkCallback(img))
- .filter(src => src && !shouldIgnoreImage(src) && !uniqueImages.has(src))
- .slice(0, 3);
- images.forEach(src => uniqueImages.add(src));
- images.forEach(src => {
- const item = document.createElement('div');
- item.className = 'image-item';
- const image = document.createElement('img');
- image.className = 'preview-image';
- image.src = src;
- image.loading = 'lazy';
- image.addEventListener('click', () => {
- modal.innerHTML = '';
- const modalImg = document.createElement('img');
- modalImg.src = src;
- modal.appendChild(modalImg);
- modal.classList.add('active');
- });
- item.appendChild(image);
- row.appendChild(item);
- });
- if (row.children.length > 0) {
- container.appendChild(row);
- loadedPosts.add(postId);
- if (post.nextElementSibling) {
- post.parentNode.insertBefore(container, post.nextElementSibling);
- } else {
- post.parentNode.appendChild(container);
- }
- }
- } catch (error) {
- }
- }
- modal.addEventListener('click', () => {
- modal.classList.remove('active');
- });
- if (previewEnabled) {
- setTimeout(() => init(), 100);
- }
- let mutationTimeout = null;
- new MutationObserver(function(mutations) {
- if (!previewEnabled) {
- document.querySelectorAll('.image-row-container').forEach(container => {
- container.remove();
- });
- return;
- }
- clearTimeout(mutationTimeout);
- mutationTimeout = setTimeout(() => {
- const hasNewPosts = mutations.some(mutation =>
- Array.from(mutation.addedNodes).some(node =>
- node.nodeType === 1 &&
- (node.matches(TYPE_HANDLERS[0].articleListSelector) ||
- node.matches(TYPE_HANDLERS[1].articleListSelector) ||
- node.matches(TYPE_HANDLERS[2].articleListSelector))
- )
- );
- if (hasNewPosts) {
- init();
- }
- }, 500);
- }).observe(document.body, { childList: true, subtree: true });
- })();
复制代码
|