Explode Thumbs

Shows the full sized image for every thumbnail in a page.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Explode Thumbs
// @description  Shows the full sized image for every thumbnail in a page.
// @match        file:///*
// @match        *://*/*
// @version      2025.02.01
// @author       Diogo Kollross
// @collaborator Schimon Jehudah
// @namespace    i2p.schimon.explode-thumbs
// @license      Unknown
// @homepageURL  https://greatest.deepsurf.us/scripts/522208-explode-thumbs
// @supportURL   https://greatest.deepsurf.us/scripts/522208-explode-thumbs/feedback
// @run-at       context-menu
// ==/UserScript==

const AutoscrollStepDelay = 50;

// We get all images inside links for now. We check if they really
// are thumbnails later.
var allThumbs;
allThumbs = document.evaluate(
    '//a[@href]/img[@src]',
    document,
    null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
    null
);

// overlayHtml also works as a flag. If there is any constructed HTML
// it means we found at least one thumbnail.
var overlayHtml = '';
for (var i = 0; i < allThumbs.snapshotLength; i++) {
    var thumb = allThumbs.snapshotItem(i);
    var thumbImage = thumb.src;
    var parentLink = thumb.parentNode;
    var linkHref = parentLink.href;

    // Every link that is an image and points to another image is
    // considered a thumbnail. I wish there was a better way to know
    // if a URL is an image, but I think this will work for most cases.
    if (/[.](bmp|gif|jpe?g|png|svg)$/.test(linkHref)) {
        overlayHtml +=
            '<p style="margin: 0.5em 0.1em"><img style="width: 100%;" src="'
            + linkHref + '" /></p>'
        ;
    }
}

if (overlayHtml) {
    var overlay = document.createElement('div');
    with (overlay.style) {
        padding = '0em 0.5em';
        borderRight = 'dashed #999 2px';
        borderBottom = 'dashed #999 2px';
        background = 'white';
        position = 'absolute';
        top = '0';
        left = '0';
        width = '100%';

        // Some sites use some weird complex layout, so we put
        // our overlay right in front of everyone to make sure it
        // does not get covered.
        zIndex = '10000';
    }
    document.body.insertBefore(overlay, document.body.firstChild);

    overlayHtml =
        '<div style="position: fixed; background: #eee; top: 0;'
        + ' left: 0; right: 0; padding: 0.5em 0.5em;'
        + 'border-bottom: solid #ccc 1px">'
            + '<div style="float: left">'
                + '<button>Autoscroll</button>'
                + ' <select>'
                    + '<option value="1">1 second/image</option>'
                    + '<option value="5">5 seconds/image</option>'
                    + '<option value="10">10 seconds/image</option>'
                    + '<option value="15">15 seconds/image</option>'
                    + '<option value="20">20 seconds/image</option>'
                + '</select>'
                + ' <button disabled="yes">Stop</button>'
            + '</div>'
            + '<div style="text-align: right">'
                + '<button>Hide</button>'
                + ' <button disabled="yes">Show</button>'
                //+ '<button>&#x2227;</button>'
                //+ ' <button disabled="yes">&#x2228;</button>'
                + '&nbsp; <button>Close</button>'
            + '</div>'
        + '</div>'
        + '<div style="margin-top: 3.2em">' + overlayHtml + '</div>'
    ;
    overlay.innerHTML = overlayHtml;

    var toolbarDiv = overlay.firstChild;
    window.addEventListener(
        'load',
        function() {
            toolbarDiv.style.width = getComputedStyle(overlay, '').width;
        },
        true
    );
    var imagesDiv = overlay.childNodes.item(1);

    var leftButtons = toolbarDiv.firstChild;
    var autoscrollButton = leftButtons.childNodes.item(0);
    var autoscrollDelay = leftButtons.childNodes.item(2);
    var stopButton = leftButtons.childNodes.item(4);

    var rightButtons = toolbarDiv.childNodes.item(1);
    var hideButton = rightButtons.firstChild;
    var showButton = rightButtons.childNodes.item(2);
    var closeButton = rightButtons.childNodes.item(4);

    autoscrollDelay.value =
        GM_getValue('autoscroll.delay', 10).toString()
    ;

    // timeoutId is used to cancel the autoscroll function so
    // we leave it out here where it can be seen by the Stop button
    // onclick handler.
    var timeoutId = null;

    function autoScroll(position, deltaY, steps) {
        if (steps) {
            position += deltaY;
            window.scrollTo(0, position);
            timeoutId = window.setTimeout(
                function() { autoScroll(position, deltaY, steps - 1); },
                AutoscrollStepDelay
            );
        } else {
            autoscrollButton.disabled = false;
            autoscrollDelay.disabled = false;
            stopButton.disabled = true;
            hideButton.disabled = false;
        }
    }

    // Some magic is used here to set the onclick handler of the
    // created controls. It seems that this must be used to set
    // any event handler from GreaseMonkey scripts. The call to
    // event.preventDefault avoids bubbling of the click event.
    autoscrollButton.addEventListener(
        'click',
        function(event) {
            autoscrollButton.disabled = true;
            autoscrollDelay.disabled = true;
            stopButton.disabled = false;
            hideButton.disabled = true;

            var expandedImages = overlay.getElementsByTagName('img');
            var imageCount = expandedImages.length;
            var expandedTop = expandedImages[0].offsetTop;

            var availableHeight = window.innerHeight - toolbarDiv.offsetHeight;
            var lastImage = expandedImages[imageCount - 1];
            var totalDelta =
                lastImage.offsetTop
                + lastImage.offsetHeight
                - expandedTop
            ;
            var offscreenDelta = Math.max(
                0, totalDelta - availableHeight
            );

            var totalTime =
                parseInt(autoscrollDelay.value) * 1000 * imageCount
            ;
            var steps = totalTime / AutoscrollStepDelay;
            var deltaY = offscreenDelta / steps;

            var firstPosition = expandedTop - toolbarDiv.offsetHeight;
            window.scrollTo(0, firstPosition);

            timeoutId = window.setTimeout(
                function() { autoScroll(firstPosition, deltaY, steps); },
                AutoscrollStepDelay
            );

            event.preventDefault();
        },
        true
    );

    autoscrollDelay.addEventListener(
        'change',
        function(event) {
            GM_setValue(
                'autoscroll.delay',
                parseInt(autoscrollDelay.value)
            );
            event.preventDefault();
        },
        true
    );

    stopButton.addEventListener(
        'click',
        function(event) {
            window.clearTimeout(timeoutId);
            timeoutId = null;
            autoscrollButton.disabled = false;
            autoscrollDelay.disabled = false;
            stopButton.disabled = true;
            hideButton.disabled = false;
            event.preventDefault();
        },
        true
    );

    hideButton.addEventListener(
        'click',
        function(event) {
            autoscrollButton.disabled = true;
            autoscrollDelay.disabled = true;
            hideButton.disabled = true;
            showButton.disabled = false;
            imagesDiv.style.visibility = 'hidden';
            var toolbarDivStyle = getComputedStyle(toolbarDiv, '');
            overlay.style.height =
                parseInt(toolbarDivStyle.height)
                + parseInt(toolbarDivStyle.paddingTop)
                + parseInt(toolbarDivStyle.paddingBottom)
                + 'px'
            ;
            overlay.style.position = 'fixed';
            overlay.style.opacity = '0.75';
            event.preventDefault();
        },
        true
    );

    showButton.addEventListener(
        'click',
        function(event) {
            autoscrollButton.disabled = false;
            autoscrollDelay.disabled = false;
            hideButton.disabled = false;
            showButton.disabled = true;
            imagesDiv.style.visibility = 'visible';
            overlay.style.height = 'auto';
            overlay.style.position = 'absolute';
            overlay.style.opacity = '1';
            event.preventDefault();
        },
        true
    );

    closeButton.addEventListener(
        'click',
        function(event) {
            if (timeoutId != null) {
                window.clearTimeout(timeoutId);
            }
            overlay.parentNode.removeChild(overlay);
            event.preventDefault();
        },
        true
    );
}