-
-
Save bijij/58cc8cfc859331e4cf80210528a7b255 to your computer and use it in GitHub Desktop.
// ==UserScript== | |
// @name View Image | |
// @namespace https://github.com/bijij/ViewImage | |
// @version 4.1.1 | |
// @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(); | |
// 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, img[src].r48jcc'); | |
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], a.J2oL9c, a.jAklOc, a.uZ49bd, a.e0XTue, a.kWgFk, a.j7ZI7c'); | |
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 | |
viewImageLink.setAttribute('target', '_blank'); | |
// 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, .KSvtLc, .Pw5kW, .q7UPLe, .K8E1Be, .pFBf7b, span'); | |
break; | |
} | |
viewImageButtonText.innerText = 'View Image'; | |
// 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, .qnLx5b, .zSA7pe, .uZ49bd, .e0XTue, .kWgFk, .j7ZI7c'); | |
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=' + encodeURIComponent(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); | |
} | |
function parseDataSource(array) { | |
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'); | |
} | |
} | |
} | |
function parseDataSource1() { | |
//const start_search = /AF_initDataCallback\({key:\s'ds:1',\sisError:\s{2}false\s,\shash:\s'\d+',\sdata:/; // Deprecated RegEx ('isError' not matched) | |
const start_search = /AF_initDataCallback\({key:\s'ds:1',\shash:\s'\d+',\sdata:/; | |
const end_search = ', sideChannel: {}});</script>'; | |
var match = document.documentElement.innerHTML.match(start_search); | |
var start_index = match.index + match[0].length; | |
var end_index = start_index + document.documentElement.innerHTML.slice(start_index).indexOf(end_search); | |
parseDataSource(JSON.parse(document.documentElement.innerHTML.slice(start_index, end_index))); | |
} | |
function parseDataSource2() { | |
const start_search = /AF_initDataCallback\({key:\s'ds:2',\sisError:\s{2}false\s,\shash:\s'\d+',\sdata:function(){return\s/; | |
const end_search = '}});</script>'; | |
var match = document.documentElement.innerHTML.match(start_search); | |
var start_index = match.index + match[0].length; | |
var end_index = start_index + document.documentElement.innerHTML.slice(start_index).indexOf(end_search); | |
parseDataSource(JSON.parse(document.documentElement.innerHTML.slice(start_index, end_index))); | |
} | |
// Check if source holds array of images | |
try { | |
if (document.documentElement.innerHTML.indexOf('key: \'ds:1\'') != -1) { | |
if (DEBUG) | |
console.log('ViewImage: Attempting to parse data source 1.'); | |
parseDataSource1(); | |
} else if (document.documentElement.innerHTML.indexOf('key: \'ds:2\'') != -1) { | |
if (DEBUG) | |
console.log('ViewImage: Attempting to parse data source 2.'); | |
parseDataSource2(); | |
} else { | |
throw 'Could not determine data source type.'; | |
} | |
if (DEBUG) | |
console.log('ViewImage: Successfully created source images array.'); | |
} catch (error) { | |
if (DEBUG) { | |
console.log('ViewImage: Failed to create source images array.'); | |
console.error(error); | |
} | |
} | |
// Define the mutation observers | |
var observer = new MutationObserver(function (mutations) { | |
if (DEBUG) | |
console.log('ViewImage: Mutations detected: ', mutations); | |
var node; | |
for (var mutation of mutations) { | |
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', 'r48jcc'].some(className => node.classList.contains(className))) { | |
addLinks(node); | |
} | |
} | |
} | |
} | |
if (mutation.target.classList && mutation.target.classList.contains('n3VNCb', 'r48jcc')) { | |
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, | |
.qnLx5b.vi_ext_addon | |
{ | |
margin: 0 4pt!important | |
} | |
.irc_hol.vi_ext_addon | |
{ | |
flex-grow:0!important | |
} | |
.zSA7pe[href^="/searchbyimage"] { | |
margin-left: 4px; | |
} | |
.ZsbmCf.vi_ext_addon{ | |
flex-grow:0 | |
}`; | |
document.head.appendChild(customStyle); |
Do you have the View Image extension installed and enabled? You can't have both running at the same time, either the extension or the user script (this one or mine).
Make sure you don't have multiple scripts running and conflicting with each other because your issue seems to be something else entirely.
I only have your script enabled in ViolentMonkey. I have the extension installed, but disabled.
The script version has worked fine for me before, because I switched to it temporarily about 4 months ago when the extension stopped working.
View Image stopped working for me when Google changed the layout of how the buttons are displayed in Firefox.
For example, in FF, I see this: -
But in Chrome, for the same image, I see this: -
Previously, FF looked the same as Chrome with a button that had "Visit" written on it. Now it doesn't. When that changed is when View Image stopped working.
Given the visual changes, that may indicate deeper structural changes that break how View Image iterates through the elements. I cannot reproduce this, I'm on a desktop and I don't have that button layout in FF, mine is still showing classic like your second screenshot.
But still, your Chrome browser also doesn't seem to have View Image working either, are you using extension or userscript in it too? What OS do you have? What's your screen resolution?
I don't see a viable way to help in this situation... could you maybe post the exact HTML code that you get from FF's new visit button layout through inspection?
But still, your Chrome browser also doesn't seem to have View Image working either, are you using extension or userscript in it too? What OS do you have? What's your screen resolution?
I didn't have it installed in Chrome, as I just use that as a backup in case a site has an issue in FF.
I did try installing it just now though, and it does not work. Likewise with MS Edge. EDIT - I just tried the script version in Chrome and that doesn't work either.
I'm using Windows 10 and my screen resolution is 4K, but nothing has changed there.
could you maybe post the exact HTML code that you get from FF's new visit button layout through inspection?
I can see that it no longer uses the <span>
element, which would typically be inside that div
, before the svg
:
<span class="iLgTbf CzIFEe cS4Vcb-pGL6qe-lfQAOe">Visit</span>
And also the fact that you can't get it working with other browsers, like Edge and Chrome, where the buttons have the classic visual seems to hint at further and deeper changes that need closer analysis and adjustments.
Try this, but if it doesn't work then I'm out of ideas due to not being able to reproduce and analyze:
Go to your user script in Violent Monkey and edit line 163, where you should add a comma and an svg
tag, like so:
viewImageButtonText = viewImageButton.querySelector('.pM4Snf, .KSvtLc, .Pw5kW, .q7UPLe, .K8E1Be, .pFBf7b, span, svg');
Probably won't work, but that's all I got at the moment. I wonder if there's a way to force Google Images to change its visual by changing any particular setting, most times it seems they just push stuff at random to a few users and not others. Bummer.
Probably won't work, but that's all I got at the moment. I wonder if there's a way to force Google Images to change its visual by changing any particular setting, most times it seems they just push stuff at random to a few users and not others. Bummer.
It partially works. Kinda.
After making that change, I get the 3 buttons I was getting before, then to the right of those I get "Search by image" and a view image button that has the same icon as the visit button.
Clicking "Search by image" just opens this page: -
https://www.google.co.uk/imghp?sbi=1
However, clicking the view image button on the right does work as intended and opens the image in a new tab.
So, you're definitely on to something.
The script doesn't work in iOS via the UserScripts safari extension, it says that the URLs don't match. I think that google could be using different set of classes/rules for mobile/iOS.
I guess Google keeps screwing up your awesome program... Weird how the Chrome extensions page actually has it listed, but the 'Add extension' button is disabled. I just now downloaded the zip file with the .js file in it. I have NO bloody idea what to do with it, can I add the folder with the .js file directly into my extensions page? I'm guessing not.
What now...? Is it time to give up : )
Thanks though for all your hard work Bro!
Ah! I guess maybe when Chrome last updated, your extension simply got removed. I just navigated to where I had unpacked your extension the last time and reloaded it! BAM! Works fine now :D
please update to 5.0.2
simply copying from here https://github.com/bijij/ViewImage/blob/master/js/content-script.js does not work on an older browser like a waterfox. and the userscript is the only option. because extensions are now manifest v3
@SigmaTheDJ That's weird, v3.7.0.5 from my gist where I used a dynamic approach to finding the class names should be working.
Regardless, even this version (4.1.1) has the class names that you mentioned declared statically. Do you have the View Image extension installed and enabled? You can't have both running at the same time, either the extension or the user script (this one or mine).
Make sure you don't have multiple scripts running and conflicting with each other because your issue seems to be something else entirely.