您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Fluid SWF navigation with pagination
// ==UserScript== // @name Heyuri better /f/ // @namespace http://tampermonkey.net/ // @version 1.0 // @description Fluid SWF navigation with pagination // @author LVO // @match https://img.heyuri.net/f/* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // ==/UserScript== // ==UserScript== // @name Heyuri SWF Navigation - Fluid Navigation // @namespace http://tampermonkey.net/ // @version 19.2 // @description Fluid SWF navigation with pagination // @author You // @match https://img.heyuri.net/f/* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; // Default configuration const defaultConfig = { prevKey: 'ArrowLeft', nextKey: 'ArrowRight', closeKey: 'Escape', autoPageTurn: true, autoOpenFirstLast: true }; // Load configuration let config = { ...defaultConfig, ...GM_getValue('swfNavConfig', {}) }; // CSS styles for UI elements GM_addStyle(` .tm-nav-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; display: flex; justify-content: space-between; align-items: center; pointer-events: none; z-index: 99999; } .tm-nav-btn { background: rgba(0,0,0,0.7); color: white; border: none; border-radius: 50%; width: 60px; height: 60px; font-size: 30px; cursor: pointer; display: flex; align-items: center; justify-content: center; margin: 20px; pointer-events: auto; transition: all 0.3s; } .tm-nav-btn:hover { background: rgba(0,0,0,0.9); transform: scale(1.1); } .tm-prev { margin-left: 20px; } .tm-next { margin-right: 20px; } .tm-controls-container { position: absolute; top: 20px; right: 20px; display: flex; gap: 10px; z-index: 10001; } .tm-close-btn, .tm-options-btn, .tm-reply-btn { background: rgba(0,0,0,0.7); color: white; border: none; border-radius: 5px; padding: 10px 15px; cursor: pointer; font-weight: bold; pointer-events: auto; transition: all 0.3s; display: flex; align-items: center; gap: 5px; } .tm-close-btn { background: rgba(200,0,0,0.7); } .tm-close-btn:hover { background: rgba(200,0,0,0.9); } .tm-options-btn:hover, .tm-reply-btn:hover { background: rgba(0,0,0,0.9); } .tm-options-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 10px; box-shadow: 0 0 20px rgba(0,0,0,0.5); z-index: 10002; max-width: 90%; width: 350px; display: none; } .tm-options-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 10001; display: none; } .tm-options-content { display: flex; flex-direction: column; gap: 15px; } .tm-option-title { margin: 0 0 15px 0; padding-bottom: 10px; border-bottom: 1px solid #eee; } .tm-option-row { display: flex; flex-direction: column; margin-bottom: 10px; } .tm-option-label { margin-bottom: 5px; font-weight: bold; } .tm-key-input { padding: 8px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; background: #f9f9f9; } .tm-option-buttons { display: flex; justify-content: flex-end; gap: 10px; margin-top: 15px; } .tm-option-btn { padding: 8px 15px; border: none; border-radius: 4px; cursor: pointer; } .tm-save-btn { background: #4CAF50; color: white; } .tm-cancel-btn { background: #f44336; color: white; } .tm-checkbox-container { display: flex; align-items: center; gap: 8px; } .tm-page-indicator { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.7); color: white; padding: 5px 15px; border-radius: 20px; font-size: 14px; pointer-events: none; z-index: 10001; opacity: 0; transition: opacity 0.3s; } .tm-page-indicator.show { opacity: 1; } .tm-file-counter { position: absolute; bottom: 20px; right: 20px; background: rgba(0,0,0,0.7); color: white; padding: 5px 15px; border-radius: 20px; font-size: 14px; pointer-events: none; z-index: 10001; } .tm-reply-count { background: rgba(255,255,255,0.2); border-radius: 10px; padding: 2px 8px; margin-left: 5px; } .sub-option { margin-left: 20px; margin-top: 8px; } `); // Global variables let currentFileIndex = -1; let swfFiles = []; let navigationActive = false; let nextPageUrl = ''; let prevPageUrl = ''; let autoOpenType = null; // Create UI elements function createUI() { // Create navigation container const navContainer = document.createElement('div'); navContainer.className = 'tm-nav-container'; navContainer.style.display = 'none'; document.body.appendChild(navContainer); // Navigation buttons const prevButton = document.createElement('button'); prevButton.className = 'tm-nav-btn tm-prev'; prevButton.innerHTML = '←'; navContainer.appendChild(prevButton); const nextButton = document.createElement('button'); nextButton.className = 'tm-nav-btn tm-next'; nextButton.innerHTML = '→'; navContainer.appendChild(nextButton); // Controls container const controlsContainer = document.createElement('div'); controlsContainer.className = 'tm-controls-container'; navContainer.appendChild(controlsContainer); // Close button const closeButton = document.createElement('button'); closeButton.className = 'tm-close-btn'; closeButton.innerHTML = 'Close'; controlsContainer.appendChild(closeButton); // Reply button const replyButton = document.createElement('button'); replyButton.className = 'tm-reply-btn'; replyButton.innerHTML = '💬 Reply'; replyButton.title = 'Go to thread'; controlsContainer.appendChild(replyButton); // Options button const optionsButton = document.createElement('button'); optionsButton.className = 'tm-options-btn'; optionsButton.innerHTML = '⚙️ Options'; optionsButton.title = 'Options'; controlsContainer.appendChild(optionsButton); // Page indicator const pageIndicator = document.createElement('div'); pageIndicator.className = 'tm-page-indicator'; document.body.appendChild(pageIndicator); // File counter const fileCounter = document.createElement('div'); fileCounter.className = 'tm-file-counter'; fileCounter.textContent = '0/0'; navContainer.appendChild(fileCounter); // Options panel const optionsBackdrop = document.createElement('div'); optionsBackdrop.className = 'tm-options-backdrop'; document.body.appendChild(optionsBackdrop); const optionsPanel = document.createElement('div'); optionsPanel.className = 'tm-options-panel'; optionsBackdrop.appendChild(optionsPanel); // Return references to important elements return { navContainer, prevButton, nextButton, closeButton, replyButton, optionsButton, pageIndicator, fileCounter, optionsBackdrop, optionsPanel }; } // Initialize script function init() { // Create UI elements const ui = createUI(); // Populate options panel ui.optionsPanel.innerHTML = ` <h3 class="tm-option-title">Shortcut Configuration</h3> <div class="tm-options-content"> <div class="tm-option-row"> <label class="tm-option-label">Previous key:</label> <input type="text" class="tm-key-input" id="tm-prev-key" value="${config.prevKey}" readonly> </div> <div class="tm-option-row"> <label class="tm-option-label">Next key:</label> <input type="text" class="tm-key-input" id="tm-next-key" value="${config.nextKey}" readonly> </div> <div class="tm-option-row"> <label class="tm-option-label">Close key:</label> <input type="text" class="tm-key-input" id="tm-close-key" value="${config.closeKey}" readonly> </div> <div class="tm-option-row"> <div class="tm-checkbox-container"> <input type="checkbox" id="tm-auto-page" ${config.autoPageTurn ? 'checked' : ''}> <label for="tm-auto-page">Automatically go to next/previous page</label> </div> </div> <div class="tm-option-row" id="auto-open-container" style="${config.autoPageTurn ? '' : 'display: none;'}"> <div class="sub-option"> <div class="tm-checkbox-container"> <input type="checkbox" id="tm-auto-open-first-last" ${config.autoOpenFirstLast ? 'checked' : ''}> <label for="tm-auto-open-first-last">Open first/last file after page change</label> </div> </div> </div> <div class="tm-option-buttons"> <button class="tm-option-btn tm-cancel-btn">Cancel</button> <button class="tm-option-btn tm-save-btn">Save</button> </div> </div> `; // Get list of SWF files swfFiles = Array.from(document.querySelectorAll('tr.thread[id^="t11_"]')); // Get next/previous page URLs const pagination = getPaginationLinks(); prevPageUrl = pagination.prev; nextPageUrl = pagination.next; // Get auto-open state autoOpenType = sessionStorage.getItem('swfNavAutoOpen'); // Setup event listeners setupEventListeners(ui); // Start monitoring SWF window monitorSWFWindow(ui); // Open file automatically if needed openAutoFile(ui); } // Function to get pagination links function getPaginationLinks() { const pager = document.getElementById('pager'); if (!pager) return { prev: null, next: null }; const prev = pager.querySelector('td:first-child a'); const next = pager.querySelector('td:last-child a'); return { prev: prev ? prev.href : null, next: next ? next.href : null }; } // Function to show page indicator function showPageIndicator(message, ui) { ui.pageIndicator.textContent = message; ui.pageIndicator.classList.add('show'); setTimeout(() => { ui.pageIndicator.classList.remove('show'); }, 2000); } // Update file counter function updateFileCounter(ui) { if (currentFileIndex >= 0 && currentFileIndex < swfFiles.length) { const totalFiles = swfFiles.length; ui.fileCounter.textContent = `${currentFileIndex + 1}/${totalFiles}`; // Update reply count const repliesCell = swfFiles[currentFileIndex].querySelector('td:nth-child(8)'); if (repliesCell) { const replyCount = repliesCell.textContent.trim(); ui.replyButton.innerHTML = `💬 Reply <span class="tm-reply-count">${replyCount}</span>`; } } else { ui.fileCounter.textContent = '0/0'; ui.replyButton.innerHTML = '💬 Reply'; } } // Open file automatically after page change function openAutoFile(ui) { if (autoOpenType) { setTimeout(() => { let fileToOpen = null; if (autoOpenType === 'first' && swfFiles.length > 0) { fileToOpen = swfFiles[0]; } else if (autoOpenType === 'last' && swfFiles.length > 0) { fileToOpen = swfFiles[swfFiles.length - 1]; } if (fileToOpen) { const embedLink = fileToOpen.querySelector('.flashboardEmbedText'); if (embedLink) { currentFileIndex = swfFiles.indexOf(fileToOpen); embedLink.click(); showNavigation(ui); updateFileCounter(ui); } } // Reset state sessionStorage.removeItem('swfNavAutoOpen'); }, 500); } } function showNavigation(ui) { ui.navContainer.style.display = 'flex'; navigationActive = true; updateFileCounter(ui); } function hideNavigation(ui) { ui.navContainer.style.display = 'none'; navigationActive = false; } function closeModal(ui) { // Close options panel first if open if (ui.optionsPanel.style.display === 'block') { hideOptions(ui); return; } // Otherwise close SWF window const modal = document.getElementById('swfWindow'); const overlay = document.getElementById('darken-embed-screen'); if (modal) modal.remove(); if (overlay) overlay.remove(); hideNavigation(ui); } function goToNext(ui) { navigateFiles(1, ui); } function goToPrev(ui) { navigateFiles(-1, ui); } function navigateFiles(direction, ui) { if (currentFileIndex === -1 || swfFiles.length === 0) return; const newIndex = currentFileIndex + direction; // Navigation within page if (newIndex >= 0 && newIndex < swfFiles.length) { closeModal(ui); setTimeout(() => { const embedLink = swfFiles[newIndex].querySelector('.flashboardEmbedText'); if (embedLink) { currentFileIndex = newIndex; embedLink.click(); updateFileCounter(ui); } }, 100); } // Go to next page else if (newIndex >= swfFiles.length && config.autoPageTurn && nextPageUrl) { showPageIndicator('Loading next page...', ui); // Store state for auto-open if (config.autoPageTurn && config.autoOpenFirstLast) { sessionStorage.setItem('swfNavAutoOpen', 'first'); } window.location.href = nextPageUrl; } // Go to previous page else if (newIndex < 0 && config.autoPageTurn && prevPageUrl) { showPageIndicator('Loading previous page...', ui); // Store state for auto-open if (config.autoPageTurn && config.autoOpenFirstLast) { sessionStorage.setItem('swfNavAutoOpen', 'last'); } window.location.href = prevPageUrl; } // Boundary reached else if (newIndex < 0 && !prevPageUrl) { showPageIndicator('You are on the first page', ui); } else if (newIndex >= swfFiles.length && !nextPageUrl) { showPageIndicator('You are on the last page', ui); } } function showOptions(ui) { ui.optionsBackdrop.style.display = 'block'; ui.optionsPanel.style.display = 'block'; } function hideOptions(ui) { ui.optionsBackdrop.style.display = 'none'; ui.optionsPanel.style.display = 'none'; } function saveConfig(ui) { config = { prevKey: document.getElementById('tm-prev-key').value, nextKey: document.getElementById('tm-next-key').value, closeKey: document.getElementById('tm-close-key').value, autoPageTurn: document.getElementById('tm-auto-page').checked, autoOpenFirstLast: document.getElementById('tm-auto-open-first-last').checked }; GM_setValue('swfNavConfig', config); hideOptions(ui); showPageIndicator('Configuration saved!', ui); } function setupEventListeners(ui) { // Navigation buttons ui.prevButton.addEventListener('click', () => goToPrev(ui)); ui.nextButton.addEventListener('click', () => goToNext(ui)); ui.closeButton.addEventListener('click', () => closeModal(ui)); ui.optionsButton.addEventListener('click', () => showOptions(ui)); // Reply button ui.replyButton.addEventListener('click', () => { if (currentFileIndex >= 0 && currentFileIndex < swfFiles.length) { const threadId = swfFiles[currentFileIndex].id.replace('t11_', ''); window.location.href = `koko.php?res=${threadId}`; } }); // Options panel buttons ui.optionsPanel.querySelector('.tm-save-btn').addEventListener('click', () => saveConfig(ui)); ui.optionsPanel.querySelector('.tm-cancel-btn').addEventListener('click', () => hideOptions(ui)); ui.optionsBackdrop.addEventListener('click', (e) => { if (e.target === ui.optionsBackdrop) hideOptions(ui); }); // Key detection for input fields document.querySelectorAll('.tm-key-input').forEach(input => { input.addEventListener('click', function() { this.value = 'Press a key...'; this.dataset.listening = true; }); }); // Handle auto-page option toggle const autoPageCheckbox = document.getElementById('tm-auto-page'); const autoOpenContainer = document.getElementById('auto-open-container'); if (autoPageCheckbox && autoOpenContainer) { autoPageCheckbox.addEventListener('change', function() { autoOpenContainer.style.display = this.checked ? 'block' : 'none'; }); } // Keyboard handling document.addEventListener('keydown', (e) => handleKeyPress(e, ui)); // Detect click on Embed links document.addEventListener('click', e => { if (e.target.classList.contains('flashboardEmbedText')) { // Find parent TR const row = e.target.closest('tr.thread'); if (row && swfFiles) { currentFileIndex = swfFiles.indexOf(row); if (currentFileIndex !== -1) { showNavigation(ui); updateFileCounter(ui); } } } }); } // Keyboard handling function handleKeyPress(e, ui) { // Key detection for configuration const activeInput = document.querySelector('.tm-key-input[data-listening="true"]'); if (activeInput) { e.preventDefault(); activeInput.value = e.key; activeInput.dataset.listening = false; return; } // Close key if (e.key === config.closeKey && navigationActive) { closeModal(ui); e.preventDefault(); } // Navigation with arrow keys if (navigationActive) { if (e.key === config.nextKey) { goToNext(ui); e.preventDefault(); } else if (e.key === config.prevKey) { goToPrev(ui); e.preventDefault(); } } } // Monitor SWF window opening/closing function monitorSWFWindow(ui) { const intervalId = setInterval(() => { const swfWindow = document.getElementById('swfWindow'); if (swfWindow && !navigationActive) { showNavigation(ui); } else if (!swfWindow && navigationActive) { hideNavigation(ui); } }, 500); // Clean up interval on page unload window.addEventListener('beforeunload', () => { clearInterval(intervalId); }); } // Start script when page is fully loaded window.addEventListener('load', init); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址