Greasy Fork is available in English.

View Image

This userscript re-implements the "View Image" and "Search by image" buttons into google images.

// ==UserScript==
// @name            View Image
// @namespace       https://github.com/bijij/ViewImage
// @version         3.0.0
// @description     This userscript re-implements the "View Image" and "Search by image" buttons into google images.
// @author          Joshua B
// @run-at          document-end
// @include         http*://*.google.tld/search*tbm=isch*
// @include         http*://*.google.tld/imgres*
// ==/UserScript==
'use strict';

// Determines wether using the redesign or not.
function isRedesign() {
    return document.querySelector('.irc_ifr') != null;
}


// Finds the div which contains all required elements
function getContainer(node) {
    return node.closest('.irc_c[style*="visibility: visible;"][style*="transform: translate3d(0px, 0px, 0px);"], .irc_c[data-ved]');
}


// Finds and deletes all extension related elements.
function clearExtElements(container) {
    // Remove previously generated elements
    var oldExtensionElements = container.querySelectorAll('.vi_ext_addon');
    for (var element of oldExtensionElements) {
        element.remove();
    }
}


// Returns the image URL
function findImage(container, redesign) {

    var image = null;

    if (!redesign) {
        image = container.querySelector('img[src]#irc_mi, img[alt^="Image result"][src]:not([src^="https://encrypted-tbn"]).irc_mut, img[src].irc_mi');
    } else {
        var iframe = container.querySelector('iframe.irc_ifr');
        image = iframe.contentDocument.querySelector('img#irc_mi');
    }


    // Override url for images using base64 embeds
    if (image === null || image.src === '' || image.src.startsWith('data')) {
        var thumbnail = document.querySelector('img[name="' + container.dataset.itemId + '"]');
        if (thumbnail === null) {
            // If no thumbnail found, try getting image from URL
            var url = new URL(window.location);
            var imgLink = url.searchParams.get('imgurl');
            if (imgLink) {
                image = new Object();
                image.src = imgLink;
            }
        } else {
            var meta = thumbnail.closest('.rg_bx').querySelector('.rg_meta');

            var metadata = JSON.parse(meta.innerHTML);

            image = new Object();
            image.src = metadata.ou;
        }
    }

    // If the above doesn't work, use the link in related images to find it
    if (image === null || image.src === '' || image.src.startsWith('data')) {
        var target_image = container.querySelector('img.target_image');
        if (target_image) {
            var link = target_image.closest('a');
            if (link) {
                // Some extensions replace google image links with their original links
                if (link.href.match(/^[a-z]+:\/\/(?:www\.)?google\.[^/]*\/imgres\?/)) {
                    var link_url = new URL(link.href);
                    var new_imgLink = link_url.searchParams.get('imgurl');
                    if (new_imgLink) {
                        image = new Object();
                        image.src = new_imgLink;
                    }
                } else {
                    image = new Object();
                    image.src = link.href;
                }
            }
        }
    }
    return image;
}


function addViewImageButton(container, image, redesign) {
    // get the visit buttonm
    var visitButton = redesign ? container.querySelector('a.irc_hol[href]') : container.querySelector('td > a.irc_vpl[href]').parentElement;

    // Create the view image button
    var viewImgButton = visitButton.cloneNode(true);
    viewImgButton.classList.add('vi_ext_addon');

    // Set the view image button url
    var viewImageLink = redesign ? viewImgButton : viewImgButton.querySelector('a');
    viewImageLink.href = image.src;
    viewImageLink.setAttribute('target', '_blank');

    // Set the view image button text
    var viewImageButtonText = viewImgButton.querySelector(redesign ? '.irc_ho' : '.Tl8XHc');
    viewImageButtonText.innerText = 'View Image';

    // Place the view image button
    visitButton.parentElement.insertBefore(viewImgButton, visitButton);
    visitButton.parentElement.insertBefore(visitButton, viewImgButton);
}


function addSearchImgButton(container, image, redesign) {

    var link = redesign ? container.querySelector('.irc_ft > a.irc_help') : container.querySelector('.irc_dsh > a.irc_hol');

    // Create the search by image button
    var searchImgButton = link.cloneNode(true);
    searchImgButton.classList.add('vi_ext_addon');

    // Set the more sizes button text
    var searchImgButtonText = searchImgButton.querySelector(redesign ? 'span' : '.irc_ho');
    searchImgButtonText.innerText = 'Search by image';


    // Set the search by image button url
    searchImgButton.href = '/searchbyimage?image_url=' + image.src;
    searchImgButton.setAttribute('target', '_blank');

    // Place the more sizes button
    link.parentElement.insertBefore(searchImgButton, link);
    link.parentElement.insertBefore(link, searchImgButton);

}


// Adds links to an object
function addLinks(node) {

    // Determine wether redesign or not
    var redesign = isRedesign();

    // Find the container
    var container = getContainer(node);

    // Return if no container was found
    if (!container)
        return;

    // Clear any old extension elements
    clearExtElements(container);

    // Find the image url
    var image = findImage(container, redesign);

    // Return if image was not found
    if (!image)
        return;

    addViewImageButton(container, image, redesign);
    addSearchImgButton(container, image, redesign);
}

// Define the mutation observers
var observer = new MutationObserver(function (mutations) {
    for (var mutation of mutations) {
        if (mutation.addedNodes && mutation.addedNodes.length > 0) {
            for (var node of mutation.addedNodes) {
                if (node.classList) {
                    // Check for new image nodes
                    if (node.classList.contains('irc_mi') | node.classList.contains('irc_mut') | node.classList.contains('irc_ris')) {
                        addLinks(node);
                    }
                }
            }
        }
    }
});

observer.observe(document.body, {
    childList: true,
    subtree: true
});

// inject CSS into document
var customStyle = document.createElement('style');
customStyle.innerText = '.irc_dsh>.irc_hol.vi_ext_addon,.irc_ft>.irc_help.vi_ext_addon{margin: 0 4pt!important}.irc_hol.vi_ext_addon{flex-grow:0!important}';
document.head.appendChild(customStyle);