Web-Font-Rendering-Core

移动端强制单行横向滚动,PC端保持多行。支持“数字+p”过滤、全量画廊、静默坏图过滤。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Web-Font-Rendering-Core
// @namespace    http://tampermonkey.net/
// @version      2.5.2
// @description  移动端强制单行横向滚动,PC端保持多行。支持“数字+p”过滤、全量画廊、静默坏图过滤。
// @author       Gemini-Adaptive
// @license      MIT
// @match        *://t66y.com/*
// @match        *://www.t66y.com/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

;(() => {
    // 【正则配置】匹配标题中的张数标识,如 50p
    const countRegex = /\d+[pP]/;

    const style = document.createElement('style');
    style.textContent = `
        /* --- 1. 列表预览容器:核心改造区 --- */
        .sys-list-container { 
            display: flex; 
            gap: 10px; 
            margin: 12px 0; 
            padding: 5px 2px; 
            clear: both;
            /* 强制单行显示 */
            flex-wrap: nowrap; 
            /* 溢出时允许横向滚动 */
            overflow-x: auto; 
            overflow-y: hidden;
            /* 增强移动端滚动体验 */
            -webkit-overflow-scrolling: touch; 
            /* 隐藏原生粗糙的滚动条(可选) */
            scrollbar-width: none; /* Firefox */
        }
        .sys-list-container::-webkit-scrollbar {
            display: none; /* Chrome/Safari */
        }

        .sys-list-thumb { 
            height: 240px; /* 移动端预览图高度 */
            width: auto; 
            flex: 0 0 auto; /* 防止图片被压缩,保持原始比例 */
            border-radius: 6px; 
            object-fit: cover; 
            background: #222; 
            cursor: zoom-in;
            box-shadow: 0 2px 6px rgba(0,0,0,0.3);
            transition: opacity 0.3s;
        }
        
        /* --- 2. PC端适配:宽度足够时恢复多行展示 --- */
        @media screen and (min-width: 1024px) {
            .sys-list-container { 
                flex-wrap: wrap; 
                overflow-x: visible; 
                scrollbar-width: auto;
            }
            .sys-list-container::-webkit-scrollbar {
                display: block;
                height: 8px;
            }
            .sys-list-thumb { 
                height: 320px; 
            }
        }

        /* --- 3. 查看器与画廊逻辑 (保持之前版本的优雅交互) --- */
        .sys-viewer-overlay {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0, 0, 0, 0.98); display: flex; flex-direction: column;
            align-items: center; justify-content: center; z-index: 999999; 
            opacity: 0; pointer-events: none; transition: opacity 0.3s ease;
            outline: none; touch-action: none;
        }
        .sys-viewer-overlay.active { opacity: 1; pointer-events: auto; }
        .sys-viewer-stage { flex: 1; width: 100%; display: flex; align-items: center; justify-content: center; overflow: hidden; position: relative; }
        .sys-viewer-node { max-width: 98%; max-height: 98%; object-fit: contain; transition: transform 0.3s cubic-bezier(0.2, 0, 0.2, 1), opacity 0.3s; opacity: 0; will-change: transform; cursor: zoom-out; }
        .sys-viewer-overlay.active .sys-viewer-node.loaded { opacity: 1; }
        
        .sys-viewer-error { display: none; color: #888; text-align: center; }
        .sys-viewer-overlay.img-error .sys-viewer-error { display: block; }
        .sys-viewer-overlay.img-error .sys-viewer-node { display: none; }

        .sys-viewer-gallery {
            width: 100%; height: 110px; background: rgba(0,0,0,0.8);
            display: flex; gap: 8px; padding: 10px; overflow-x: auto;
            border-top: 1px solid rgba(255,255,255,0.1); backdrop-filter: blur(15px);
            scrollbar-width: none;
        }
        .sys-viewer-gallery::-webkit-scrollbar { display: none; }
        .sys-gallery-item { height: 90px; width: auto; flex: 0 0 auto; border-radius: 4px; cursor: pointer; border: 2px solid transparent; transition: all 0.2s; opacity: 0.5; object-fit: cover; }
        .sys-gallery-item.current { border-color: #fff; opacity: 1; transform: scale(1.05); }

        .sys-loader { position: absolute; width: 40px; height: 40px; border: 3px solid rgba(255,255,255,0.1); border-radius: 50%; border-top-color: #fff; animation: sys-spin 0.8s linear infinite; display: none; }
        .sys-viewer-overlay.loading .sys-loader { display: block; }
        @keyframes sys-spin { to { transform: rotate(360deg); } }
    `;
    document.head.appendChild(style);

    // --- 初始化结构 ---
    const overlay = document.createElement('div');
    overlay.className = 'sys-viewer-overlay';
    overlay.tabIndex = 0;
    const stage = document.createElement('div');
    stage.className = 'sys-viewer-stage';
    const gallery = document.createElement('div');
    gallery.className = 'sys-viewer-gallery';
    const loader = document.createElement('div');
    loader.className = 'sys-loader';
    const errorTip = document.createElement('div');
    errorTip.className = 'sys-viewer-error';
    errorTip.innerHTML = '<div style="font-size:30px;">⚠️</div>加载失败,滑动切换下一张';
    const bigImg = document.createElement('img');
    bigImg.className = 'sys-viewer-node';

    stage.append(loader, errorTip, bigImg);
    overlay.append(stage, gallery);
    document.body.appendChild(overlay);

    let currentSrcList = [];
    let currentImgIndex = -1;
    let startX = 0, deltaX = 0, isDragging = false;

    // --- 核心工具函数 ---

    const setupImageErrorFilter = (imgNode) => {
        imgNode.onerror = () => { imgNode.style.display = 'none'; };
    };

    const updateViewer = (index) => {
        if (index < 0 || index >= currentSrcList.length) {
            bigImg.style.transform = `translateX(0px)`;
            return;
        }
        currentImgIndex = index;
        overlay.classList.add('loading');
        overlay.classList.remove('img-error');
        bigImg.classList.remove('loaded');
        bigImg.style.transition = 'none';
        bigImg.style.transform = `translateX(0px) scale(0.98)`;
        bigImg.src = currentSrcList[currentImgIndex];

        Array.from(gallery.children).forEach((item, idx) => {
            const isCurrent = idx === currentImgIndex;
            item.classList.toggle('current', isCurrent);
            if (isCurrent) item.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
        });
    };

    const releaseView = () => {
        overlay.classList.remove('active', 'loading', 'img-error');
        document.body.style.overflow = '';
        setTimeout(() => { bigImg.src = ''; gallery.innerHTML = ''; }, 300);
    };

    // --- 事件绑定 ---
    bigImg.onclick = (e) => { e.stopPropagation(); releaseView(); };
    overlay.onclick = (e) => { if(e.target === stage || e.target === overlay) releaseView(); };
    overlay.onkeydown = (e) => {
        if (e.key === 'Escape') releaseView();
        if (e.key === 'ArrowLeft') updateViewer(currentImgIndex - 1);
        if (e.key === 'ArrowRight') updateViewer(currentImgIndex + 1);
    };

    stage.addEventListener('touchstart', (e) => {
        if (e.touches.length > 1) return;
        startX = e.touches[0].clientX;
        deltaX = 0; isDragging = true;
        bigImg.style.transition = 'none';
    }, { passive: false });

    stage.addEventListener('touchmove', (e) => {
        if (!isDragging) return;
        deltaX = e.touches[0].clientX - startX;
        if (Math.abs(deltaX) > 5) {
            e.preventDefault();
            bigImg.style.transform = `translateX(${deltaX}px)`;
        }
    }, { passive: false });

    stage.addEventListener('touchend', () => {
        if (!isDragging) return;
        isDragging = false;
        bigImg.style.transition = 'transform 0.3s cubic-bezier(0.2, 0, 0.2, 1)';
        if (Math.abs(deltaX) > window.innerWidth * 0.15) {
            deltaX > 0 ? updateViewer(currentImgIndex - 1) : updateViewer(currentImgIndex + 1);
        } else {
            bigImg.style.transform = `translateX(0px)`;
        }
    }, { passive: false });

    bigImg.onload = () => { overlay.classList.remove('loading', 'img-error'); bigImg.classList.add('loaded'); bigImg.style.transform = `translateX(0px) scale(1)`; };
    bigImg.onerror = () => { overlay.classList.remove('loading'); overlay.classList.add('img-error'); };

    // --- 业务函数 ---

    const openViewer = async (targetSrc, containerSelector, imgSelector) => {
        const container = document.querySelector(containerSelector);
        let imgs = container ? Array.from(container.querySelectorAll(imgSelector)) : [];
        currentSrcList = imgs.map(img => img.getAttribute('ess-data') || img.src).filter(s => s && !s.includes('adblo_ck'));
        
        // 如果是预览进入,Fetch 全量图片
        if (currentSrcList.length < 15 && container && container.dataset.fullurl) {
             overlay.classList.add('loading');
             try {
                const res = await fetch(container.dataset.fullurl);
                const text = await res.text();
                const doc = new DOMParser().parseFromString(text, 'text/html');
                const area = doc.querySelector('#conttpc');
                if (area) {
                    const fullImgs = Array.from(area.querySelectorAll('img[ess-data]')).map(el => {
                         let s = el.getAttribute("ess-data");
                         return s.startsWith('//') ? 'https:' + s : s;
                    }).filter(s => s && !s.includes('adblo_ck'));
                    if (fullImgs.length > currentSrcList.length) currentSrcList = fullImgs;
                }
             } catch (e) {}
             overlay.classList.remove('loading');
        }
        
        currentImgIndex = currentSrcList.indexOf(targetSrc);
        if (currentImgIndex === -1) currentImgIndex = 0;

        if (currentSrcList.length > 0) {
            gallery.innerHTML = '';
            currentSrcList.forEach((src, idx) => {
                const thumb = document.createElement('img');
                thumb.className = 'sys-gallery-item';
                thumb.src = src;
                thumb.onclick = (e) => { e.stopPropagation(); updateViewer(idx); };
                setupImageErrorFilter(thumb);
                gallery.appendChild(thumb);
            });
            document.body.style.overflow = 'hidden';
            overlay.classList.add('active');
            overlay.focus();
            updateViewer(currentImgIndex);
        }
    };

    async function fetchPreview(item, url, title) {
        if (!countRegex.test(title) || item.dataset.proc === "1") return;
        item.dataset.proc = "1";
        try {
            const res = await fetch(url);
            const text = await res.text();
            const doc = new DOMParser().parseFromString(text, 'text/html');
            const area = doc.querySelector('#conttpc');
            if (!area) return;
            const allImgs = Array.from(area.querySelectorAll('img[ess-data]'));
            if (!allImgs.length) return;

            const box = document.createElement("div");
            box.className = "sys-list-container";
            box.dataset.fullurl = url; 

            allImgs.slice(0, 10).forEach(el => {
                let s = el.getAttribute("ess-data");
                if (s.startsWith('//')) s = 'https:' + s;
                const t = document.createElement("img");
                t.src = s;
                t.className = "sys-list-thumb";
                t.loading = "lazy";
                t.onclick = (e) => {
                    e.preventDefault(); e.stopPropagation();
                    openViewer(s, `.sys-list-container[data-fullurl="${url}"]`, '.sys-list-thumb');
                };
                setupImageErrorFilter(t);
                box.appendChild(t);
            });
            item.querySelector("h3").after(box);
        } catch (e) {}
    }

    // --- 初始化 ---
    const loc = window.location.href;
    if (loc.includes('/htm_data/')) {
        setTimeout(() => {
            const root = document.querySelector('#conttpc, .tpc_content');
            if (!root) return;
            root.querySelectorAll('img').forEach(img => {
                const src = img.getAttribute('ess-data') || img.src;
                if (!src || src.includes('adblo_ck') || src.includes('common/')) return;
                img.classList.add('sys-detail-node');
                img.onclick = (e) => { e.preventDefault(); openViewer(src, '#conttpc, .tpc_content', 'img.sys-detail-node'); };
            });
        }, 500);
    } else {
        const obs = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const a = entry.target.querySelector("h3 > a");
                    if (a) fetchPreview(entry.target, a.href, a.innerText);
                    obs.unobserve(entry.target);
                }
            });
        }, { rootMargin: '600px' });
        document.querySelectorAll("tr.tac, tr.tal").forEach(r => { if (r.querySelector("h3 > a")) obs.observe(r); });
    }
})();