Hover Zoom Minus --

image popup: zoom, pan, scroll, pin, scale

目前為 2024-03-23 提交的版本,檢視 最新版本

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name             Hover Zoom Minus --
// @namespace        Hover Zoom Minus --
// @version          1.0.7
// @description      image popup: zoom, pan, scroll, pin, scale
// @author           Ein, Copilot AI
// @match            *://*/*
// @license          MIT
// @grant            none
// ==/UserScript==

// 01. to use hover over the image(container) to view a popup of the target image
// 02. to zoom in/out use wheel up/down.
// 03. click left mouse to lock popup this will make it move along with the mouse, click again to release (indicated by green border)
// 04. while being locked"Y" the wheel up/down will act as scroll up/down
// 05. double click will lock it on screen preventing it from being hidden
// 06. hover below the image and click blue bar this will make a 3rd mode for wheel bottom, which will scroll next/previous image under an album
// 07. while locked at screen (indicated by red outline) a single click with the blurred background will unblur it, only one popup per time, so the locked popup will prevent other popup to spawn
// 08. double clicking on blurred background will de-spawn popup
// 09. click on the corner to toggle scaling (blue) will retain current aspect ratio, double clicking will turn to 2nd mode for scaling (magenta) will not retain current aspect ratio
// 10. return to original aspect ration via clicking on sides, if it's in an album to reset to aspect ratio on bottom side use double click
// 11. to turn on/off hover at the bottom of the page

(function() {
    'use strict';

    // Configuration ----------------------------------------------------------

    // Define regexp of web page you want HoverZoomMinus to run with,
    // 1st array value - default status at start of page: '1' for on, '0' for off
    // 2nd array value - spawn position for popup: 'center' for center of screen, '' for cursor position
    // 3rd array value - allowed interval for spawning popup; i.e. when exited on popup but immediately touches an img container thus making it "blink spawn blink spawn", experiment it with the right number

    const siteConfig = {
        'reddit.com': [1, 'center', '0'],
        '9gag.com': [1, 'center', '0'],
        'feedly.com': [1, 'center', '200'],
        '4chan.org': [1, '', '400'],
        'deviantart.com': [0, 'center', '300'],
        'home': [1, 'center', '0'] /* for testing */
    };

    // image container [hover box where popup triggers]
    const imgContainers = `
    /* ------- reddit */ ._3BxRNDoASi9FbGX01ewiLg, ._3Oa0THmZ3f5iZXAQ0hBJ0k > div, ._35oEP5zLnhKEbj5BlkTBUA, ._1ti9kvv_PMZEF2phzAjsGW > div,
    /* ------- reddit */ ._28TEYBuEdOuE3kN6UyoKMa div, ._3Oa0THmZ3f5iZXAQ0hBJ0k.WjuR4W-BBrvdtABBeKUMx div, ._3m20hIKOhTTeMgPnfMbVNN,
    /* --------- 9gag */ .post-container .post-view > picture,
    /* ------- feedly */ .PinableImageContainer, .entryBody,
    /* -------- 4chan */ div.post div.file a,
    /* --- deviantart */ ._3_LJY, ._2e1g3, ._2SlAD, ._1R2x6
    `;
    // target img
    const imgElements = `
    /* ------- reddit */ ._2_tDEnGMLxpM6uOa2kaDB3, ._1dwExqTGJH2jnA-MYGkEL-, ._2_tDEnGMLxpM6uOa2kaDB3._1XWObl-3b9tPy64oaG6fax,
    /* --------- 9gag */ .post-container .post-view > picture > img,
    /* ------- feedly */ .pinable, .entryBody img,
    /* -------- 4chan */ div.post div.file img:nth-child(1), ._3Oa0THmZ3f5iZXAQ0hBJ0k.WjuR4W-BBrvdtABBeKUMx img, div.post div.file .fileThumb img,
    /* --- deviantart */ ._3_LJY img, ._2e1g3 img, ._2SlAD img, ._1R2x6 img
    `;
    // excluded element
    const nopeElements = `
    /* ------- reddit */ ._2ED-O3JtIcOqp8iIL1G5cg
    `;
    // AlbumSelector take note that it will only load image that are already in the DOM tree
    // example reddit will not include all until you press navigator buttons so most of the time this will only load a few image
    // unless you update it via interacting with navigator button thus updating the DOM tree
    let albumSelector = [
        /* ---reddit */ { imgElement: '._1dwExqTGJH2jnA-MYGkEL-', albumElements: '._1apobczT0TzIKMWpza0OhL' },
        /* ---feedly */ { imgElement: '.entryBody > div > img', albumElements: '.entryBody > div' },
        /* ---feedly */ { imgElement: 'div[id^="Article-"] > div > span > img', albumElements: 'div[id^="Article-"]' },
    ];

    // specialElements were if targeted, will call it's paired function
    // for convenience an element className "specialElement" will be remove during mouseout or function hidepopup() is called you can use that with specialElements function for temporary elements
    const specialElements = [
        /* --- 4chan */ { selector: 'div.post div.file .fileThumb img', func: SP1 },
        /* -- reddit */ { selector: '._1dwExqTGJH2jnA-MYGkEL-', func: SP2 }
    ];
    // special function triggered when clicking LeftBar/ wheel down in album mode
    const specialLeftBar = [
        /* -- reddit */ { selector: '._1dwExqTGJH2jnA-MYGkEL-', func: SPL }
    ];
    // special function triggered when clicking RightBar/ wheel up in album mode
    const specialRightBar = [
        /* -- reddit */ { selector: '._1dwExqTGJH2jnA-MYGkEL-', func: SPR }
    ];
    //-------------------------------------------------------------------------
    // Special Funtions -------------------------------------------------------


    // 4chan: replaces thumbnail so popup will use the larger version, or if its animated replace it with that
    function SP1(imageElement) {
        const parentElement = imageElement.parentElement;
        if (parentElement.tagName === 'A') {
            const href = parentElement.getAttribute('href');
            if (href.endsWith('.webm')) {
                const videoElement = document.createElement('video');
                videoElement.src = href;
                videoElement.controls = true;
                parentElement.replaceChild(videoElement, imageElement);
            } else if (href.endsWith('.gif')) {
                imageElement.setAttribute('src', href);
            }
        } else {
            let src = imageElement.getAttribute('src');
            if (src && src.includes('s.jpg')) {
                let newSrc = src.replace('s.jpg', '.jpg');
                imageElement.setAttribute('src', newSrc);
            }
        }
    }

    // reddit: added the label/description of image along with the popup
    // only shows what is currently loaded on DOM tree so when you navigate thru albums in popup it will not automatically update the current label/desc
    function SP2(imageElement) {
        if (enableP === 0) {
            return;
        }
        let closestElement = imageElement.closest('.m3aNC6yp8RrNM_-a0rrfa, .kcerW9lbT-se3SXd-wp2i');
        if (!closestElement) {
            return;
        }
        let descendantElement = closestElement.querySelector('._15nNdGlBIgryHV04IfAfpA');
        if (descendantElement) {
            document.querySelectorAll('.specialElement').forEach(e => e.remove());
            let title = descendantElement.getAttribute('title');
            const specialContent = document.createElement('div');
            specialContent.className = 'specialElement';
            specialContent.style.cssText = 'position:fixed; bottom:80px; left:50%; transform:translateX(-50%); width:100vw; text-align:center; font-size:40px; color:white; text-shadow:0 0 10px rgba(255,255,255,0.7); z-index:99999;';
            specialContent.textContent = title;
            document.body.appendChild(specialContent);

            var specialBackdrop = document.createElement('div');
            specialBackdrop.className = 'specialElement';
            specialBackdrop.style.cssText = 'position:fixed; bottom:60px; left:0; width:100vw; height:80px; background:rgba(0,0,0,0.2); backdrop-filter:blur(5px); z-index:99998;';
            document.body.appendChild(specialBackdrop);

        } else {
            return;
        }
    }
    function SPL(imageElement) {
        document.querySelectorAll('.specialElement').forEach(e => e.remove());

        let closestElement = imageElement.closest('.kcerW9lbT-se3SXd-wp2i');
        var element = closestElement.querySelector('._1fSFPkxZ9pToLETLQT2dmc');
        if (element) element.click();

        let descendantElement = closestElement.querySelector('._15nNdGlBIgryHV04IfAfpA');
        if (descendantElement) {
            let title = descendantElement.getAttribute('title');
            const specialContent = document.createElement('div');
            specialContent.className = 'specialElement';
            specialContent.style.cssText = 'position:fixed; bottom:80px; left:50%; transform:translateX(-50%); width:100vw; text-align:center; font-size:40px; color:white; text-shadow:0 0 10px rgba(255,255,255,0.7); z-index:99999;';
            specialContent.textContent = title;
            document.body.appendChild(specialContent);

            var specialBackdrop = document.createElement('div');
            specialBackdrop.className = 'specialElement';
            specialBackdrop.style.cssText = 'position:fixed; bottom:60px; left:0; width:100vw; height:80px; background:rgba(0,0,0,0.2); backdrop-filter:blur(5px); z-index:99998;';
            document.body.appendChild(specialBackdrop);
        } else {
            return;
        }
    }
    function SPR(imageElement) {
        document.querySelectorAll('.specialElement').forEach(e => e.remove());

        let closestElement = imageElement.closest('.kcerW9lbT-se3SXd-wp2i');
        var element = closestElement.querySelector('._3-JCOd-nY76g29C7ZVX_kl:last-child');
        if (element) element.click();

        let descendantElement = closestElement.querySelector('._15nNdGlBIgryHV04IfAfpA');
        if (descendantElement) {
            let title = descendantElement.getAttribute('title');
            const specialContent = document.createElement('div');
            specialContent.className = 'specialElement';
            specialContent.style.cssText = 'position:fixed; bottom:80px; left:50%; transform:translateX(-50%); width:100vw; text-align:center; font-size:40px; color:white; text-shadow:0 0 10px rgba(255,255,255,0.7); z-index:99999;';
            specialContent.textContent = title;
            document.body.appendChild(specialContent);

            var specialBackdrop = document.createElement('div');
            specialBackdrop.className = 'specialElement';
            specialBackdrop.style.cssText = 'position:fixed; bottom:60px; left:0; width:100vw; height:80px; background:rgba(0,0,0,0.2); backdrop-filter:blur(5px); z-index:99998;';
            document.body.appendChild(specialBackdrop);
        } else {
            return;
        }
    }
    //-------------------------------------------------------------------------


    // Configuration variables
    const currentHref = window.location.href;
    let enableP, positionP, intervalP, URLmatched;
    Object.keys(siteConfig).some((config) => {
        const regex = new RegExp(config);
        if (currentHref.match(regex)) {
            [enableP, positionP, intervalP] = siteConfig[config];
            URLmatched = true;
            return true;
        }
    });


    // The HoverZoomMinus Function---------------------------------------------
    function HoverZoomMinus() {
        let isshowPopupEnabled = true;
        isshowPopupEnabled = true;

        const style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = `
		.popup-container {
			display: none;
			z-index: 1001;
			cursor: move;
		}
		.popup-image {
			max-height: calc(90vh - 10px);
			display: none;
		}
		.popup-backdrop {
			position: fixed;
			top: 0;
			left: 0;
			width: 100vw;
			height: 100vh;
			display: none;
			z-index: 1000;
		}
        .centerBox {
			top: 40px;
            left: 40px;
            position: absolute;
            height: calc(100% - 80px);
            width: calc(100% - 80px);
        }
        .TopBar,
        .BottomBar,
        .LeftBar,
        .RightBar {
	        opacity: 0;
	        box-sizing: border-box;
            background: #0000;
            position: absolute;
            z-index: 9999;
            &::after {
                content: "";
                position: absolute;
            }
        }
        .TopBar,
        .BottomBar {
            height: 40px;
            width: calc(100% - 80px);
            left: 40px;
	        &::after {
                display: none;
                width: 25px;
                height: 25px;
                transform: rotate(45deg) translateX(-50%) translateY(-50%);
	        }
        }
        .TopBar { top: 0; }
        .BottomBar { bottom: 0; }
        .TopBar::after {
            left: calc(50% - 17px);
            top: calc(50% + 15px);
            border-top: 5px solid #6f8e9e;
            border-left: 5px solid #6f8e9e;
        }
        .BottomBar::after {
            left: calc(50% - 2px);
            bottom: -8px;
            border-bottom: 5px solid #6f8e9e;
            border-right: 5px solid #6f8e9e;
        }
        .LeftBar,
        .RightBar {
            height: calc(100% - 80px);
            width: 40px;
            top: 40px;
	        &::before {
                content: "";
                display: block;
                position: absolute;
                top: 50%;
                width: 40px;
                height: 40px;
                transform: translateY(-50%);
                border-radius: 9999px;
                background: #242526;
	        }
	        &::after {
                content: "";
                display: block;
                position: absolute;
                top: calc(50% + -2px);
                width: 15px;
                height: 15px;
	        }
            &:hover::before {
                display: none;
            }
            &:hover::after {
                width: 25px;
                height: 25px;
                border-color: #6f8e9e;
                border-width: 5px;
            }
        }
        .LeftBar { left: 0; }
        .RightBar { right: 0; }
        .LeftBar::before { left: 5px; }
        .RightBar::before { right: 5px; }
        .LeftBar:hover::after { left: 3px; }
        .RightBar:hover::after { right: 3px; }
        .LeftBar::after {
	        transform: rotate(45deg) translateY(-50%);
	        border-bottom: 3px solid #777;
	        border-left: 3px solid #777;
	        left: 12px;
        }
        .RightBar::after {
	        transform: rotate(-45deg) translateY(-50%);
            border-bottom: 3px solid #777;
            border-right: 3px solid #777;
            right: 12px;
        }
        .CornerBox {
            box-sizing: border-box;
            height: 40px;
            width: 40px;
            background: #0000;
		    position: absolute;
            z-index: 9999;
        }
        .CornerBox.TR {
	        top: 0;
            right: 0;
            text-align: center;
            line-height: 25px;
            color:white;
            text-shadow:0 0 10px rgba(255,255,255,0.7);
        }
        .CornerBox.TL {
	        top: 0;
            left: 0;
        }
        .CornerBox.BR {
	        bottom: 0;
            right: 0;text-align: center;
            line-height: 25px;
            color:white;
            text-shadow:0 0 10px rgba(255,255,255,0.7);
        }
        .CornerBox.BL {
	        bottom: 0;
            left: 0;
        }
        `;

        document.head.appendChild(style);
        const backdrop = document.createElement('div');
        backdrop.className = 'popup-backdrop';
        document.body.appendChild(backdrop);
        const popupContainer = document.createElement('div');
        popupContainer.className = 'popup-container';
        document.body.appendChild(popupContainer);
        const popup = document.createElement('img');
        popup.className = 'popup-image';
        popupContainer.appendChild(popup);

        const BottomBar = document.createElement('div');
        BottomBar.className = 'BottomBar';
        popupContainer.appendChild(BottomBar);
        const TopBar = document.createElement('div');
        TopBar.className = 'TopBar';
        popupContainer.appendChild(TopBar);
        const RightBar = document.createElement('div');
        RightBar.className = 'RightBar';
        popupContainer.appendChild(RightBar);
        const LeftBar = document.createElement('div');
        LeftBar.className = 'LeftBar';
        popupContainer.appendChild(LeftBar);

        const TR = document.createElement('div');
        TR.className = 'CornerBox TR';
        popupContainer.appendChild(TR);
        const TL = document.createElement('div');
        TL.className = 'CornerBox TL';
        popupContainer.appendChild(TL);
        const BR = document.createElement('div');
        BR.className = 'CornerBox BR';
        popupContainer.appendChild(BR);
        const BL = document.createElement('div');
        BL.className = 'CornerBox BL';
        popupContainer.appendChild(BL);

        const centerBox = document.createElement('div');
        centerBox.className = 'popup centerBox';
        popupContainer.appendChild(centerBox);

        const style2 = document.createElement('style');
        style2.type = 'text/css';
        document.head.appendChild(style2);

        //-------------------------------------------------------------------------

        // Variable
        const ZOOM_SPEED = 0.005;
        let pageDirection;
        let isLockedY = false;
        let isLockedX = false;
        let scale = 1;
        let clickTimeout;
        let popupTimer;
        let isScale, isScaleTR, isScaleBR, isScaleTL, isScaleBL;
        let rect, rectT, rectH, rectL, rectW;
        let rectIH, rectIW, rectIT, rectIL, rectIRatio;
        let rectzT, rectzH, rectzL, rectzW, rectzRatio;
        let scaleCurrentX, scaleCurrentY;
        let scaleFactorX, scaleFactorY;
        let offsetX, offsetY, offsetXR, offsetYT, offsetXL, offsetYB;
        let offsetRatioY, offsetRatioX;
        let ishidePopupEnabled = true;
        let ScaleMode2 = false;
        let imgElementsList = [];
        let currentZeroImgElement;
        let rectIzRatio;

        function NoMode() {
            isLockedY = false;
            isLockedX = false;
            isScale = false;
            isScaleTR = false;
            isScaleTL = false;
            isScaleBL = false;
            isScaleBR = false;
            popupContainer.style.border = '';
            TR.style.border = '';
            BR.style.border = '';
            TL.style.border = '';
            BL.style.border = '';
        }

        function LockedYMode() {
            NoMode();
            isLockedY = true;
            popupContainer.style.borderLeft = '6px solid #00ff00';
            popupContainer.style.borderRight = '6px solid #00ff00';
            LeftBar.style.opacity = '0';
            RightBar.style.opacity = '0';
        }
        function LockedXMode() {
            NoMode();
            LockedScreen();
            isLockedX = true;
            popupContainer.style.borderTop = '6px solid #00ff00';
            popupContainer.style.borderBottom = '6px solid #00ff00';
            LeftBar.style.opacity = '1';
            RightBar.style.opacity = '1';
            style2.innerHTML = `
                .LeftBar::before,
                .LeftBar::after,
                .RightBar::before,
                .RightBar::after {
                    display: block;
                }
            `;
        }

        function toggleLockedScreen(event) {
            ishidePopupEnabled = !ishidePopupEnabled;
            popupContainer.style.outline = ishidePopupEnabled ? '' : '6px solid #ae0001';
        }

        function LockedScreen() {
            ishidePopupEnabled = false;
            popupContainer.style.outline = '6px solid #ae0001';
        }
        function ScalingMode1() {
            TL.style.borderTop = '6px solid #0000ff';
            TR.style.borderTop = '6px solid #0000ff';
            TL.style.borderLeft = '6px solid #0000ff';
            BL.style.borderLeft = '6px solid #0000ff';
            TR.style.borderRight = '6px solid #0000ff';
            BR.style.borderRight = '6px solid #0000ff';
            BR.style.borderBottom = '6px solid #0000ff';
            BL.style.borderBottom = '6px solid #0000ff';
        }
        function ScalingMode2() {
            ScalingMode1();
            TL.style.borderColor = '#ff00ff';
            TR.style.borderColor = '#ff00ff';
            BL.style.borderColor = '#ff00ff';
            BR.style.borderColor = '#ff00ff';
        }
        function ScalingMode0() {
            TL.style.border = '';
            TR.style.border = '';
            BL.style.border = '';
            BR.style.border = '';
        }

        function ResetGeometry() {
            let rectF = popup.getBoundingClientRect();
            rectzH = rectF.height;
            rectzW = rectF.width;
            rectzT = rectF.top;
            rectzL = rectF.left;
            popup.style.maxHeight = rectzH + 'px';
            popup.style.height = rectzH + 'px';
            popup.style.width = rectzW + 'px';
            popupContainer.style.top = rectzT + (rectzH / 2) + 'px';
            popupContainer.style.left = rectzL + (rectzW / 2) + 'px';
            popupContainer.style.transformOrigin = '50% 50% 0px';
            popupContainer.style.transform = `translate(-50%, -50%) scaleX(1) scaleY(1)`;
        }

        function BarClear() {
            TL.style.border = '';
            TR.style.border = '';
            BL.style.border = '';
            BR.style.border = '';
            TL.style.background = '';
            TR.style.background = '';
            BL.style.background = '';
            BR.style.background = '';
            TopBar.style.background = '';
            LeftBar.style.background = '';
            RightBar.style.background = '';
            BottomBar.style.background = '';
            popupContainer.style.border = '';
            LeftBar.style.opacity = '0';
            RightBar.style.opacity = '0';
        }


        function NavigateAlbum() {
            let pair = albumSelector.find(pair => currentZeroImgElement.matches(pair.imgElement));
            if (pair) {
                let ancestorElement = currentZeroImgElement.closest(pair.albumElements);
                if (ancestorElement) {
                    imgElementsList = Array.from(ancestorElement.querySelectorAll(pair.imgElement));
                    let zeroIndex = imgElementsList.indexOf(currentZeroImgElement);
                    let direction = pageDirection;
                    let newIndex = zeroIndex + direction;

                    TR.textContent = `${newIndex + 1}/${imgElementsList.length}`;

                    if (newIndex <= 0) {
                        LeftBar.style.display = 'none';
                        TR.textContent = `1/${imgElementsList.length}`;
                    } else {
                        LeftBar.style.display = '';
                    }
                    if (newIndex >= imgElementsList.length - 1) {
                        RightBar.style.display = 'none';
                        TR.textContent = `${imgElementsList.length}/${imgElementsList.length}`;
                    } else {
                        RightBar.style.display = '';
                    }
                    if (newIndex < 0 || newIndex >= imgElementsList.length) {
                        return;
                    }
                    currentZeroImgElement = imgElementsList[newIndex];
                    popup.src = currentZeroImgElement.src;
                }
            }
        }




        // Mouse Click: Center
        centerBox.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                ResetGeometry();
                if (!isScale) {
                    isLockedY = !isLockedY;
                }
                if (isLockedY) {
                    LockedYMode();
                    let rect = popupContainer.getBoundingClientRect();
                    offsetX = event.clientX - rect.left - (rect.width / 2);
                    offsetY = event.clientY - rect.top - (rect.height / 2);
                } else {
                    NoMode();
                }
            }, 300);
        });

        centerBox.addEventListener('dblclick', function(event) {
            clearTimeout(clickTimeout);
            toggleLockedScreen();
            if (!ishidePopupEnabled) {
                backdrop.style.display = 'block';
                backdrop.style.zIndex = '999';
                backdrop.style.backdropFilter = 'blur(10px)';
            }
        });
        //-------------------------------------------------------------------------

        // Mouse Click: Corners
        document.querySelectorAll('.CornerBox').forEach(element => {
            element.addEventListener('click', function(event) {
                ishidePopupEnabled = false;
                if (isScale) {
                    BarClear();
                    isLockedY = false;
                    isLockedX = false;
                    popup.style.border = '';

                    const clickedElement = event.target;
                    const popupContainer = clickedElement.parentElement;
                    const currentTransform = window.getComputedStyle(popupContainer).transform;
                    const matrixMatch = currentTransform.match(/^matrix\(([^,]+), [^,]+, [^,]+, ([^,]+), [^,]+, [^,]+\)$/);
                    if (matrixMatch) {
                        scaleCurrentX = parseFloat(matrixMatch[1]);
                        scaleCurrentY = parseFloat(matrixMatch[2]);
                    }

                    let rect = popupContainer.getBoundingClientRect();
                    offsetYT = event.clientY - rect.top;
                    offsetXL = event.clientX - rect.left;
                    offsetYB = rect.height + rect.top - event.clientY;
                    offsetXR = rect.width + rect.left - event.clientX;

                    rectT = rect.top;
                    rectL = rect.left;
                    rectH = rect.height;
                    rectW = rect.width;

                } else {
                    LockedScreen();
                    ResetGeometry();
                }

            });
        });
        document.querySelectorAll('.CornerBox').forEach(element => {
            element.addEventListener('click', function(event) {
                if (clickTimeout) clearTimeout(clickTimeout);
                isScale = !isScale;
            }, 300);
        });
        document.querySelectorAll('.CornerBox').forEach(element => {
            element.addEventListener('dblclick', function(event) {
                clearTimeout(clickTimeout);
                ScaleMode2 = !ScaleMode2;
            });
        });

        TL.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                isScaleTL = !isScaleTL;
                isScaleTR = false;
                isScaleBL = false;
                isScaleBR = false;
            }, 300);
        });
        TR.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                isScaleTL = false;
                isScaleTR = !isScaleTR;
                isScaleBL = false;
                isScaleBR = false;
            }, 300);
        });
        BL.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                isScaleTL = false;
                isScaleTR = false;
                isScaleBL = !isScaleBL;
                isScaleBR = false;
            }, 300);
        });
        BR.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                isScaleTL = false;
                isScaleTR = false;
                isScaleBL = false;
                isScaleBR = !isScaleBR;
            }, 300);
        });

        //-------------------------------------------------------------------------


        // Mouse Move: Pan, Scale
        document.addEventListener('mousemove', function(event) {
            // Panning mode: popup locked and follows the mouse
            if (isLockedY) {
                popupContainer.style.left = (event.clientX - offsetX) + 'px';
                popupContainer.style.top = (event.clientY - offsetY) + 'px';
            } else if (isScale) {
                ScalingMode1();
                if (ScaleMode2) {
                    ScalingMode2();
                }
                if (isScaleTL) {
                    popupContainer.style.transformOrigin = '100% 100% 0px';
                    scaleFactorY = scaleCurrentY * (1 + (rectT - event.clientY + offsetYT) / rectH);
                    scaleFactorX = scaleCurrentX * (1 + (rectL - event.clientX + offsetXL) / rectW);
                } else if (isScaleTR) {
                    popupContainer.style.transformOrigin = '0 100% 0px';
                    scaleFactorY = scaleCurrentY * (1 + (rectT - event.clientY + offsetYT) / rectH);
                    scaleFactorX = scaleCurrentX * ((event.clientX - rectL + offsetXR) / rectW);
                } else if (isScaleBL) {
                    popupContainer.style.transformOrigin = '100% 0% 0px';
                    scaleFactorY = scaleCurrentY * ((event.clientY - rectT + offsetYB) / rectH);
                    scaleFactorX = scaleCurrentX * (1 + (rectL - event.clientX + offsetXL) / rectW);
                } else if (isScaleBR) {
                    popupContainer.style.transformOrigin = '0% 0% 0px';
                    scaleFactorY = scaleCurrentY * ((event.clientY - rectT + offsetYB) / rectH);
                    scaleFactorX = scaleCurrentX * ((event.clientX - rectL + offsetXR) / rectW);
                }
                if (ScaleMode2) {
                    popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scaleFactorX}) scaleY(${scaleFactorY})`;

                } else {
                    popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scaleFactorX}) scaleY(${scaleFactorX})`;
                }
            }
        });


        // Mouse Wheel: Zoom, Scroll, Navigate
        function ZoomOrScroll(event) {
            event.preventDefault();
            if (isLockedY) {
                let deltaY = event.deltaY * -ZOOM_SPEED;
                let newTop = parseInt(popupContainer.style.top) || 0;
                newTop += deltaY * 100;
                popupContainer.style.top = newTop + 'px';
                offsetY -= deltaY * 100;

            } else if (isLockedX && currentZeroImgElement) {
                pageDirection = event.deltaY > 0 ? 1 : -1;
                NavigateAlbum();

                if (pageDirection === 1) {
                    const imageElement = currentContainer.querySelector(imgElements);
                    specialRightBar.forEach(pair => {
                        if (imageElement.matches(pair.selector)) {
                            pair.func(imageElement);
                        }
                    });
                } else {
                    const imageElement = currentContainer.querySelector(imgElements);
                    specialLeftBar.forEach(pair => {
                        if (imageElement.matches(pair.selector)) {
                            pair.func(imageElement);
                        }
                    });
                }

            } else {
                scale += event.deltaY * -ZOOM_SPEED;
                scale = Math.min(Math.max(0.125, scale), 10);
                popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scale}) scaleY(${scale})`;
            }
        }
        popupContainer.addEventListener('wheel', ZoomOrScroll);
        //-------------------------------------------------------------------------


        // Bottom Bar: Album
        BottomBar.addEventListener('mouseenter', function(e) {

            if (isScale) {
                return;
            } else {
                BottomBar.style.opacity = '1';

                let rect = popup.getBoundingClientRect();
                rectzRatio = rect.height / rect.width;
                rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));

                if (isAlbum) {
                    if (!isScale) {
                        BottomBar.style.background = 'linear-gradient(to right, rgba(0, 0, 255, 0) 0%, rgba(0, 0, 255, 0.5) 25%, rgba(0, 0, 255, 0.5) 75%, rgba(0, 0, 255, 0) 100%)';
                    }
                } else {
                    BottomBar.style.background = '';
                    if (!( (rectIzRatio === 1) || isScale ) ) {
                        style2.innerHTML = `
                            .BottomBar::after {
                                display: block;
                            }
                        `;
                        BL.style.background = 'rgba(0, 0, 0, 0.5)';
                        BR.style.background = 'rgba(0, 0, 0, 0.5)';
                        BottomBar.style.background = 'rgba(0, 0, 0, 0.5)';
                    } else {
                        style2.innerHTML = `
                            .BottomBar::after {
                                display: none;
                            }
                        `;
                    }
                }
            }
        });

        BottomBar.addEventListener('mouseleave', function() {
            BottomBar.style.opacity = '0';
            if (!isLockedX) {
                BarClear();
            }
        });
        BottomBar.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                if (!isScale) {
                    LeftBar.style.opacity = '0';
                    RightBar.style.opacity = '0';
                }
                if (isAlbum) {
                    ResetGeometry();
                    isLockedX = !isLockedX;
                    if (isLockedX) {
                        LockedXMode();
                        LeftBar.style.opacity = '1';
                        RightBar.style.opacity = '1';
                    } else {
                        popupContainer.style.border = '';
                    }
                } else {
                    ResetGeometry();
                    if (isScale) {
                        isScale = false;
                        popup.style.maxHeight = rectzH + 'px';
                        popup.style.height = rectzH + 'px';
                        offsetRatioY = 0;
                    } else {
                        popup.style.maxHeight = 'unset';
                        popup.style.height = '';
                        offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
                    }
                    popupContainer.style.top = rectzT + (rectzH / 2) + offsetRatioY + 'px';
                }
            }, 300);
        });


        BottomBar.addEventListener('dblclick', function(event) {
            clearTimeout(clickTimeout);

            if (isAlbum) {
                ResetGeometry();
                if (isScale) {
                    popup.style.maxHeight = rectzH + 'px';
                    popup.style.height = rectzH + 'px';
                    offsetRatioY = 0;
                } else {
                    popup.style.maxHeight = 'unset';
                    popup.style.height = '';
                    offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
                }
                popupContainer.style.top = rectzT + (rectzH / 2) + offsetRatioY + 'px';
                BarClear();
            } else {
                return;
            }
        });
        //-------------------------------------------------------------------------


        // Indicators
        document.querySelectorAll('.CornerBox').forEach(element => {
            element.addEventListener('mouseenter', function(event) {
                ScalingMode1();
                if (ScaleMode2) {
                    ScalingMode2();
                }
            });
            element.addEventListener('mouseleave', function(event) {
                if (!isScale) {
                    ScalingMode0();
                }
            });
        });

        // Re-scale/ Navigate
        TopBar.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                ResetGeometry();
                if (isScale) {
                    isScale = false;
                    popup.style.maxHeight = rectzH + 'px';
                    popup.style.height = rectzH + 'px';
                    offsetRatioY = 0;
                } else {
                    popup.style.maxHeight = 'unset';
                    popup.style.height = '';
                    offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
                }
                popupContainer.style.top = rectzT + (rectzH / 2) - offsetRatioY + 'px';
                BarClear();

            }, 300);
        });

        TopBar.addEventListener('dblclick', function(event) {
            clearTimeout(clickTimeout);
            LockedScreen();
        });



        let zeroIndex;
        LeftBar.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                ResetGeometry();
                if (isScale) {
                    isScale = false;
                    popup.style.width = rectzW + 'px';
                    offsetRatioX = 0;
                } else {
                    popup.style.width = '';
                    offsetRatioX = (rectzW - (rectzH / rectIRatio)) / 2 ;
                }
                if (isLockedX && currentZeroImgElement) {
                    pageDirection = -1;
                    NavigateAlbum();

                    TL.style.background = 'rgba(0, 0, 0, 0.5)';
                    BL.style.background = 'rgba(0, 0, 0, 0.5)';
                    LeftBar.style.background = 'rgba(0, 0, 0, 0.5)';

                    const imageElement = currentContainer.querySelector(imgElements);
                    specialLeftBar.forEach(pair => {
                        if (imageElement.matches(pair.selector)) {
                            pair.func(imageElement);
                        }
                    });

                } else {
                    popupContainer.style.left = rectzL + (rectzW / 2) - offsetRatioX + 'px';
                }
            }, 300);
        });
        RightBar.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                ResetGeometry();
                if (isScale) {
                    isScale = false;
                    popup.style.width = rectzW + 'px';
                    offsetRatioX = 0;
                } else {
                    popup.style.width = '';
                    offsetRatioX = (rectzW - (rectzH / rectIRatio)) / 2 ;
                }
                if (isLockedX && currentZeroImgElement) {
                    pageDirection = 1;
                    NavigateAlbum();
                    TR.style.background = 'rgba(0, 0, 0, 0.5)';
                    BR.style.background = 'rgba(0, 0, 0, 0.5)';
                    RightBar.style.background = 'rgba(0, 0, 0, 0.5)';

                    const imageElement = currentContainer.querySelector(imgElements);
                    specialRightBar.forEach(pair => {
                        if (imageElement.matches(pair.selector)) {
                            pair.func(imageElement);
                        }
                    });

                } else {
                    popupContainer.style.left = rectzL + (rectzW / 2) + offsetRatioX + 'px';
                }
            }, 300);
        });



        TopBar.addEventListener('mouseenter', function() {
            let rect = popup.getBoundingClientRect();
            rectzRatio = rect.height / rect.width;
            rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
            if (!( (rectIzRatio === 1) || isScale ) ) {
                TopBar.style.opacity = '1';
                style2.innerHTML = `
                .TopBar::after {
                display: block;
                }`;
                TL.style.background = 'rgba(0, 0, 0, 0.5)';
                TR.style.background = 'rgba(0, 0, 0, 0.5)';
                TopBar.style.background = 'rgba(0, 0, 0, 0.5)';
            } else {
                style2.innerHTML = `
                .TopBar::after {
                display: none;
                }`;
                if (ishidePopupEnabled) {
                    TopBar.style.opacity = '1';
                    TL.style.background = 'rgba(0, 0, 0, 0.5)';
                    TR.style.background = 'rgba(0, 0, 0, 0.5)';
                    TopBar.style.background = 'rgba(0, 0, 0, 0.5)';
                }
            }
        });
        LeftBar.addEventListener('mouseenter', function() {
            LeftBar.style.opacity = '1';
            let rect = popup.getBoundingClientRect();
            rectzRatio = rect.height / rect.width;
            rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
            if ( isLockedX || !( (rectIzRatio === 1) || isScale ) ) {
                style2.innerHTML = `
                .LeftBar::before, .LeftBar::after {
                display: block;
                }`;
                TL.style.background = 'rgba(0, 0, 0, 0.5)';
                BL.style.background = 'rgba(0, 0, 0, 0.5)';
                LeftBar.style.background = 'rgba(0, 0, 0, 0.5)';
            } else {
                style2.innerHTML = `
                .LeftBar::before, .LeftBar::after {
                display: none;
                }`;
            }
        });
        RightBar.addEventListener('mouseenter', function() {
            RightBar.style.opacity = '1';
            let rect = popup.getBoundingClientRect();
            rectzRatio = rect.height / rect.width;
            rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
            if ( isLockedX || !( (rectIzRatio === 1) || isScale ) ) {
                style2.innerHTML = `
                .RightBar::before, .RightBar::after {
                display: block;
                }`;
                TR.style.background = 'rgba(0, 0, 0, 0.5)';
                BR.style.background = 'rgba(0, 0, 0, 0.5)';
                RightBar.style.background = 'rgba(0, 0, 0, 0.5)';
            } else {
                style2.innerHTML = `
                .RightBar::before, .RightBar::after {
                display: none;
                }`;
            }
        });

        TopBar.addEventListener('mouseleave', function() {
            TopBar.style.opacity = '0';
            TopBar.style.border = '';
            TL.style.background = '';
            TR.style.background = '';
            TopBar.style.background = '';
            if (isScale) {
                TR.style.borderColor = '#0000ff';
                TL.style.borderColor = '#0000ff';
                if (ScaleMode2) {
                    TR.style.borderColor = '#ff00ff';
                    TL.style.borderColor = '#ff00ff';
                }
            } else {
                TR.style.border = '';
                TL.style.border = '';
            }
        });
        LeftBar.addEventListener('mouseleave', function() {
            if (isLockedX) {
                LeftBar.style.opacity = '1';
            } else {
                LeftBar.style.opacity = '0';
            }
            TL.style.background = '';
            BL.style.background = '';
            LeftBar.style.background = '';
        });
        RightBar.addEventListener('mouseleave', function() {
            if (isLockedX) {
                RightBar.style.opacity = '1';
            } else {
                RightBar.style.opacity = '0';
            }
            TR.style.background = '';
            BR.style.background = '';
            RightBar.style.background = '';
        });
        //-------------------------------------------------------------------------


        // show popup
        function showPopup(src, mouseX, mouseY) {
            if (!isshowPopupEnabled) return;
            ishidePopupEnabled = true;
            if (enableP === 0) return;

            popup.src = src;
            popup.style.display = 'block';
            popupContainer.style.display = 'block';
            popupContainer.style.position = 'fixed';
            popupContainer.style.transform = 'translate(-50%, -50%) scaleX(1) scaleY(1)';
            backdrop.style.display = 'block';
            backdrop.style.zIndex = '999';
            backdrop.style.backdropFilter = 'blur(10px)';

            if (positionP === 'center') {
                popupContainer.style.top = '50%';
                popupContainer.style.left = '50%';
            } else {
                popupContainer.style.top = `${mouseY}px`;
                popupContainer.style.left = `${mouseX}px`;
            }

            let rectI = popup.getBoundingClientRect();
            rectIH = rectI.height;
            rectIW = rectI.width;
            rectIT = rectI.top;
            rectIL = rectI.left;

            rectIRatio = rectIH / rectIW;

            if (isAlbum) {
                LockedXMode();
            }

        }

        let isAlbum;
        let currentContainer;
        document.addEventListener('mouseover', function(e) {

            if (popupTimer) return;
            let target = e.target.closest(imgContainers);

            console.log('Entered imgContainer:', target);
            if (!target) return;


            if (target.querySelector(nopeElements)) return;
            currentContainer = target;

            const imageElement = currentContainer.querySelector(imgElements);

            specialElements.forEach(pair => {
                if (imageElement.matches(pair.selector)) {
                    pair.func(imageElement);
                }
            });

            if (imageElement) {
                currentZeroImgElement = imageElement;
                if (currentZeroImgElement) {
                    let pair = albumSelector.find(pair => currentZeroImgElement.matches(pair.imgElement));
                    if (pair) {
                        let closestAlbumElement = currentZeroImgElement.closest(pair.albumElements);
                        isAlbum = closestAlbumElement && closestAlbumElement.querySelectorAll(pair.imgElement).length > 1;
                        if (isAlbum) {
                            imgElementsList = Array.from(closestAlbumElement.querySelectorAll(pair.imgElement));
                            let zeroIndex = imgElementsList.indexOf(currentZeroImgElement);

                            TR.textContent = `${zeroIndex + 1}/${imgElementsList.length}`;

                            if (zeroIndex === 0) {
                                LeftBar.style.display = 'none';
                            } else {
                                LeftBar.style.display = '';
                            }
                            if (zeroIndex === imgElementsList.length - 1) {
                                RightBar.style.display = 'none';
                            } else {
                                RightBar.style.display = '';
                            }
                        }
                    } else {
                        isAlbum = false;
                        TR.textContent = '';
                        LeftBar.style.display = '';
                        RightBar.style.display = '';
                    }
                }
                if (intervalP === '') {
                    showPopup(imageElement.src, e.clientX, e.clientY);
                } else {
                    popupTimer = setTimeout(() => {
                        showPopup(imageElement.src, e.clientX, e.clientY);
                        popupTimer = null;
                    }, parseInt(intervalP));
                }
            }
        });
        //-------------------------------------------------------------------------

        // hide popup
        function hidePopup() {
            if (!ishidePopupEnabled) return;
            imgElementsList = [];
            if (popupTimer) {
                clearTimeout(popupTimer);
            }
            popup.style.display = 'none';
            popupContainer.style.display = 'none';

            NoMode();
            popup.style.maxHeight = 'calc(90vh - 10px)';
            popup.style.width = '';
            popup.style.height = '';
            popupContainer.style.left = '50%';
            popupContainer.style.top = '50%';
            popupContainer.style.position = 'fixed';
            popupContainer.style.transform = 'translate(-50%, -50%) scaleX(1) scaleY(1)';
            popupContainer.style.transformOrigin = '';
            popupContainer.style.outline = '';
            backdrop.style.zIndex = '';
            backdrop.style.display = 'none';
            backdrop.style.backdropFilter = '';
            LeftBar.style.opacity = '0';
            RightBar.style.opacity = '0';
            style2.innerHTML = `
            .LeftBar::before,
            .LeftBar::after,
            .RighttBar::before,
            .RighttBar::after {
                display: none;
            }`;
            document.querySelectorAll('.specialElement').forEach(e => e.remove());
        }
        popupContainer.addEventListener('mouseout', function(event) {

            let relatedTarget = event.relatedTarget;
            if (relatedTarget && (popupContainer.contains(relatedTarget) || relatedTarget.matches('.imgContainers') || relatedTarget.closest('.imgContainers'))) {
                return;
            }

            document.querySelectorAll('.specialElement').forEach(e => e.remove());

            hidePopup();
            if (intervalP !== '') {
                popupTimer = setTimeout(() => {
                    popupTimer = null;
                }, parseInt(intervalP));
            }
        });
        document.addEventListener('keydown', function(event) {
            if (event.key === "Escape") {
                event.preventDefault();
                hidePopup();
            }
        });
        //-------------------------------------------------------------------------


        // lock popup in screen
        backdrop.addEventListener('dblclick', function(event) {
            clearTimeout(clickTimeout);
            ishidePopupEnabled = true;
            hidePopup();
        });
        backdrop.addEventListener('click', function(event) {
            if (clickTimeout) clearTimeout(clickTimeout);
            clickTimeout = setTimeout(function() {
                ResetGeometry();
                if (isScale) {
                    isScale = false;
                    isScaleTL = false;
                    isScaleTR = false;
                    isScaleBL = false;
                    isScaleBR = false;
                    ScalingMode0();

                } else if (isLockedY) {
                    isLockedY = false;
                    popupContainer.style.border = '';
                } else {
                    backdrop.style.zIndex = '';
                    backdrop.style.display = 'none';
                    backdrop.style.backdropFilter = '';
                    isshowPopupEnabled = false;
                }
            }, 300);
        });

    }
    //-------------------------------------------------------------------------


    // Is to be run -----------------------------------------------------------
    if (URLmatched) {
        const indicatorBar = document.createElement('div');
        indicatorBar.style.cssText = `
        position: fixed;
        bottom: 0;
        left: 50%;
        transform: translateX(-50%);
        z-index: 9999;
        height: 30px;
        width: 50vw;
        background: #0000;`;
        document.body.appendChild(indicatorBar);

        function toggleIndicator() {
            enableP = 1 - enableP;

            indicatorBar.style.background = enableP ? 'linear-gradient(to right, rgba(50, 190, 152, 0) 0%, rgba(50, 190, 152, 0.5) 25%, rgba(50, 190, 152, 0.5) 75%, rgba(50, 190, 152, 0) 100%)' : 'linear-gradient(to right, rgba(174, 0, 1, 0) 0%, rgba(174, 0, 1, 0.5) 25%, rgba(174, 0, 1, 0.5) 75%, rgba(174, 0, 1, 0) 100%)';
            setTimeout(() => {
                indicatorBar.style.background = '#0000';
            }, 1000);
            if (enableP === 1) {
                HoverZoomMinus();
            } else {
                document.querySelectorAll('.popup-container').forEach(e => e.remove());
                document.querySelectorAll('.popup-backdrop').forEach(e => e.remove());
                document.querySelectorAll('.specialElement').forEach(e => e.remove());
            }
        }
        let hoverTimeout;
        indicatorBar.addEventListener('mouseenter', () => {
            hoverTimeout = setTimeout(toggleIndicator, 500);
        });
        indicatorBar.addEventListener('mouseleave', () => {
            clearTimeout(hoverTimeout);
            indicatorBar.style.background = '#0000';
        });
        if (enableP === 1) {
            HoverZoomMinus();
        }
    } else {
        return;
    }

})();