image color viewer

Adds context menu options and shortcuts to view images with white/black backgrounds. Includes "Both" side-by-side option in new tab.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         image color viewer
// @namespace    github.com/annaprojects
// @version      1.6
// @description  Adds context menu options and shortcuts to view images with white/black backgrounds. Includes "Both" side-by-side option in new tab.
// @author       AnnaRoblox
// @match        *://*/*
// @grant        GM_registerMenuCommand
// @grant        GM_openInTab
// ==/UserScript==

(function() {
    'use strict';

    let imageUrl = null;
    let currentImageElement = null;

    const WRAPPER_CLASS = 'gm-image-bg-wrapper';
    const DUAL_WRAPPER_CLASS = 'gm-image-bg-dual-container';

    /**
     * Context menu tracking
     */
    document.addEventListener('contextmenu', function(e) {
        if (e.target.tagName === 'IMG') {
            imageUrl = e.target.src;
            currentImageElement = e.target;
        } else {
            imageUrl = null;
            currentImageElement = null;
        }
    }, true);

    /**
     * Click shortcut handling
     */
    document.addEventListener('click', function(e) {
        if (e.target.tagName === 'IMG') {
            const imageElement = e.target;
            let handled = false;

            // Ctrl + Alt + Click: Side-by-Side Dual View (Black Left, White Right)
            if (e.ctrlKey && e.altKey) {
                setDualBackground(imageElement);
                handled = true;
            }
            // Ctrl + Shift + Click: Set background to White
            else if (e.ctrlKey && e.shiftKey) {
                setImageBackground(imageElement, 'white');
                handled = true;
            }
            // Alt + Shift + Click: Set background to Black
            else if (e.altKey && e.shiftKey) {
                setImageBackground(imageElement, 'black');
                handled = true;
            }
            // Alt + Click: Toggle background between White and Black
            else if (e.altKey && !e.shiftKey) {
                const parent = imageElement.parentNode;
                let currentBgColor = '';

                if (parent && parent.classList && parent.classList.contains(WRAPPER_CLASS)) {
                    currentBgColor = parent.style.backgroundColor;
                }

                if (currentBgColor === 'black') {
                    setImageBackground(imageElement, 'white');
                } else {
                    setImageBackground(imageElement, 'black');
                }
                handled = true;
            }

            if (handled) {
                e.preventDefault();
                e.stopImmediatePropagation();
            }
        }
    }, true);

    /**
     * Menu Commands
     */
    GM_registerMenuCommand("View Image in New Tab", () => imageUrl && openImageViewer(imageUrl, 'white'));
    GM_registerMenuCommand("Set Image BG (White)", () => currentImageElement && setImageBackground(currentImageElement, 'white'));
    GM_registerMenuCommand("Set Image BG (Black)", () => currentImageElement && setImageBackground(currentImageElement, 'black'));
    GM_registerMenuCommand("Set Image BG (Transparent)", () => currentImageElement && setImageBackground(currentImageElement, 'transparent'));
    GM_registerMenuCommand("Reset Image BG", () => currentImageElement && resetImageBackground(currentImageElement));

    /**
     * Side-by-Side view logic (Current Page)
     */
    function setDualBackground(imageElement) {
        if (imageElement.parentNode.classList.contains(WRAPPER_CLASS)) {
            resetImageBackground(imageElement);
        }

        const parent = imageElement.parentNode;
        const dualContainer = document.createElement('div');
        dualContainer.classList.add(DUAL_WRAPPER_CLASS);

        dualContainer.style.cssText = `
            display: inline-flex !important;
            flex-direction: row;
            gap: 15px;
            padding: 10px;
            background-color: #222;
            border-radius: 8px;
            vertical-align: middle;
            position: relative;
            z-index: 9999;
        `;

        const createSubWrapper = (color) => {
            const w = document.createElement('div');
            w.classList.add(WRAPPER_CLASS);
            w.style.cssText = `display: inline-block; line-height: 0; background-color: ${color}; padding: 0; margin: 0; flex: 0 0 auto;`;
            return w;
        };

        const blackWrapper = createSubWrapper('black');
        const whiteWrapper = createSubWrapper('white');
        const clonedImage = imageElement.cloneNode(true);

        parent.insertBefore(dualContainer, imageElement);
        blackWrapper.appendChild(imageElement);
        whiteWrapper.appendChild(clonedImage);
        dualContainer.appendChild(blackWrapper);
        dualContainer.appendChild(whiteWrapper);

        [imageElement, clonedImage].forEach(img => {
            img.style.cssText = "max-width: none; max-height: none; display: block; position: static; visibility: visible; opacity: 1;";
        });
    }

    /**
     * Single background logic (Current Page)
     */
    function setImageBackground(imageElement, bgColor) {
        const parent = imageElement.parentNode;
        if (parent.classList.contains(WRAPPER_CLASS) && parent.parentNode.classList.contains(DUAL_WRAPPER_CLASS)) {
            resetImageBackground(imageElement);
        }

        const currentParent = imageElement.parentNode;
        let wrapper = null;

        if (currentParent && currentParent.classList && currentParent.classList.contains(WRAPPER_CLASS)) {
            wrapper = currentParent;
        } else {
            wrapper = document.createElement('div');
            wrapper.classList.add(WRAPPER_CLASS);
            wrapper.style.cssText = "display: inline-block; line-height: 0; vertical-align: middle; transition: background-color 0.3s ease;";
            currentParent.insertBefore(wrapper, imageElement);
            wrapper.appendChild(imageElement);
        }

        if (bgColor === 'transparent') {
            wrapper.style.backgroundImage = 'linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%)';
            wrapper.style.backgroundSize = '20px 20px';
            wrapper.style.backgroundColor = '';
        } else {
            wrapper.style.backgroundImage = 'none';
            wrapper.style.backgroundColor = bgColor;
        }

        imageElement.style.maxWidth = 'none';
        imageElement.style.maxHeight = 'none';
        imageElement.style.position = 'static';
    }

    /**
     * Restoration logic
     */
    function resetImageBackground(imageElement) {
        let parent = imageElement.parentNode;
        if (parent.classList.contains(WRAPPER_CLASS) && parent.parentNode.classList.contains(DUAL_WRAPPER_CLASS)) {
            const dualContainer = parent.parentNode;
            dualContainer.parentNode.insertBefore(imageElement, dualContainer);
            dualContainer.parentNode.removeChild(dualContainer);
        }
        else if (parent && parent.classList && parent.classList.contains(WRAPPER_CLASS)) {
            parent.parentNode.insertBefore(imageElement, parent);
            parent.parentNode.removeChild(parent);
        }
        imageElement.style.maxWidth = '';
        imageElement.style.maxHeight = '';
        imageElement.style.position = '';
    }

    /**
     * External Tab Viewer
     */
    function openImageViewer(url, initialBgColor) {
        const imageViewerHTML = `
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="UTF-8">
                <title>Image Viewer - Dual Mode</title>
                <style>
                    body {
                        background-color: ${initialBgColor};
                        margin: 0;
                        display: flex;
                        flex-direction: column;
                        justify-content: center;
                        align-items: center;
                        min-height: 100vh;
                        transition: background-color 0.3s ease;
                        font-family: sans-serif;
                        overflow: auto;
                    }
                    .controls {
                        position: fixed;
                        top: 20px;
                        background: rgba(255, 255, 255, 0.95);
                        padding: 12px 20px;
                        border-radius: 12px;
                        display: flex;
                        gap: 12px;
                        box-shadow: 0 4px 15px rgba(0,0,0,0.3);
                        z-index: 100;
                    }
                    button {
                        cursor: pointer;
                        padding: 8px 16px;
                        border: 1px solid #ccc;
                        border-radius: 6px;
                        background: white;
                        font-weight: bold;
                        transition: all 0.2s;
                    }
                    button:hover { background: #f0f0f0; border-color: #999; }
                    button.active { background: #007bff; color: white; border-color: #0056b3; }

                    #viewContainer {
                        margin-top: 80px;
                        display: flex;
                        justify-content: center;
                        align-items: center;
                    }

                    /* Dual View Styles */
                    .dual-layout {
                        display: flex;
                        gap: 20px;
                        padding: 20px;
                        background: #111;
                        border-radius: 10px;
                    }
                    .img-wrap { line-height: 0; }
                    .dual-layout img { max-width: 45vw; height: auto; }

                    /* Single View Styles */
                    .single-view img {
                        max-width: 90vw;
                        height: auto;
                        box-shadow: 0 0 30px rgba(0,0,0,0.2);
                    }
                </style>
            </head>
            <body>
                <div class="controls">
                    <button id="btn-white" onclick="setView('white')">White</button>
                    <button id="btn-black" onclick="setView('black')">Black</button>
                    <button id="btn-trans" onclick="setView('transparent')">Transparent</button>
                    <button id="btn-both" onclick="setView('both')">Both</button>
                </div>

                <div id="viewContainer" class="single-view">
                    <img src="${url}" id="mainImg">
                </div>

                <script>
                    const url = "${url}";
                    const container = document.getElementById('viewContainer');

                    function setView(mode) {
                        // Reset active states
                        document.querySelectorAll('button').forEach(b => b.classList.remove('active'));
                        document.getElementById('btn-' + mode.substring(0,5)).classList.add('active');

                        if (mode === 'both') {
                            document.body.style.backgroundColor = '#222';
                            document.body.style.backgroundImage = 'none';
                            container.className = '';
                            container.innerHTML = \`
                                <div class="dual-layout">
                                    <div class="img-wrap" style="background: black;"><img src="\${url}"></div>
                                    <div class="img-wrap" style="background: white;"><img src="\${url}"></div>
                                </div>
                            \`;
                        } else {
                            container.className = 'single-view';
                            container.innerHTML = \`<img src="\${url}">\`;

                            const b = document.body;
                            if (mode === 'transparent') {
                                b.style.backgroundImage = 'linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%)';
                                b.style.backgroundSize = '20px 20px';
                                b.style.backgroundColor = '';
                            } else {
                                b.style.backgroundImage = 'none';
                                b.style.backgroundColor = mode;
                            }
                        }
                    }

                    // Set initial active button
                    document.getElementById('btn-white').classList.add('active');
                <\/script>
            </body>
            </html>
        `;

        const blob = new Blob([imageViewerHTML], { type: 'text/html' });
        const blobURL = URL.createObjectURL(blob);
        GM_openInTab(blobURL, { active: true });
        setTimeout(() => URL.revokeObjectURL(blobURL), 1000);
    }

})();