小窗预览

随心所欲,不离不弃。我在此地,不曾离去。我希望停留在此地,但我也想看着锅里的,吃着碗里的。

当前为 2024-09-01 提交的版本,查看 最新版本

// ==UserScript==
// @name         小窗预览
// @version      3.8.2
// @description  随心所欲,不离不弃。我在此地,不曾离去。我希望停留在此地,但我也想看着锅里的,吃着碗里的。
// @match        *://*/*
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_info
// @namespace https://gf.qytechs.cn/users/217852
// ==/UserScript==

(function() {
    'use strict';

    // State to track dragging, popup status, and overlay visibility
    const state = {
        isDragging: false,
        linkToPreload: null,
        popupWindow: null,
        acrylicOverlay: null,
        overlayVisible: false,
        preloadElement: null, // Track the created preload element
    };

    // Configuration settings with defaults
    const config = {
        windowWidth: GM_getValue('windowWidth', 870),
        windowHeight: GM_getValue('windowHeight', 530),
        blurIntensity: GM_getValue('blurIntensity', 20),
        blurEnabled: GM_getValue('blurEnabled', true),
        closeOnMouseClick: GM_getValue('closeOnMouseClick', true),
        closeOnScroll: GM_getValue('closeOnScroll', true),
        transitionDuration: 220, // 300ms fade-in/fade-out
    };

    // Website-specific configurations
    const specificSites = {
        "bilibili.com": { windowWidth: 1000, windowHeight: 565, enableLeftClickPopup: true },
        "douyin.com": { windowWidth: 1000, windowHeight: 500, enableLeftClickPopup: true },
        "x.com": { windowWidth: 1000, windowHeight: 700, enableLeftClickPopup: true },
        "youtube.com": { windowWidth: 1040, windowHeight: 700, enableLeftClickPopup: false }
    };

    // Utility to delay operations (e.g., to allow preload to complete)
    function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    // Preload a link using a <link rel="prefetch"> element
    async function preloadLink(link) {
        try {
            // Remove any existing preload element to avoid duplication
            removePreloadedLink();

            const preloadElement = document.createElement('link');
            preloadElement.rel = 'prefetch';
            preloadElement.href = link;
            preloadElement.as = 'document';
            preloadElement.onload = () => console.log(`Prefetch successful: ${link}`);
            preloadElement.onerror = () => console.log(`Prefetch failed: ${link}`);
            document.head.appendChild(preloadElement);
            state.preloadElement = preloadElement;

            await delay(1); // Ensure the prefetch operation starts
        } catch (error) {
            console.error('Error in prefetch operation:', error);
        }
    }

    // Remove the preloaded link element from the DOM
    function removePreloadedLink() {
        if (state.preloadElement) {
            state.preloadElement.remove();
            state.preloadElement = null;
        }
    }

    // Create an acrylic overlay with blur effect and fade-in transition
    function createAcrylicOverlay() {
        const acrylicOverlay = document.createElement('div');
        Object.assign(acrylicOverlay.style, {
            position: 'fixed',
            top: '0',
            left: '0',
            width: '100%',
            height: '100%',
            zIndex: '9999',
            backdropFilter: config.blurEnabled ? `blur(${config.blurIntensity}px)` : 'none',
            opacity: '0',
            transition: `opacity ${config.transitionDuration}ms ease-in-out`,
        });

        if (config.closeOnMouseClick) {
            acrylicOverlay.addEventListener('click', handleAcrylicOverlayClick);
        }

        document.body.appendChild(acrylicOverlay);

        // Trigger fade-in effect
        requestAnimationFrame(() => {
            acrylicOverlay.style.opacity = '1';
            state.overlayVisible = true;
        });

        return acrylicOverlay;
    }

    // Handle click on the acrylic overlay (close popup)
    function handleAcrylicOverlayClick(event) {
        if (event.target === state.acrylicOverlay) {
            closePopupWindow();
        }
    }

    // Remove the acrylic overlay with fade-out transition
    function removeAcrylicOverlay() {
        if (state.acrylicOverlay && state.overlayVisible) {
            state.acrylicOverlay.style.opacity = '0';

            setTimeout(() => {
                if (state.acrylicOverlay) {
                    state.acrylicOverlay.remove();
                    state.acrylicOverlay = null;
                    state.overlayVisible = false;
                }
            }, config.transitionDuration);
        }
    }

    // Open a popup window centered on the screen
    function openPopupWindow(link, width = config.windowWidth, height = config.windowHeight) {
        const screenLeft = (window.screen.width - width) / 2;
        const screenTop = (window.screen.height - height) / 3;

        if (!state.popupWindow || state.popupWindow.closed) {
            state.acrylicOverlay = createAcrylicOverlay();
            state.popupWindow = window.open(link, '_blank', `width=${width},height=${height},left=${screenLeft},top=${screenTop}`);
            state.popupWindowChecker = setInterval(checkPopupWindowStatus, 200);
        }
    }

    // Close the popup window and cleanup
    function closePopupWindow() {
        if (state.popupWindow && !state.popupWindow.closed) {
            state.popupWindow.close();
            state.popupWindow = null;
            removeAcrylicOverlay();
            removePreloadedLink();
            window.removeEventListener('scroll', closePopupOnScroll);
        }
    }

    // Check if the popup window is still open; remove overlay if closed
    function checkPopupWindowStatus() {
        if (state.popupWindow && state.popupWindow.closed) {
            removeAcrylicOverlay();
            clearInterval(state.popupWindowChecker);
        }
    }

    // Close the popup window when the page is scrolled
    function closePopupOnScroll() {
        closePopupWindow();
    }

    // Check if the current site matches a specific configuration
    function getSiteConfig() {
        const hostname = window.location.hostname;
        return specificSites[Object.keys(specificSites).find(domain => hostname.includes(domain))];
    }

    // Register menu commands for the script
    function registerMenuCommand(label, action) {
        return GM_registerMenuCommand(label, () => {
            action();
            updateMenuCommands();
        });
    }

    // Menu commands for configuring the script
    const menuCommands = [
        { label: `模糊旧时刻 (${config.blurEnabled ? '开' : '关'})`, action: toggleBlurEffect },
        { label: `模糊的强弱 (${config.blurIntensity})`, action: setBlurIntensity },
        { label: `轻点关闭否 (${config.closeOnMouseClick ? '开' : '关'})`, action: toggleCloseOnMouseClick },
        { label: `滚动关闭否 (${config.closeOnScroll ? '开' : '关'})`, action: toggleCloseOnScroll },
        { label: `不变的大小 (${config.windowWidth}x${config.windowHeight})`, action: setWindowSize },
    ];

    // Toggle blur effect on/off
    function toggleBlurEffect() {
        config.blurEnabled = !config.blurEnabled;
        GM_setValue('blurEnabled', config.blurEnabled);
    }

    // Prompt user to set blur intensity
    function setBlurIntensity() {
        const intensity = prompt('输入模糊强度(0-10):', config.blurIntensity);
        if (intensity !== null) {
            config.blurIntensity = parseInt(intensity, 10);
            GM_setValue('blurIntensity', config.blurIntensity);
        }
    }

    // Toggle the option to close the popup on mouse click
    function toggleCloseOnMouseClick() {
        config.closeOnMouseClick = !config.closeOnMouseClick;
        GM_setValue('closeOnMouseClick', config.closeOnMouseClick);
    }

    // Toggle the option to close the popup on scroll
    function toggleCloseOnScroll() {
        config.closeOnScroll = !config.closeOnScroll;
        handleScrollCommand();
        GM_setValue('closeOnScroll', config.closeOnScroll);
    }

    // Handle scroll event based on user settings
    function handleScrollCommand() {
        if (config.closeOnScroll) {
            window.addEventListener('scroll', closePopupOnScroll, { once: true });
        } else {
            window.removeEventListener('scroll', closePopupOnScroll);
        }
    }

    // Prompt user to set window size
    function setWindowSize() {
        const size = prompt(`输入小窗口宽度x高度(例如: 870x530):`, `${config.windowWidth}x${config.windowHeight}`);
        if (size !== null) {
            const [width, height] = size.split('x').map(val => parseInt(val.trim(), 10));
            if (!isNaN(width) && !isNaN(height)) {
                config.windowWidth = width;
                config.windowHeight = height;
                GM_setValue('windowWidth', config.windowWidth);
                GM_setValue('windowHeight', config.windowHeight);
                if (state.popupWindow && !state.popupWindow.closed) {
                    state.popupWindow.resizeTo(config.windowWidth, config.windowHeight);
                }
            }
        }
    }

    // Update the menu commands when configuration changes
    function updateMenuCommands() {
        menuCommands.forEach((command) => {
            registerMenuCommand(command.label, command.action);
        });
    }

    // Initialize menu commands
    updateMenuCommands();

    // Event listeners for drag-and-drop and other interactions
    document.body.addEventListener('dragstart', async (event) => {
        const linkElement = event.target.tagName === 'A' ? event.target : event.target.closest('a');
        if (linkElement) {
            state.isDragging = true;
            state.linkToPreload = linkElement.href;

            await preloadLink(state.linkToPreload);

            if (config.closeOnScroll) {
                window.addEventListener('scroll', closePopupOnScroll, { once: true });
            }
        }
    });

    document.body.addEventListener('dragend', () => {
        if (state.isDragging && state.linkToPreload) {
            state.isDragging = false;
            openPopupWindow(state.linkToPreload);
            state.linkToPreload = null;
        }
    });

    document.body.addEventListener('mousedown', async (event) => {
        const linkElement = event.target.tagName === 'A' ? event.target : event.target.closest('a');
        if (event.button === 0 && linkElement) { // Left mouse button
            const siteConfig = getSiteConfig();
            if (siteConfig && siteConfig.enableLeftClickPopup && !event.shiftKey) {
                // Custom behavior for specific sites: Prefetch and open popup
                await preloadLink(linkElement.href);
                openPopupWindow(linkElement.href, siteConfig.windowWidth, siteConfig.windowHeight);
                event.preventDefault();
            }
        }
    });

    document.body.addEventListener('wheel', () => {
        if (config.closeOnScroll) {
            closePopupWindow();
        }
    });

    document.body.addEventListener('click', (event) => {
        if (event.target === state.acrylicOverlay) {
            removeAcrylicOverlay();
        }
    });

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址