JavDB 瀏覽增強

JavDB 功能增強:懸浮搜索、隱藏搜索、文字過濾、評分過濾...等功能

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         JavDB 瀏覽增強
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  JavDB 功能增強:懸浮搜索、隱藏搜索、文字過濾、評分過濾...等功能
// @author       Gemini
// @match        https://javdb.com/*
// @match        https://javdb36.com/*
// @match        https://javdb007.com/*
// @match        https://javdb521.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=javdb.com
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @grant        GM_openInTab
// ==/UserScript==

(function() {
    'use strict';

    // 僅在頂層視窗執行主要邏輯
    if (window.self !== window.top) {
        return;
    }

    // --- 設定預設值與讀取 ---
    const DEFAULTS = {
        cols: 4,
        infiniteScroll: true,
        modalPreview: true,
        hideSearchBar: false, 
        margin: 0, 
        minRating: 0, 
        hiddenTags: '', 
        showQuickBlock: true,
        translateTitle: false, 
        floatingSearch: false, 
        triggerDistance: 1500
    };

    let state = {
        isLoading: false,
        nextPageUrl: null,
        mainContainer: null,
        hiddenItems: { rating: [], text: [] },
        hiddenByRatingCount: 0,
        hiddenByTextCount: 0,
        modalHistory: [],
        currentModalUrl: null,
        activeMovieUrl: null, 
        searchObserver: null,
        config: {
            cols: parseInt(localStorage.getItem('jd_cols')) || DEFAULTS.cols,
            infiniteScroll: (localStorage.getItem('jd_infinite') !== 'false'),
            modalPreview: (localStorage.getItem('jd_modal') !== 'false'),
            hideSearchBar: (localStorage.getItem('jd_hide_search') === 'true'),
            margin: (localStorage.getItem('jd_margin') !== null) ? parseInt(localStorage.getItem('jd_margin')) : DEFAULTS.margin,
            minRating: parseFloat(localStorage.getItem('jd_rating')) || DEFAULTS.minRating,
            hiddenTags: localStorage.getItem('jd_tags') || DEFAULTS.hiddenTags,
            showQuickBlock: (localStorage.getItem('jd_quick_block') !== 'false'),
            translateTitle: (localStorage.getItem('jd_translate_title') === 'true'),
            floatingSearch: (localStorage.getItem('jd_floating_search') === 'true')
        }
    };

    // --- CSS 樣式 ---
    const styles = `
        :root {
            --jd-cols: ${state.config.cols};
            --jd-margin: ${state.config.margin}%;
        }

        .section .container, 
        .navbar .container, 
        .footer .container {
            max-width: calc(100% - (var(--jd-margin) * 2)) !important;
            width: calc(100% - (var(--jd-margin) * 2)) !important;
            margin-left: auto !important;
            margin-right: auto !important;
            transition: width 0.3s ease, max-width 0.3s ease;
        }
        
        /* 指定隱藏規則 */
        body.jd-hide-search nav.sub-header:nth-child(3) { display: none !important; }
        body.jd-hide-search #search-bar-container > div.column { display: none !important; }
        
        /* 排除懸浮搜尋表單 (#jd-search-form),只隱藏網頁原本的 */
        body.jd-hide-search form[action="/search"]:not(#jd-search-form),
        body.jd-hide-search .navbar-item:has(form[action="/search"]) { display: none !important; }
        
        .jd-search-hidden { display: none !important; }

        /* 強制應用 Grid 設定 */
        #jd-main-grid, .jd-main-grid { 
            display: grid !important; 
            grid-template-columns: repeat(var(--jd-cols), minmax(0, 1fr)) !important; 
            width: 100% !important; 
        }
        @media (max-width: 768px) {
             #jd-main-grid, .jd-main-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
        }

        body.jd-hide-quick-block .jd-block-tag-btn { display: none !important; }

        /* 懸浮控制區容器 */
        #jd-wrapper {
            position: fixed; bottom: 30px; right: 30px; z-index: 20000;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
            pointer-events: none; 
        }
        
        #jd-wrapper > * {
            pointer-events: auto; 
        }

        /* 底部操作列 */
        #jd-action-row {
            display: flex; align-items: center; justify-content: flex-end; gap: 12px;
            height: 52px; 
        }

        /* 通用圓形按鈕 */
        .jd-round-btn {
            width: 52px; height: 52px;
            background: linear-gradient(135deg, #2a2a2a, #1a1a1a);
            border-radius: 50%; display: flex; align-items: center; justify-content: center;
            cursor: pointer; box-shadow: 0 6px 16px rgba(0,0,0,0.4), inset 0 1px 1px rgba(255,255,255,0.1);
            border: 1px solid #444; transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); color: #ddd;
            flex-shrink: 0; position: relative; z-index: 10;
        }
        .jd-round-btn:hover {
            transform: scale(1.05); background: linear-gradient(135deg, #0066cc, #004499);
            border-color: #0077ff; color: white; box-shadow: 0 8px 20px rgba(0, 102, 204, 0.4);
        }
        .jd-round-btn svg { width: 24px; height: 24px; fill: currentColor; transition: transform 0.5s ease; }
        #jd-gear-btn:hover svg { transform: rotate(90deg); }

        /* 懸浮搜索欄 */
        #jd-floating-search-bar {
            height: 52px;
            width: 0; 
            opacity: 0;
            overflow: hidden;
            background: rgba(30, 30, 30, 0.98); 
            backdrop-filter: blur(10px);
            border-radius: 26px;
            display: flex; 
            align-items: center;
            transition: width 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s ease, padding 0.3s ease; 
            padding: 0;
            border: 0px solid #444;
            white-space: nowrap;
            margin-right: -26px; 
            padding-right: 30px; 
            z-index: 1;
        }
        
        #jd-floating-search-bar.active {
            width: 540px; /* 寬度 */
            opacity: 1;
            padding: 0 10px 0 15px; 
            border: 1px solid #555;
            margin-right: 10px; 
            padding-right: 10px;
            box-shadow: 0 6px 20px rgba(0,0,0,0.5);
        }

        /* 搜尋欄內部表單 - 強制顯示與覆蓋 */
        #jd-search-form { 
            display: flex !important; 
            gap: 8px !important; 
            height: 100% !important; 
            align-items: center !important; 
            width: 100% !important;
            min-width: 520px !important; 
            margin: 0 !important;
            padding: 0 !important;
            visibility: visible !important; 
            opacity: 1 !important;
        }
        
        /* 下拉選單 */
        #jd-search-select {
            display: block !important;
            visibility: visible !important;
            opacity: 1 !important;
            background: #222 !important; 
            color: #fff !important; 
            border: 1px solid #555 !important; 
            border-radius: 20px !important; 
            padding: 0 10px !important; 
            font-size: 13px !important; 
            width: auto !important;
            min-width: 75px !important;
            height: 36px !important; 
            outline: none !important;
            flex-shrink: 0 !important;
            cursor: pointer !important;
        }
        
        /* 輸入框 */
        #jd-search-input {
            display: block !important;
            visibility: visible !important;
            opacity: 1 !important;
            flex: 1 !important; 
            background: #222 !important; 
            color: #fff !important; 
            border: 1px solid #555 !important; 
            border-radius: 20px !important; 
            padding: 0 15px !important; 
            font-size: 14px !important; 
            height: 36px !important; 
            min-width: 100px !important;
        }
        #jd-search-input:focus { outline: none; border-color: #0066cc !important; }
        
        /* 按鈕 */
        .jd-search-icon-btn {
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            visibility: visible !important;
            opacity: 1 !important;
            background: transparent !important; 
            border: 1px solid #555 !important;
            color: #ccc !important; 
            width: 36px !important; 
            height: 36px !important; 
            cursor: pointer !important; 
            border-radius: 50% !important; 
            transition: 0.2s !important; 
            flex-shrink: 0 !important;
        }
        .jd-search-icon-btn:hover { background: #444 !important; color: #fff !important; border-color: #0066cc !important; }
        .jd-search-icon-btn svg { width: 18px !important; height: 18px !important; fill: currentColor !important; display: block !important; }

        /* 設定面板 */
        #jd-panel {
            position: absolute; bottom: 70px; right: 0; width: 300px;
            background: rgba(26, 26, 26, 0.95); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
            border-radius: 12px; padding: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.6);
            border: 1px solid #444; display: none; flex-direction: column; gap: 18px;
            opacity: 0; transform: translateY(15px); transition: opacity 0.3s ease, transform 0.3s ease;
            z-index: 19999;
        }
        #jd-panel.active { display: flex; opacity: 1; transform: translateY(0); }

        .jd-row { display: flex; justify-content: space-between; align-items: center; color: #eee; font-size: 14px; }
        .jd-label { font-weight: 500; color: #ccc; }
        
        .jd-status-bar {
            font-size: 13px; color: #aaa; text-align: center; padding-top: 10px;
            border-top: 1px solid #444; margin-top: 5px; line-height: 1.6;
        }
        
        .jd-status-btn {
            color: #fff; cursor: pointer; padding: 2px 6px; border-radius: 4px;
            transition: background 0.2s; text-decoration: underline;
            text-decoration-style: dotted; text-underline-offset: 3px;
        }
        .jd-status-btn:hover { background: #0066cc; color: white; text-decoration: none; }

        .jd-input-num {
            width: 50px; background: #111; border: 1px solid #444; color: #fff;
            border-radius: 6px; padding: 5px; text-align: center; font-size: 14px;
        }

        .jd-btn {
            background: #333; border: 1px solid #555; color: #fff; padding: 5px 12px;
            border-radius: 6px; cursor: pointer; font-size: 13px; transition: background 0.2s;
        }
        .jd-btn:hover { background: #0066cc; border-color: #0077ff; }

        /* 文字過濾設定視窗 */
        #jd-tag-panel {
            position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
            width: 320px; background: #1a1a1a; border: 1px solid #444; border-radius: 10px;
            padding: 20px; z-index: 20001; display: none; box-shadow: 0 10px 40px rgba(0,0,0,0.8);
            flex-direction: column; gap: 10px;
        }
        #jd-tag-panel.active { display: flex; }
        #jd-tag-panel h3 { margin: 0 0 5px 0; color: #fff; font-size: 16px; text-align: center; display: none; } 
        #jd-tag-panel p { margin: 0; color: #aaa; font-size: 12px; text-align: center; }
        #jd-tag-input {
            width: 100%; height: 100px; background: #111; border: 1px solid #444;
            color: #eee; padding: 10px; border-radius: 6px; resize: vertical; font-size: 14px;
        }
        #jd-tag-input:focus { outline: none; border-color: #0066cc; }
        .jd-tag-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 5px; }

        .jd-slider { -webkit-appearance: none; width: 100px; height: 4px; background: #444; border-radius: 2px; outline: none; }
        .jd-slider::-webkit-slider-thumb {
            -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%;
            background: #0066cc; cursor: pointer; border: 2px solid #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.3);
        }
        .jd-switch { position: relative; display: inline-block; width: 44px; height: 24px; }
        .jd-switch input { opacity: 0; width: 0; height: 0; }
        .jd-slider-switch {
            position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0;
            background-color: #444; transition: .3s; border-radius: 24px;
        }
        .jd-slider-switch:before {
            position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px;
            background-color: white; transition: .3s; border-radius: 50%; box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        input:checked + .jd-slider-switch { background-color: #0066cc; }
        input:checked + .jd-slider-switch:before { transform: translateX(20px); }
        .jd-val-display { font-size: 13px; color: #aaa; min-width: 35px; text-align: right; font-variant-numeric: tabular-nums; }

        /* 詳情 Modal */
        #jd-modal-overlay {
            position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
            background: rgba(0, 0, 0, 0.7); z-index: 10000; display: none;
            justify-content: center; align-items: center; opacity: 0; transition: opacity 0.2s ease;
        }
        #jd-modal-overlay.active { display: flex; opacity: 1; }
        #jd-modal-content {
            background: #ffffff; width: 90%; max-width: 1200px; height: 90%;
            border-radius: 8px; position: relative;
            box-shadow: 0 5px 30px rgba(0,0,0,0.3); color: #333333;
            display: flex; flex-direction: column; overflow: hidden; padding: 0;
        }
        #jd-modal-toolbar {
            flex: 0 0 auto; height: 44px; background: rgba(255, 255, 255, 0.98);
            border-bottom: 1px solid #eaeaea; display: flex; align-items: center;
            justify-content: space-between; padding: 0 15px; z-index: 100;
        }
        #jd-modal-body { flex: 1 1 auto; overflow: hidden; position: relative; padding: 0; }
        #jd-modal-iframe { width: 100%; height: 100%; border: none; display: block; }
        #jd-modal-close {
            width: 32px; height: 32px; cursor: pointer; color: #ff4444;
            transition: transform 0.2s, color 0.2s; display: flex; align-items: center; justify-content: center;
        }
        #jd-modal-close:hover { transform: scale(1.1); color: #d50000; }
        #jd-modal-close svg { width: 24px; height: 24px; fill: currentColor; }
        #jd-modal-back {
            width: 32px; height: 32px; cursor: pointer; color: #2ecc71;
            transition: transform 0.2s, color 0.2s; display: flex;
            align-items: center; justify-content: center;
        }
        #jd-modal-back:hover { transform: scale(1.1); color: #27ae60; }
        #jd-modal-back svg { width: 24px; height: 24px; fill: currentColor; }

        /* 過濾列表 Modal */
        #jd-filtered-modal-overlay {
            position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
            background: rgba(0, 0, 0, 0.5); z-index: 11000; display: none;
            justify-content: center; align-items: center; opacity: 0; transition: opacity 0.2s ease;
        }
        #jd-filtered-modal-overlay.active { display: flex; opacity: 1; }
        #jd-filtered-modal-content {
            background: #ffffff; width: 85%; max-width: 1100px; height: 85%;
            border-radius: 8px; position: relative;
            box-shadow: 0 5px 25px rgba(0,0,0,0.5); color: #333333;
            display: flex; flex-direction: column; overflow: hidden; padding: 0;
        }
        #jd-filtered-modal-header {
            flex: 0 0 auto; height: 44px; background: #f8f9fa;
            border-bottom: 1px solid #eaeaea; display: flex; align-items: center;
            justify-content: space-between; padding: 0 20px;
        }
        #jd-filtered-modal-body { flex: 1 1 auto; overflow-y: auto; padding: 25px; }
        #jd-filtered-modal-close {
            font-size: 24px; cursor: pointer; color: #999; line-height: 1;
        }
        #jd-filtered-modal-close:hover { color: #333; }

        /* 分組樣式 */
        .jd-group-header {
            background: #f5f5f5; color: #333; padding: 10px 15px;
            font-weight: bold; cursor: pointer; border-radius: 4px;
            margin-bottom: 10px; display: flex; justify-content: space-between;
            align-items: center; border-left: 5px solid #0066cc;
        }
        .jd-group-header:hover { background: #e8e8e8; }
        .jd-group-header span { font-size: 12px; color: #666; transition: transform 0.2s; }
        .jd-group-header.collapsed span { transform: rotate(-90deg); }
        .jd-group-content { margin-bottom: 20px; overflow: hidden; transition: max-height 0.3s ease-out; }
        .jd-group-content.collapsed { display: none; }
        .jd-hidden-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 15px; width: 100%; }
        @media (max-width: 768px) { .jd-hidden-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

        .jd-loading {
            position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
            text-align: center; padding: 30px; font-size: 16px; color: #888;
        }
        
        .jd-hidden-item-container { padding-bottom: 50px; }
        .jd-hidden-item-container a { color: #0066cc; }
        .jd-hidden-item-container img { max-width: 100%; border-radius: 4px; }
        
        .jd-translated-title {
            font-size: 1.5rem; font-weight: bold; color: #111;
            margin-top: 10px; padding: 5px 0; border-top: 1px dashed #eee; line-height: 1.4;
        }
        .jd-block-tag-btn {
            display: inline-flex; align-items: center; justify-content: center;
            width: 20px; height: 20px; margin-left: 5px; cursor: pointer;
            font-size: 12px; color: #999; border: 1px solid #ddd; border-radius: 4px;
            transition: all 0.2s;
        }
        .jd-block-tag-btn:hover { background: #ffebee; color: #d32f2f; border-color: #ef9a9a; }
    `;

    GM_addStyle(styles);

    // --- 核心工具 ---
    function findMainContainer(doc = document) {
        const firstItem = doc.querySelector('.movie-list .item, .grid .item');
        if (firstItem) return firstItem.parentElement;
        const candidates = ['.grid-cols-2', '.grid-cols-3', '.grid-cols-4', '.grid-cols-5', '#videos .grid', '.section .grid', '.movie-list'];
        for (let selector of candidates) { const el = doc.querySelector(selector); if (el) return el; }
        return null;
    }

    function getHiddenTagsArray() {
        return state.config.hiddenTags.split('/').map(tag => tag.trim()).filter(tag => tag.length > 0);
    }

    function escapeRegExp(string) {
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }

    function addTagToFilter(tag) {
        const currentTags = getHiddenTagsArray();
        if (!currentTags.includes(tag)) {
            currentTags.push(tag);
            const newStr = currentTags.join('/');
            state.config.hiddenTags = newStr; localStorage.setItem('jd_tags', newStr);
            const tagInput = document.getElementById('jd-tag-input'); if (tagInput) tagInput.value = newStr;
            filterItems();
            const btn = document.createElement('div');
            btn.style.position = 'fixed'; btn.style.bottom = '20px'; btn.style.left = '50%';
            btn.style.transform = 'translateX(-50%)'; btn.style.background = 'rgba(0,0,0,0.8)';
            btn.style.color = 'white'; btn.style.padding = '10px 20px'; btn.style.borderRadius = '5px';
            btn.style.zIndex = '20002'; btn.innerText = `已將「${tag}」加入過濾清單`;
            document.body.appendChild(btn); setTimeout(() => btn.remove(), 2000);
        }
    }
    
    function translateText(text) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=zh-TW&dt=t&q=" + encodeURIComponent(text),
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        let translatedText = "";
                        if (data && data[0]) {
                            data[0].forEach(item => { if (item[0]) translatedText += item[0]; });
                        }
                        resolve(translatedText);
                    } catch (e) { reject(e); }
                },
                onerror: function(err) { reject(err); }
            });
        });
    }

    function toggleQuickBlock(show) {
        if (show) document.body.classList.remove('jd-hide-quick-block');
        else document.body.classList.add('jd-hide-quick-block');
        
        const iframe = document.getElementById('jd-modal-iframe');
        if (iframe && iframe.contentDocument && iframe.contentDocument.body) {
            if (show) iframe.contentDocument.body.classList.remove('jd-hide-quick-block');
            else iframe.contentDocument.body.classList.add('jd-hide-quick-block');
        }
    }

    function toggleSearchBar(hide) {
        if (hide) {
            document.body.classList.add('jd-hide-search');
        } else {
            document.body.classList.remove('jd-hide-search');
        }

        const searchInputs = document.querySelectorAll('form input[name="q"]');
        searchInputs.forEach(input => {
            const form = input.closest('form');
            if (!form) return;

            let target = null;
            const navItem = form.closest('.navbar-item');
            if (navItem) target = navItem;
            else {
                let parent = form.parentElement;
                while (parent && parent.tagName !== 'BODY') {
                    if (parent.classList.contains('section') || parent.classList.contains('hero') || parent.tagName === 'SECTION') {
                        const hasContent = parent.querySelector('.movie-list') || 
                                           parent.querySelector('.grid-cols-2') || 
                                           parent.querySelector('.grid-cols-4') ||
                                           parent.querySelector('.video-meta-panel') ||
                                           parent.id === 'videos';
                        if (!hasContent) target = parent;
                        else parent.classList.toggle('jd-collapsed', hide);
                        break;
                    }
                    parent = parent.parentElement;
                }
            }

            if (!target || !hide) {
                form.classList.toggle('jd-search-hidden', hide);
                let sibling = form.nextElementSibling;
                while(sibling) {
                    if (sibling.classList.contains('tags') || sibling.tagName === 'DIV' || sibling.tagName === 'P') {
                        sibling.classList.toggle('jd-search-hidden', hide);
                    }
                    sibling = sibling.nextElementSibling;
                }
            }

            if (target) target.classList.toggle('jd-search-hidden', hide);
        });
    }
    
    // 切換懸浮搜尋欄 (展開/收起)
    function toggleFloatingSearch(show) {
        const btn = document.getElementById('jd-search-btn');
        const bar = document.getElementById('jd-floating-search-bar');
        if (show) {
            if (btn) btn.style.display = 'flex';
        } else {
            if (btn) btn.style.display = 'none';
            if (bar) bar.classList.remove('active');
        }
    }

    function isMatch(text, tag) {
        if (!tag) return false;
        if (/[^\u0000-\u007f]/.test(tag)) {
            return text.includes(tag);
        } else {
            try {
                const regex = new RegExp(`\\b${escapeRegExp(tag)}\\b`, 'i');
                return regex.test(text);
            } catch (e) {
                return text.includes(tag);
            }
        }
    }

    function processContainerItems(container) {
        const items = container.querySelectorAll('.item');
        const minRating = state.config.minRating;
        const hiddenTags = getHiddenTagsArray();
        
        let localRatingCount = 0;
        let localTextCount = 0;

        items.forEach(item => {
            let isVisible = true;
            let hiddenReason = null; 
            let hiddenMeta = null; 
            
            let itemText = item.textContent.toLowerCase();
            item.querySelectorAll('[title]').forEach(el => itemText += ' ' + (el.getAttribute('title') || '').toLowerCase());
            item.querySelectorAll('[alt]').forEach(el => itemText += ' ' + (el.getAttribute('alt') || '').toLowerCase());
            item.querySelectorAll('img[data-original]').forEach(el => itemText += ' ' + (el.getAttribute('data-original') || '').toLowerCase());

            if (minRating > 0) {
                const match = itemText.match(/(\d+\.\d+)分/);
                if (match) {
                    const score = parseFloat(match[1]);
                    if (score < minRating) {
                        isVisible = false; hiddenReason = 'rating'; hiddenMeta = score;
                    }
                }
            }

            if (isVisible && hiddenTags.length > 0) {
                const matchedTag = hiddenTags.find(tag => isMatch(itemText, tag.toLowerCase()));
                if (matchedTag) {
                    isVisible = false; 
                    hiddenReason = 'text';
                    hiddenMeta = matchedTag;
                }
            }

            if (!isVisible) {
                const clone = document.importNode(item, true);
                clone.style.display = ''; 
                clone.querySelectorAll('img').forEach(img => {
                    if(img.dataset.src) img.src = img.dataset.src;
                    if(img.dataset.original) img.src = img.dataset.original;
                    img.style.opacity = 1;
                });
                
                if (hiddenReason === 'rating') {
                    state.hiddenItems.rating.push({ item: clone, score: hiddenMeta });
                    localRatingCount++;
                } else if (hiddenReason === 'text') {
                    state.hiddenItems.text.push({ item: clone, tag: hiddenMeta });
                    localTextCount++;
                }
            }
            item.style.display = isVisible ? '' : 'none';
        });
        return { r: localRatingCount, t: localTextCount };
    }

    function filterItems() {
        state.hiddenItems.rating = [];
        state.hiddenItems.text = [];
        let totalRating = 0;
        let totalText = 0;

        if (!state.mainContainer) state.mainContainer = findMainContainer();
        if (state.mainContainer) {
            state.mainContainer.id = 'jd-main-grid'; 
            const res = processContainerItems(state.mainContainer);
            totalRating += res.r;
            totalText += res.t;
        }

        const iframe = document.getElementById('jd-modal-iframe');
        if (iframe && iframe.contentDocument) {
            const iframeContainer = findMainContainer(iframe.contentDocument);
            if (iframeContainer) {
                iframeContainer.classList.add('jd-main-grid'); 
                const res = processContainerItems(iframeContainer);
                totalRating += res.r;
                totalText += res.t;
            }
        }

        state.hiddenByRatingCount = totalRating;
        state.hiddenByTextCount = totalText;
        updateStatusBar();
    }

    function updateStatusBar() {
        const statusBar = document.getElementById('jd-status-bar');
        if (statusBar) {
            statusBar.innerHTML = `
                已隱藏:
                <span id="jd-btn-show-rating" class="jd-status-btn" title="點擊檢視因評分隱藏的影片">評分: ${state.hiddenByRatingCount}</span>
                / 
                <span id="jd-btn-show-text" class="jd-status-btn" title="點擊檢視因文字隱藏的影片">文字: ${state.hiddenByTextCount}</span>
            `;
            document.getElementById('jd-btn-show-rating').onclick = (e) => { e.stopPropagation(); openHiddenItemsModal('rating'); };
            document.getElementById('jd-btn-show-text').onclick = (e) => { e.stopPropagation(); openHiddenItemsModal('text'); };
        }
    }

    function openHiddenItemsModal(type) {
        const modal = document.getElementById('jd-filtered-modal-overlay');
        const container = document.getElementById('jd-filtered-modal-body');
        const title = document.getElementById('jd-filtered-modal-header').querySelector('span'); 
        
        const titleText = type === 'rating' ? '因評分過低隱藏的影片' : '因文字過濾隱藏的影片';
        const itemsData = type === 'rating' ? state.hiddenItems.rating : state.hiddenItems.text;

        title.innerText = titleText;
        container.innerHTML = '';

        if (itemsData.length === 0) {
            container.innerHTML = '<div style="text-align:center; padding:20px; color:#666;">沒有相關項目</div>';
            modal.classList.add('active');
            return;
        }

        const groups = {};
        if (type === 'rating') {
            // v3.86: 調整評分分組 (細分)
            const ranges = ['4.1-5.0', '3.1-4.0', '2.1-3.0', '1.1-2.0', '0.1-1.0', '0'];
            ranges.forEach(r => groups[r] = []);
            itemsData.forEach(data => {
                const s = data.score;
                let key = '0';
                if (s > 4.0) key = '4.1-5.0';
                else if (s > 3.0) key = '3.1-4.0';
                else if (s > 2.0) key = '2.1-3.0';
                else if (s > 1.0) key = '1.1-2.0';
                else if (s > 0) key = '0.1-1.0';
                if (groups[key]) groups[key].push(data.item);
            });
        } else {
            itemsData.forEach(data => {
                const tag = data.tag || '其他';
                if (!groups[tag]) groups[tag] = [];
                groups[tag].push(data.item);
            });
        }

        const wrapper = document.createElement('div');
        wrapper.className = 'jd-hidden-item-container';
        
        Object.keys(groups).forEach(groupName => {
            const items = groups[groupName];
            if (items.length === 0) return;

            const groupDiv = document.createElement('div');
            groupDiv.className = 'jd-hidden-group';
            
            const header = document.createElement('div');
            header.className = 'jd-group-header';
            header.innerHTML = `${groupName} (${items.length}) <span>▼</span>`;
            
            const content = document.createElement('div');
            content.className = 'jd-group-content'; 
            
            const grid = document.createElement('div');
            grid.className = 'jd-hidden-grid';
            items.forEach(item => {
                item.querySelector('a').onclick = (e) => {
                    e.preventDefault(); e.stopPropagation();
                    openModal(item.querySelector('a').href, false, true);
                };
                grid.appendChild(item);
            });
            
            content.appendChild(grid);
            groupDiv.appendChild(header);
            groupDiv.appendChild(content);
            wrapper.appendChild(groupDiv);

            header.onclick = () => {
                content.classList.toggle('collapsed');
                header.classList.toggle('collapsed');
            };
        });
        
        container.appendChild(wrapper);
        modal.classList.add('active');
    }

    // --- 初始化 UI ---
    function initUI() {
        const wrapper = document.createElement('div');
        wrapper.id = 'jd-wrapper';
        wrapper.innerHTML = `
            <div id="jd-panel">
                <div class="jd-row"><span class="jd-label">每行數量</span><input type="number" id="jd-input-cols" class="jd-input-num" min="1" max="10" value="${state.config.cols}"></div>
                <div class="jd-row"><span class="jd-label">懸浮視窗</span><label class="jd-switch"><input type="checkbox" id="jd-switch-modal" ${state.config.modalPreview ? 'checked' : ''}><span class="jd-slider-switch"></span></label></div>
                <div class="jd-row"><span class="jd-label">標題翻譯</span><label class="jd-switch"><input type="checkbox" id="jd-switch-translate" ${state.config.translateTitle ? 'checked' : ''}><span class="jd-slider-switch"></span></label></div>
                <div class="jd-row"><span class="jd-label">無縫瀏覽</span><label class="jd-switch"><input type="checkbox" id="jd-switch-scroll" ${state.config.infiniteScroll ? 'checked' : ''}><span class="jd-slider-switch"></span></label></div>
                <div class="jd-row"><span class="jd-label">兩側留白</span><div style="display:flex; align-items:center; gap:8px;"><input type="range" id="jd-input-margin" class="jd-slider" min="0" max="20" step="1" value="${state.config.margin}"><span id="jd-val-margin" class="jd-val-display" style="width:35px;">${state.config.margin}%</span></div></div>
                <div class="jd-row"><span class="jd-label">評分過濾</span><div style="display:flex; align-items:center; gap:8px;"><input type="range" id="jd-input-rating" class="jd-slider" min="0" max="5" step="0.1" value="${state.config.minRating}"><span id="jd-val-rating" class="jd-val-display" style="width:30px;">${state.config.minRating}</span></div></div>
                <div class="jd-row"><span class="jd-label">文字過濾</span><button id="jd-btn-edit-tags" class="jd-btn">編輯過濾文字</button></div>
                <div class="jd-row"><span class="jd-label">快速過濾</span><label class="jd-switch"><input type="checkbox" id="jd-switch-quickblock" ${state.config.showQuickBlock ? 'checked' : ''}><span class="jd-slider-switch"></span></label></div>
                <div class="jd-row"><span class="jd-label">隱藏搜索</span><label class="jd-switch"><input type="checkbox" id="jd-switch-hidesearch" ${state.config.hideSearchBar ? 'checked' : ''}><span class="jd-slider-switch"></span></label></div>
                <div class="jd-row"><span class="jd-label">懸浮搜索</span><label class="jd-switch"><input type="checkbox" id="jd-switch-floating" ${state.config.floatingSearch ? 'checked' : ''}><span class="jd-slider-switch"></span></label></div>
                <div id="jd-status-bar" class="jd-status-bar"></div>
            </div>
            
            <div id="jd-action-row">
                <div id="jd-floating-search-bar">
                    <form action="/search" method="get" id="jd-search-form" target="_blank">
                        <div style="flex: 0 0 75px;">
                             <select name="f" id="jd-search-select">
                                 <option value="1">影片</option>
                                 <option value="actor">演員</option>
                                 <option value="code">番號</option>
                                 <option value="series">系列</option>
                                 <option value="maker">片商</option>
                                 <option value="director">導演</option>
                                 <option value="playable">可播放</option>
                                 <option value="sub">字幕</option>
                             </select>
                        </div>
                        <input type="text" name="q" id="jd-search-input" placeholder="搜尋番號、演員..." autocomplete="off">
                        <div class="jd-search-icon-btn" id="jd-img-search-btn" title="以圖搜片">
                            <svg viewBox="0 0 24 24"><path d="M12 8.8a3.2 3.2 0 1 0 0 6.4 3.2 3.2 0 0 0 0-6.4zM9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></svg>
                        </div>
                        <button type="submit" class="jd-search-icon-btn" title="搜尋">
                             <svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
                        </button>
                        <a href="https://javdb.com/advanced_search" target="_blank" class="jd-search-icon-btn" title="進階搜尋" id="jd-adv-search-btn">
                             <svg viewBox="0 0 24 24"><path d="M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z"/></svg>
                        </a>
                    </form>
                     <form action="/search_by_image" method="post" enctype="multipart/form-data" target="_blank" id="jd-img-form" style="display:none;">
                        <input type="file" name="file" id="jd-file-input" accept="image/*">
                    </form>
                </div>
                <div id="jd-search-btn" class="jd-round-btn" style="display: none;">
                    <svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
                </div>
                <div id="jd-gear-btn" class="jd-round-btn">
                    <svg viewBox="0 0 24 24"><path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.06-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.06,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/></svg>
                </div>
            </div>
        `;
        document.body.appendChild(wrapper);

        const tagPanel = document.createElement('div');
        tagPanel.id = 'jd-tag-panel';
        tagPanel.innerHTML = `<p>輸入欲隱藏的文字,使用 / 分隔</p><textarea id="jd-tag-input" placeholder="例如: VR/歐美/重口味"></textarea><div class="jd-tag-actions"><button id="jd-tag-cancel" class="jd-btn" style="background:#555;">取消</button><button id="jd-tag-save" class="jd-btn" style="background:#0066cc;">儲存</button></div>`;
        document.body.appendChild(tagPanel);

        // 3. 詳情 Modal
        const modal = document.createElement('div');
        modal.id = 'jd-modal-overlay';
        modal.innerHTML = `<div id="jd-modal-content"><div id="jd-modal-toolbar"><div id="jd-modal-back" title="上一頁"><svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg></div><div style="flex:1;"></div><div id="jd-modal-close" title="關閉"><svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></div></div><div id="jd-modal-body"></div></div>`;
        document.body.appendChild(modal);

        // 4. 過濾列表 Modal
        const filteredModal = document.createElement('div');
        filteredModal.id = 'jd-filtered-modal-overlay';
        filteredModal.innerHTML = `<div id="jd-filtered-modal-content"><div id="jd-filtered-modal-header"><span id="jd-filtered-title"></span><div id="jd-filtered-modal-close">×</div></div><div id="jd-filtered-modal-body"></div></div>`;
        document.body.appendChild(filteredModal);
        
        const closeFiltered = () => filteredModal.classList.remove('active');
        filteredModal.addEventListener('click', (e) => { if (e.target === filteredModal) closeFiltered(); });
        document.getElementById('jd-filtered-modal-close').addEventListener('click', closeFiltered);

        bindSettingsEvents(tagPanel, modal);

        document.documentElement.style.setProperty('--jd-cols', state.config.cols);
        document.documentElement.style.setProperty('--jd-margin', state.config.margin + '%');

        setTimeout(() => { 
            state.mainContainer = findMainContainer();
            if (state.mainContainer) {
                state.mainContainer.id = 'jd-main-grid';
                filterItems();
            }
            toggleQuickBlock(state.config.showQuickBlock);
            toggleSearchBar(state.config.hideSearchBar);
            toggleFloatingSearch(state.config.floatingSearch);
            updateStatusBar(); 
            
            state.searchObserver = new MutationObserver(() => {
                if (state.config.hideSearchBar) toggleSearchBar(true);
            });
            state.searchObserver.observe(document.body, { childList: true, subtree: true });
        }, 500);

        document.body.addEventListener('click', (e) => {
            if (!state.config.modalPreview) return;
            
            const target = e.target;
            const link = target.closest('a');
            
            if (!link || !link.closest('.item')) return;

            if (link.hasAttribute('data-method') || 
                link.hasAttribute('data-confirm') || 
                link.classList.contains('button')) {
                return; 
            }
            
            const text = link.innerText.trim();
            if (['刪除', 'Delete', '訂正', 'Edit'].some(key => text.includes(key))) return;

            if (link.href.includes('/v/') && !link.closest('#jd-panel') && !link.closest('.jd-hidden-group') && !e.ctrlKey && !e.shiftKey && !e.metaKey) {
                e.preventDefault(); 
                e.stopPropagation(); 
                openModal(link.href, false, true);
            }
        });
    }
    
    function bindSettingsEvents(tagPanel, modal) {
        const gearBtn = document.getElementById('jd-gear-btn');
        const searchBtn = document.getElementById('jd-search-btn'); 
        const panel = document.getElementById('jd-panel');
        const searchBar = document.getElementById('jd-floating-search-bar'); 
        const tagInput = document.getElementById('jd-tag-input');
        
        searchBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            searchBar.classList.toggle('active');
            if (searchBar.classList.contains('active')) {
                document.getElementById('jd-search-input').focus();
            }
        });
        
        document.getElementById('jd-search-form').addEventListener('submit', (e) => {
            e.preventDefault();
            const q = document.getElementById('jd-search-input').value;
            const f = document.getElementById('jd-search-select').value;
            if (q.trim()) {
                const url = `${window.location.origin}/search?q=${encodeURIComponent(q)}&f=${f}`;
                GM_openInTab(url, { active: false, insert: true });
                searchBar.classList.remove('active');
            }
        });
        
        document.getElementById('jd-img-search-btn').addEventListener('click', () => {
            document.getElementById('jd-file-input').click();
        });
        document.getElementById('jd-file-input').addEventListener('change', (e) => {
            if (e.target.files.length > 0) {
                document.getElementById('jd-img-form').submit();
                searchBar.classList.remove('active');
                setTimeout(() => { e.target.value = ''; }, 1000); 
            }
        });

        document.addEventListener('click', (e) => { 
            if (!wrapper.contains(e.target) && panel.classList.contains('active') && !tagPanel.contains(e.target)) { 
                panel.classList.remove('active'); setTimeout(() => panel.style.display = 'none', 300); 
            }
            if (!wrapper.contains(e.target) && searchBar.classList.contains('active')) {
                searchBar.classList.remove('active');
            }
        });

        gearBtn.addEventListener('click', (e) => { e.stopPropagation(); const isActive = panel.classList.contains('active'); if (isActive) { panel.classList.remove('active'); setTimeout(() => panel.style.display = 'none', 300); } else { panel.style.display = 'flex'; requestAnimationFrame(() => panel.classList.add('active')); } });
        document.getElementById('jd-input-cols').addEventListener('change', (e) => { const val = parseInt(e.target.value); if (val >= 1 && val <= 10) { state.config.cols = val; localStorage.setItem('jd_cols', val); document.documentElement.style.setProperty('--jd-cols', val); const iframe = document.getElementById('jd-modal-iframe'); if (iframe && iframe.contentDocument) iframe.contentDocument.documentElement.style.setProperty('--jd-cols', val); } });
        document.getElementById('jd-switch-hidesearch').addEventListener('change', (e) => { state.config.hideSearchBar = e.target.checked; localStorage.setItem('jd_hide_search', e.target.checked); toggleSearchBar(e.target.checked); });
        document.getElementById('jd-switch-scroll').addEventListener('change', (e) => { state.config.infiniteScroll = e.target.checked; localStorage.setItem('jd_infinite', e.target.checked); });
        document.getElementById('jd-switch-modal').addEventListener('change', (e) => { state.config.modalPreview = e.target.checked; localStorage.setItem('jd_modal', e.target.checked); });
        document.getElementById('jd-switch-translate').addEventListener('change', (e) => { state.config.translateTitle = e.target.checked; localStorage.setItem('jd_translate_title', e.target.checked); });
        document.getElementById('jd-input-margin').addEventListener('input', (e) => { const val = e.target.value; state.config.margin = val; localStorage.setItem('jd_margin', val); document.documentElement.style.setProperty('--jd-margin', val + '%'); document.getElementById('jd-val-margin').textContent = val + '%'; const iframe = document.getElementById('jd-modal-iframe'); if (iframe && iframe.contentDocument) iframe.contentDocument.documentElement.style.setProperty('--jd-margin', val + '%'); });
        document.getElementById('jd-switch-quickblock').addEventListener('change', (e) => { state.config.showQuickBlock = e.target.checked; localStorage.setItem('jd_quick_block', e.target.checked); toggleQuickBlock(e.target.checked); });
        document.getElementById('jd-switch-floating').addEventListener('change', (e) => { state.config.floatingSearch = e.target.checked; localStorage.setItem('jd_floating_search', e.target.checked); toggleFloatingSearch(e.target.checked); });
        document.getElementById('jd-input-rating').addEventListener('input', (e) => { const val = parseFloat(e.target.value); state.config.minRating = val; document.getElementById('jd-val-rating').innerText = val; localStorage.setItem('jd_rating', val); filterItems(); });
        document.getElementById('jd-btn-edit-tags').addEventListener('click', (e) => { e.stopPropagation(); panel.classList.remove('active'); setTimeout(() => panel.style.display = 'none', 300); tagInput.value = state.config.hiddenTags; tagPanel.classList.add('active'); tagInput.focus(); });
        document.getElementById('jd-tag-save').addEventListener('click', () => { state.config.hiddenTags = tagInput.value; localStorage.setItem('jd_tags', tagInput.value); tagPanel.classList.remove('active'); filterItems(); });
        document.getElementById('jd-tag-cancel').addEventListener('click', () => tagPanel.classList.remove('active'));
        const closeModalFunc = () => { modal.classList.remove('active'); state.modalHistory = []; state.currentModalUrl = null; state.activeMovieUrl = null; document.getElementById('jd-modal-back').style.display = 'none'; setTimeout(() => document.getElementById('jd-modal-body').innerHTML = '', 200); };
        const backModalFunc = () => {
            if (state.modalHistory.length > 0) {
                const prevUrl = state.modalHistory.pop();
                openModal(prevUrl, true, false);
            }
        };

        modal.addEventListener('click', (e) => { if (e.target === modal) closeModalFunc(); });
        document.getElementById('jd-modal-close').addEventListener('click', closeModalFunc);
        document.getElementById('jd-modal-back').addEventListener('click', backModalFunc);
        document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModalFunc(); });
    }

    function initInfiniteScroll() {
        const pagination = document.querySelector('.pagination');
        if (!pagination) return;
        const nextLink = pagination.querySelector('a[rel="next"]');
        if (nextLink) state.nextPageUrl = nextLink.href;
        if (state.config.infiniteScroll && document.documentElement.scrollHeight <= window.innerHeight + 200) loadNextPage();
        window.addEventListener('scroll', () => {
            if (!state.config.infiniteScroll) return;
            if (state.isLoading || !state.nextPageUrl) return;
            const scrollTop = window.scrollY || document.documentElement.scrollTop;
            const clientHeight = window.innerHeight;
            if (scrollTop + clientHeight >= document.documentElement.scrollHeight - DEFAULTS.triggerDistance) loadNextPage();
        });
    }

    function loadNextPage() {
        if (!state.mainContainer) state.mainContainer = findMainContainer();
        if (!state.mainContainer) return;
        state.isLoading = true;
        const loader = document.createElement('div');
        loader.className = 'jd-loading'; loader.innerText = '正在讀取下一頁內容...';
        state.mainContainer.appendChild(loader);
        fetch(state.nextPageUrl).then(res => res.text()).then(html => {
            const doc = new DOMParser().parseFromString(html, 'text/html');
            const newItems = doc.querySelectorAll('.movie-list .item, .grid .item');
            loader.remove();
            if (newItems.length > 0) {
                newItems.forEach(item => {
                    const img = item.querySelector('img');
                    if (img) { if (img.dataset.src) img.src = img.dataset.src; if (img.dataset.original) img.src = img.dataset.original; img.style.opacity = 1; }
                    state.mainContainer.appendChild(item);
                });
                filterItems();
            }
            const nextLink = doc.querySelector('.pagination a[rel="next"]');
            if (nextLink) { state.nextPageUrl = nextLink.href; if (state.config.infiniteScroll && document.documentElement.scrollHeight <= window.innerHeight + 200) setTimeout(loadNextPage, 500); } else { state.nextPageUrl = null; const endMsg = document.createElement('div'); endMsg.className = 'jd-loading'; endMsg.innerText = '--- 已經到底了 ---'; state.mainContainer.appendChild(endMsg); }
            state.isLoading = false;
        }).catch(() => { loader.innerText = '載入失敗'; state.isLoading = false; });
    }

    function openModal(url, isBack = false, isRoot = false) {
        const modal = document.getElementById('jd-modal-overlay');
        const body = document.getElementById('jd-modal-body');
        const backBtn = document.getElementById('jd-modal-back');
        const absUrl = new URL(url, window.location.href).href;
        state.activeMovieUrl = absUrl;

        if (isRoot) {
            state.modalHistory = [];
            state.currentModalUrl = url;
            backBtn.style.display = 'none'; 
        } else if (!isBack) {
            if (state.currentModalUrl) state.modalHistory.push(state.currentModalUrl);
            state.currentModalUrl = url;
            backBtn.style.display = 'flex';
        } else {
            state.currentModalUrl = url;
            backBtn.style.display = state.modalHistory.length > 0 ? 'flex' : 'none';
        }
        
        if (document.getElementById('jd-filtered-modal-overlay').classList.contains('active')) {
            modal.style.zIndex = 12000;
        } else {
            modal.style.zIndex = 10000;
        }

        body.innerHTML = '<div class="jd-loading" style="color:#666; font-size: 20px; padding-top: 100px;">正在讀取...</div>';
        const iframe = document.createElement('iframe');
        iframe.id = 'jd-modal-iframe'; iframe.src = url; iframe.style.width = '100%'; iframe.style.height = '100%'; iframe.style.border = 'none'; iframe.style.visibility = 'hidden'; iframe.style.position = 'absolute';
        
        iframe.onload = function() {
            const doc = iframe.contentDocument;
            if (doc) {
                // --- v3.82 修正:歷史紀錄同步邏輯 ---
                const currentFrameUrl = doc.location.href;
                // 如果 Iframe 內部的網址與腳本記錄的當前網址不同 (且不是 about:blank)
                // 代表發生了「原生頁面跳轉」(如點擊訂正),需要補上歷史紀錄
                if (state.currentModalUrl && currentFrameUrl !== state.currentModalUrl && currentFrameUrl !== 'about:blank') {
                    state.modalHistory.push(state.currentModalUrl);
                    state.currentModalUrl = currentFrameUrl;
                    backBtn.style.display = 'flex'; // 顯示上一頁
                }

                const style = doc.createElement('style');
                style.textContent = `
                    :root { --jd-cols: ${state.config.cols}; --jd-margin: ${state.config.margin}%; }
                    #jd-main-grid, .jd-main-grid, .movie-list, .grid { display: grid !important; grid-template-columns: repeat(var(--jd-cols), minmax(0, 1fr)) !important; width: 100% !important; }
                    @media (max-width: 768px) { #jd-main-grid, .jd-main-grid, .movie-list, .grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; } }
                    .section .container { max-width: calc(100% - (var(--jd-margin) * 2)) !important; width: calc(100% - (var(--jd-margin) * 2)) !important; }
                    body.jd-hide-search nav.sub-header:nth-child(3), body.jd-hide-search #search-bar-container > div.column { display: none !important; }
                    body.jd-hide-search form[action="/search"]:not(#jd-search-form), body.jd-hide-search .navbar-item:has(form[action="/search"]) { display: none !important; }
                    .navbar, .footer, .ad-banner, .is-hidden-mobile { display: none !important; }
                    html, body { padding-bottom: 80px !important; background-color: #fff !important; height: auto !important; min-height: 100% !important; overflow-y: auto !important; }
                    .section { padding: 20px 10px !important; }
                    .columns > .column.is-3, .columns > .column.is-one-quarter { display: none !important; }
                    .columns > .column.is-9, .columns > .column.is-three-quarters { width: 100% !important; flex: none !important; }
                    body.jd-hide-quick-block .jd-block-tag-btn { display: none !important; }
                    .jd-translated-title { font-size: 1.5rem; font-weight: bold; color: #111; margin-top: 10px; padding: 5px 0; border-top: 1px dashed #eee; line-height: 1.4; }
                `;
                doc.head.appendChild(style);
                
                if (state.config.hideSearchBar) doc.body.classList.add('jd-hide-search');
                if (!state.config.showQuickBlock) doc.body.classList.add('jd-hide-quick-block');
                doc.querySelectorAll('base[target="_blank"]').forEach(b => b.remove());
                doc.querySelectorAll('a[target="_blank"]').forEach(a => a.removeAttribute('target'));
                
                const videoMeta = doc.querySelector('.video-meta-panel');
                if (videoMeta) {
                    if (state.config.translateTitle) {
                        const titleEl = videoMeta.querySelector('.title.is-4') || doc.querySelector('.section .title');
                        if (titleEl) {
                            const existingTrans = titleEl.parentNode.querySelector('.jd-translated-title');
                            if (!existingTrans) {
                                const originalText = titleEl.innerText.trim();
                                translateText(originalText).then(translated => {
                                    if (translated) {
                                        const transDiv = doc.createElement('div'); transDiv.className = 'jd-translated-title'; transDiv.innerText = translated; titleEl.parentNode.insertBefore(transDiv, titleEl.nextSibling);
                                    }
                                });
                            }
                        }
                    }
                    const hiddenTags = getHiddenTagsArray();
                    let foundHiddenTags = [];
                    const metaText = videoMeta.textContent.toLowerCase();
                    const strictMode = true; 
                    hiddenTags.forEach(tag => { if (isMatch(metaText, tag.toLowerCase(), strictMode)) foundHiddenTags.push(tag); });
                    if (foundHiddenTags.length > 0) {
                        const alertDiv = doc.createElement('div');
                        alertDiv.style.background = '#ffebee'; alertDiv.style.color = '#c62828'; alertDiv.style.border = '1px solid #ffcdd2'; alertDiv.style.padding = '10px'; alertDiv.style.borderRadius = '6px'; alertDiv.style.marginBottom = '15px'; alertDiv.style.display = 'flex'; alertDiv.style.alignItems = 'center'; alertDiv.style.justifyContent = 'space-between';
                        alertDiv.innerHTML = `<strong>⚠️ 警告</strong> <span>此影片包含隱藏文字:${foundHiddenTags.join(', ')}</span>`;
                        const mainSection = doc.querySelector('.section .container');
                        if (mainSection) { mainSection.insertBefore(alertDiv, mainSection.firstChild); } else { doc.body.insertBefore(alertDiv, doc.body.firstChild); }
                    }
                    const tagLinks = doc.querySelectorAll('.video-meta-panel .value a');
                    tagLinks.forEach(link => {
                        const btn = doc.createElement('span');
                        btn.innerText = '🚫'; btn.className = 'jd-block-tag-btn'; btn.style.cursor = 'pointer'; btn.style.marginLeft = '5px'; btn.style.fontSize = '12px';
                        btn.onclick = (e) => { e.preventDefault(); e.stopPropagation(); addTagToFilter(link.innerText.trim()); };
                        link.parentNode.insertBefore(btn, link.nextSibling);
                    });
                }
                
                const iContainer = findMainContainer(doc);
                if (iContainer) iContainer.classList.add('jd-main-grid');

                // Iframe 內監聽
                doc.body.addEventListener('click', (e) => {
                    const link = e.target.closest('a');
                    if (!link) return;
                    
                    // 1. 功能按鈕 & 播放器排除 (v3.82 修正)
                    if (link.hasAttribute('data-method') || 
                        link.hasAttribute('data-confirm') || 
                        link.classList.contains('button') ||
                        link.closest('.video-player') || // 排除播放器
                        link.closest('.preview-video-container') || // 排除預覽容器
                        link.classList.contains('play-button') // 排除播放按鈕
                    ) {
                        return;
                    }

                    // 2. 內嵌導航攔截
                    if (link.href.includes('/v/')) {
                         e.preventDefault();
                         e.stopPropagation();
                         openModal(link.href, false, false);
                         return;
                    }

                    // 3. 其他連結
                    if (link.closest('.video-meta-panel') && !link.classList.contains('jd-block-tag-btn')) {
                        if (link.href && !link.href.startsWith('javascript') && !link.href.includes('#') && !link.href.startsWith('magnet:')) {
                            e.preventDefault();
                            e.stopPropagation();
                            GM_openInTab(link.href, { active: false, insert: true });
                        }
                        return;
                    }
                }, true); // 使用 Capture

                body.querySelector('.jd-loading')?.remove();
                iframe.style.visibility = 'visible'; iframe.style.position = 'static'; backBtn.style.display = state.modalHistory.length > 0 ? 'flex' : 'none';
            }
        };
        body.appendChild(iframe);
        modal.classList.add('active');
    }

    function main() {
        setTimeout(() => {
            initUI(); initInfiniteScroll(); 
            console.log('JavDB Enhancer v3.86 Started');
        }, 500); 
    }

    if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } 
    else { main(); }

})();