PerfectGonzo: View Photos Popup & Carousel (High Res, Fit Entire Image, Grid Toggle)

Adds a button that opens a fullscreen grid of high-res photos. Clicking a photo opens a carousel overlay that scales the image to fit without cropping. In the carousel, a "Grid View" button lets you return to the grid.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         PerfectGonzo: View Photos Popup & Carousel (High Res, Fit Entire Image, Grid Toggle)
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  Adds a button that opens a fullscreen grid of high-res photos. Clicking a photo opens a carousel overlay that scales the image to fit without cropping. In the carousel, a "Grid View" button lets you return to the grid.
// @match        *://*.perfectgonzo.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Function to create the grid popup gallery
    function createPopupGallery(imageUrls) {
        // Create overlay for grid gallery
        const overlay = document.createElement('div');
        overlay.id = 'pgPhotoOverlay';
        overlay.style.position = 'fixed';
        overlay.style.top = '0';
        overlay.style.left = '0';
        overlay.style.width = '100%';
        overlay.style.height = '100%';
        overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
        overlay.style.zIndex = '10000';
        overlay.style.overflowY = 'auto';
        overlay.style.padding = '20px';

        // Close button for grid gallery
        const closeBtn = document.createElement('button');
        closeBtn.innerText = 'X';
        closeBtn.style.position = 'fixed';
        closeBtn.style.top = '20px';
        closeBtn.style.right = '20px';
        closeBtn.style.fontSize = '24px';
        closeBtn.style.background = 'transparent';
        closeBtn.style.color = '#fff';
        closeBtn.style.border = 'none';
        closeBtn.style.cursor = 'pointer';
        closeBtn.style.zIndex = '10001';
        overlay.appendChild(closeBtn);

        closeBtn.addEventListener('click', function() {
            document.body.removeChild(overlay);
        });

        // Gallery container (grid layout)
        const galleryContainer = document.createElement('div');
        galleryContainer.style.display = 'flex';
        galleryContainer.style.flexWrap = 'wrap';
        galleryContainer.style.justifyContent = 'center';
        galleryContainer.style.gap = '20px';
        galleryContainer.style.marginTop = '60px'; // leave room for the close button
        overlay.appendChild(galleryContainer);

        imageUrls.forEach((url, index) => {
            const imgContainer = document.createElement('div');
            imgContainer.style.flex = '1 1 auto';
            imgContainer.style.maxWidth = '400px';
            imgContainer.style.cursor = 'pointer';

            const img = document.createElement('img');
            img.src = url;
            img.alt = 'Photo';
            img.style.width = '100%';
            img.style.height = 'auto';
            img.style.border = '2px solid #fff';
            img.style.boxShadow = '0 0 10px rgba(255,255,255,0.5)';
            img.style.transition = 'transform 0.2s ease-in-out';
            img.addEventListener('mouseover', () => { img.style.transform = 'scale(1.05)'; });
            img.addEventListener('mouseout', () => { img.style.transform = 'scale(1)'; });

            // When clicking the image, close the grid and open the carousel at this index.
            img.addEventListener('click', function(e) {
                e.stopPropagation();
                document.body.removeChild(overlay);
                createCarousel(index, imageUrls);
            });

            imgContainer.appendChild(img);
            galleryContainer.appendChild(imgContainer);
        });

        document.body.appendChild(overlay);
    }

    // Function to create a fullscreen carousel overlay with a "Grid View" button
    function createCarousel(currentIndex, imageUrls) {
        // Create carousel overlay
        const carouselOverlay = document.createElement('div');
        carouselOverlay.id = 'pgCarouselOverlay';
        carouselOverlay.style.position = 'fixed';
        carouselOverlay.style.top = '0';
        carouselOverlay.style.left = '0';
        carouselOverlay.style.width = '100vw';
        carouselOverlay.style.height = '100vh';
        carouselOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
        carouselOverlay.style.zIndex = '10001';
        carouselOverlay.style.display = 'flex';
        carouselOverlay.style.alignItems = 'center';
        carouselOverlay.style.justifyContent = 'center';

        // Container for image and controls
        const container = document.createElement('div');
        container.style.position = 'relative';
        container.style.width = '100vw';
        container.style.height = '100vh';
        container.style.display = 'flex';
        container.style.alignItems = 'center';
        container.style.justifyContent = 'center';
        carouselOverlay.appendChild(container);

        // Image element with scaling that preserves aspect ratio
        const carouselImg = document.createElement('img');
        carouselImg.src = imageUrls[currentIndex];
        carouselImg.style.display = 'block';
        carouselImg.style.maxWidth = '100%';
        carouselImg.style.maxHeight = '100%';
        carouselImg.style.width = 'auto';
        carouselImg.style.height = 'auto';
        carouselImg.style.objectFit = 'contain';
        container.appendChild(carouselImg);

        // Left arrow
        const leftArrow = document.createElement('div');
        leftArrow.innerHTML = '❮'; // left arrow symbol
        leftArrow.style.position = 'absolute';
        leftArrow.style.left = '20px';
        leftArrow.style.top = '50%';
        leftArrow.style.transform = 'translateY(-50%)';
        leftArrow.style.fontSize = '48px';
        leftArrow.style.color = '#fff';
        leftArrow.style.cursor = 'pointer';
        leftArrow.style.userSelect = 'none';
        container.appendChild(leftArrow);

        // Right arrow
        const rightArrow = document.createElement('div');
        rightArrow.innerHTML = '❯'; // right arrow symbol
        rightArrow.style.position = 'absolute';
        rightArrow.style.right = '20px';
        rightArrow.style.top = '50%';
        rightArrow.style.transform = 'translateY(-50%)';
        rightArrow.style.fontSize = '48px';
        rightArrow.style.color = '#fff';
        rightArrow.style.cursor = 'pointer';
        rightArrow.style.userSelect = 'none';
        container.appendChild(rightArrow);

        // Close button
        const closeBtn = document.createElement('div');
        closeBtn.innerHTML = '×';
        closeBtn.style.position = 'absolute';
        closeBtn.style.top = '20px';
        closeBtn.style.right = '30px';
        closeBtn.style.fontSize = '48px';
        closeBtn.style.color = '#fff';
        closeBtn.style.cursor = 'pointer';
        closeBtn.style.userSelect = 'none';
        container.appendChild(closeBtn);

        // New: "Grid View" button to go back to grid overlay.
        const gridViewBtn = document.createElement('button');
        gridViewBtn.innerText = 'Grid View';
        gridViewBtn.style.position = 'absolute';
        gridViewBtn.style.top = '20px';
        gridViewBtn.style.left = '30px';
        gridViewBtn.style.fontSize = '16px';
        gridViewBtn.style.padding = '8px 12px';
        gridViewBtn.style.background = '#ffa700';
        gridViewBtn.style.border = 'none';
        gridViewBtn.style.borderRadius = '5px';
        gridViewBtn.style.cursor = 'pointer';
        gridViewBtn.style.zIndex = '10002';
        container.appendChild(gridViewBtn);

        // Arrow navigation events
        leftArrow.addEventListener('click', function(e) {
            e.stopPropagation();
            currentIndex = (currentIndex - 1 + imageUrls.length) % imageUrls.length;
            carouselImg.src = imageUrls[currentIndex];
        });

        rightArrow.addEventListener('click', function(e) {
            e.stopPropagation();
            currentIndex = (currentIndex + 1) % imageUrls.length;
            carouselImg.src = imageUrls[currentIndex];
        });

        // Close carousel events
        closeBtn.addEventListener('click', function() {
            document.body.removeChild(carouselOverlay);
        });
        carouselOverlay.addEventListener('click', function(e) {
            if (e.target === carouselOverlay) {
                document.body.removeChild(carouselOverlay);
            }
        });

        // "Grid View" button: remove carousel and reopen grid overlay
        gridViewBtn.addEventListener('click', function(e) {
            e.stopPropagation();
            document.body.removeChild(carouselOverlay);
            createPopupGallery(imageUrls);
        });

        document.body.appendChild(carouselOverlay);
    }

    // When the page loads, add the "View Photos" button
    window.addEventListener('load', function() {
        const btn = document.createElement('button');
        btn.innerText = 'View Photos';
        btn.style.position = 'fixed';
        btn.style.top = '10px';
        btn.style.right = '10px';
        btn.style.zIndex = '9999';
        btn.style.padding = '10px 15px';
        btn.style.background = '#ffa700';
        btn.style.border = 'none';
        btn.style.borderRadius = '5px';
        btn.style.cursor = 'pointer';
        btn.style.fontSize = '14px';
        document.body.appendChild(btn);

        // On button click, gather high-res photo URLs from the Photos section
        btn.addEventListener('click', function() {
            let imageUrls = [];
            const photoAnchors = document.querySelectorAll('.bxslider_pics_fancybox a[href]');
            photoAnchors.forEach(anchor => {
                const url = anchor.href;
                if(url) {
                    imageUrls.push(url);
                }
            });
            imageUrls = [...new Set(imageUrls)]; // Remove duplicates
            if(imageUrls.length > 0) {
                createPopupGallery(imageUrls);
            } else {
                alert('No photos found.');
            }
        });
    });
})();