Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Userscript version of the View Image chrome extension
// ==UserScript==
// @name View Image
// @namespace https://github.com/bijij/ViewImage
// @version 3.3.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*
// @updateURL https://gist.githubusercontent.com/bijij/58cc8cfc859331e4cf80210528a7b255/raw/viewimage.user.js
// ==/UserScript==
'use strict';
const DEBUG = false;
const VERSIONS = {
FEB18: 'FEB18',
JUL19: 'JUL19',
OCT19: 'OCT19'
};
var images = new Object();
function toI18n(str) {
return str.replace(/__MSG_(\w+)__/g, function (match, v1) {
return v1 ? chrome.i18n.getMessage(v1) : '';
});
}
// Finds the div which contains all required elements
function getContainer(node) {
var container, version;
[
['.irc_c[style*="visibility: visible;"][style*="transform: translate3d(0px, 0px, 0px);"]', VERSIONS.FEB18],
['.irc_c[data-ved]', VERSIONS.JUL19],
['.tvh9oe', VERSIONS.OCT19]
].forEach(element => {
if (node.closest(element[0])) {
[container, version] = [node.closest(element[0]), element[1]];
}
});
return [container, version];
}
// 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 findImageURL(container, version) {
var image = null;
switch (version) {
case VERSIONS.FEB18:
image = container.querySelector('img[src]#irc_mi, img[alt^="Image result"][src]:not([src^="https://encrypted-tbn"]).irc_mut, img[src].irc_mi');
break;
case VERSIONS.JUL19:
var iframe = container.querySelector('iframe.irc_ifr');
if (!iframe)
return findImageURL(container, VERSIONS.FEB18);
image = iframe.contentDocument.querySelector('img#irc_mi');
break;
case VERSIONS.OCT19:
image = container.querySelector('img[src].n3VNCb');
if (image.src in images) {
return images[image.src];
}
}
// 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) {
return imgLink;
}
} else {
var meta = thumbnail.closest('.rg_bx').querySelector('.rg_meta');
var metadata = JSON.parse(meta.innerHTML);
return 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) {
return new_imgLink;
}
} else {
return link.href;
}
}
}
}
if (image) {
return image.src;
}
}
function addViewImageButton(container, imageURL, version) {
// get the visit buttonm
var visitButton;
switch (version) {
case VERSIONS.FEB18:
visitButton = container.querySelector('td > a.irc_vpl[href]').parentElement;
break;
case VERSIONS.JUL19:
visitButton = container.querySelector('a.irc_hol[href]');
break;
case VERSIONS.OCT19:
visitButton = container.querySelector('.ZsbmCf[href]');
break;
}
// Create the view image button
var viewImageButton = visitButton.cloneNode(true);
viewImageButton.classList.add('vi_ext_addon');
// Set the view image button url
var viewImageLink;
switch (version) {
case VERSIONS.FEB18:
viewImageLink = viewImageButton.querySelector('a');
break;
default:
viewImageLink = viewImageButton;
}
viewImageLink.href = imageURL;
if (version == VERSIONS.OCT19) {
viewImageLink.removeAttribute('jsaction');
}
// Set additional options
if (true) {
viewImageLink.setAttribute('target', '_blank');
}
if (false) {
viewImageLink.setAttribute('rel', 'noreferrer');
}
// Set the view image button text
var viewImageButtonText;
switch (version) {
case VERSIONS.FEB18:
viewImageButtonText = viewImageButton.querySelector('.Tl8XHc');
break;
case VERSIONS.JUL19:
viewImageButtonText = viewImageButton.querySelector('.irc_ho');
break;
case VERSIONS.OCT19:
viewImageButtonText = viewImageButton.querySelector('.pM4Snf');
break;
}
viewImageButtonText.innerText = 'View Image';
// Remove globe icon if not wanted
if (false) {
switch (version) {
case VERSIONS.FEB18:
viewImageButton.querySelector('.RL3J9c').remove();
break;
case VERSIONS.JUL19:
viewImageButton.querySelector('.aDEWOd').remove();
break;
case VERSIONS.OCT19:
viewImageButton.querySelector('.XeEBj.AJkoub').remove();
break;
}
}
// Place the view image button
visitButton.parentElement.insertBefore(viewImageButton, visitButton);
visitButton.parentElement.insertBefore(visitButton, viewImageButton);
}
function addSearchImageButton(container, imageURL, version) {
var link;
switch (version) {
case VERSIONS.FEB18:
link = container.querySelector('.irc_dsh > a.irc_hol');
break;
case VERSIONS.JUL19:
link = container.querySelector('.irc_ft > a.irc_help');
break;
case VERSIONS.OCT19:
link = container.querySelector('.PvkmDc');
break;
}
// Create the search by image button
var searchImageButton = link.cloneNode(true);
searchImageButton.classList.add('vi_ext_addon');
// Set the more sizes button text
var searchImageButtonText;
switch (version) {
case VERSIONS.FEB18:
searchImageButtonText = container.querySelector('.irc_ho');
break;
case VERSIONS.JUL19:
searchImageButtonText = searchImageButton.querySelector('span');
break;
case VERSIONS.OCT19:
searchImageButtonText = searchImageButton;
break;
}
searchImageButtonText.innerText = 'Search by Image';
// Set the search by image button url
searchImageButton.href = '/searchbyimage?image_url=' + imageURL;
// Set additional options
if (true) {
searchImageButton.setAttribute('target', '_blank');
}
// Place the more sizes button
link.parentElement.insertBefore(searchImageButton, link);
link.parentElement.insertBefore(link, searchImageButton);
}
// Adds links to an object
function addLinks(node) {
if (DEBUG)
console.log('ViewImage: Trying to add links to node: ', node);
// Find the container
var [container, version] = getContainer(node);
// Return if no container was found
if (!container) {
if (DEBUG)
console.log('ViewImage: Adding links failed, container was not found.');
return;
}
if (DEBUG)
console.log('ViewImage: Assuming site version: ', version);
// Clear any old extension elements
clearExtElements(container);
// Find the image url
var imageURL = findImageURL(container, version);
// Return if image was not found
if (!imageURL) {
if (DEBUG)
console.log('ViewImage: Adding links failed, image was not found.');
return;
}
addViewImageButton(container, imageURL, version);
addSearchImageButton(container, imageURL, version);
}
// Check if source holds array of images
try {
const start_search = 'AF_initDataCallback({key: \'ds:2\', isError: false , hash: \'3\', data:function(){return ';
const end_search = '}});</script>';
var start_index = document.documentElement.innerHTML.indexOf(start_search) + start_search.length;
var end_index = start_index + document.documentElement.innerHTML.slice(start_index).indexOf(end_search);
var array = JSON.parse(document.documentElement.innerHTML.slice(start_index, end_index));
var meta = array[31][0][12][2];
for (var i = 0; i < meta.length; i++) {
try {
images[meta[i][1][2][0]] = meta[i][1][3][0];
} catch (error) {
if (DEBUG)
console.log('ViewImage: Skipping image');
}
}
if (DEBUG)
console.log('ViewImage: Successfully created source images array.');
} catch (error) {
if (DEBUG)
console.log('ViewImage: Failed to create source images array.');
}
// Define the mutation observers
var observer = new MutationObserver(function (mutations) {
if (DEBUG)
console.log('ViewImage: Mutations detected: ', mutations);
for (var mutation of mutations) {
var node;
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
for (node of mutation.addedNodes) {
if (node.classList) {
// Check for new image nodes
if (['irc_mi', 'irc_mut', 'irc_ris', 'n3VNCb'].some(className => node.classList.contains(className))) {
addLinks(node);
}
}
}
}
if (mutation.target.classList && mutation.target.classList.contains('n3VNCb')) {
node = mutation.target.closest('.tvh9oe');
if (!node.hasAttribute('aria-hidden')) {
addLinks(node);
}
}
}
});
// Start adding links
if (DEBUG)
console.log('ViewImage: Initialising observer...');
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true
});
// inject CSS into document
if (DEBUG)
console.log('ViewImage: Injecting CSS...');
var customStyle = document.createElement('style');
customStyle.innerText = '.irc_dsh>.irc_hol.vi_ext_addon,.irc_ft>.irc_help.vi_ext_addon,.PvkmDc.vi_ext_addon{margin: 0 4pt!important}.irc_hol.vi_ext_addon{flex-grow:0!important}.ZsbmCf.vi_ext_addon{flex-grow:0}';
document.head.appendChild(customStyle);
@artur3b

This comment has been minimized.

Copy link

@artur3b artur3b commented Mar 20, 2018

this script didn't work

@psychonaut25

This comment has been minimized.

Copy link

@psychonaut25 psychonaut25 commented Mar 28, 2018

It still doesn't work

@trlkly

This comment has been minimized.

Copy link

@trlkly trlkly commented Apr 6, 2018

This script, as written, doesn't work, as you left some stuff in that's only available to the WebExtension concept. You can't use localiseObject().

I have fixed it in a fork: https://gist.github.com/trlkly/cb4f9a349259f1df4eeef6e3f438600c

@gitterliu

This comment has been minimized.

Copy link

@gitterliu gitterliu commented Oct 28, 2018

viewImageTextClone.innerText = "Search By Image"; Should be viewImageTextClone.innerText = "View Image";

@blinkenlight

This comment has been minimized.

Copy link

@blinkenlight blinkenlight commented Dec 15, 2018

@gitterliu Thanks for that, you're a true lifesaver, I had no idea why it was not doing anything. Also thanks to @bijij of course...

@LOuroboros

This comment has been minimized.

Copy link

@LOuroboros LOuroboros commented Aug 12, 2019

EDIT: I just checked the forks of this userscript, and saviorxzero98's does work.
firefox-2019-08-14_00-57-06

Original post:
I tried this userscript using ViolentMonkey v2.11.2 on Firefox 68.0.1 x86, and it doesn't really work on my end.
It's basically meant to be an alternative to the addon version, right? It should bring back the View Image button on Google Images, but it doesn't.
Note: I haven't modified the userscript in the slightest. The picture showing it off is simply a way to confirm that I have installed the userscript.
2019-08-12_16-27-09
2019-08-12_16-26-57

@BaNru

This comment has been minimized.

Copy link

@BaNru BaNru commented Aug 18, 2019

LOuroboros +1
Fork from @saviorxzero98 works
LINK: https://gist.github.com/saviorxzero98/88662a751b48cfeb90f22a61ca4567eb

Change your language (line 16 and 17)

var i18nText = {
    searchByImage: '以圖搜尋',
    viewImage: '檢視圖片'
}

for example

var i18nText = {
    searchByImage: 'Search by image',
    viewImage: 'View image'
}

If your script does not start (it didn’t start on Firefox + Greasemonkey), then replace the include's

// @include      http://www.google.*/search?*tbm=isch*
// @include      https://www.google.*/search?*tbm=isch*
// @include      http*://*.google.tld/search*tbm=isch*
@frozenpandaman

This comment has been minimized.

Copy link

@frozenpandaman frozenpandaman commented Dec 13, 2019

@BaNru doesn't seem to anymore :(

@Chuck2134

This comment has been minimized.

Copy link

@Chuck2134 Chuck2134 commented Feb 4, 2020

when i click the view image button it only loads thumbnails now not the full size images

@sudokai

This comment has been minimized.

Copy link

@sudokai sudokai commented Apr 11, 2020

Hey Josh, I updated the script with the latest changes from v3.3.0:

https://gist.github.com/sudokai/2464bb1c94f492b1adad4152da68f435

Hope you apply the changes soon!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.