Skip to content

Instantly share code, notes, and snippets.

@saviorxzero98
Forked from bijij/viewimage.user.js
Last active June 7, 2023 12:50
Show Gist options
  • Select an option

  • Save saviorxzero98/88662a751b48cfeb90f22a61ca4567eb to your computer and use it in GitHub Desktop.

Select an option

Save saviorxzero98/88662a751b48cfeb90f22a61ca4567eb to your computer and use it in GitHub Desktop.
Userscript version of the View Image chrome extension
// ==UserScript==
// @name View Image
// @namespace https://github.com/bijij/ViewImage
// @version 3.7.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.github.com/saviorxzero98/88662a751b48cfeb90f22a61ca4567eb/raw/viewimage.user.js
// ==/UserScript==
'use strict';
// i18n
const i18nTextMap = {
'en': {
searchByImage: 'Search by image',
viewImage: 'View Image'
},
'zh-hans': {
searchByImage: '以图搜图',
viewImage: '查看图片'
},
'zh-hant': {
searchByImage: '以圖搜尋',
viewImage: '檢視圖片'
}
}
function geti18nText() {
// Get navigator language
let lang = navigator.language || navigator.userLanguage || 'en';
switch (lang.toLowerCase()) {
case 'zh-hans':
case 'zh-cn':
case 'zh-sg':
case 'zh-my':
return i18nTextMap["zh-hans"];
case 'zh-hant':
case 'zh-tw':
case 'zh-hk':
case 'zh-mo':
return i18nTextMap["zh-hant"];
default:
return i18nTextMap["en"];
}
}
var i18nText = geti18nText();
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');
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');
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, .KSvtLc, .Pw5kW, .q7UPLe, .K8E1Be, .pFBf7b, span');
break;
}
viewImageButtonText.innerText = i18nText.viewImage;
// 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');
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 = i18nText.searchByImage;
// Set the search by image button url
//searchImageButton.href = '/searchbyimage?image_url=' + imageURL;
// Set additional options
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) {
// Find the container
var [container, version] = getContainer(node);
// Return if no container was found
if (!container) {
return;
}
// Clear any old extension elements
clearExtElements(container);
// Find the image url
var imageURL = findImageURL(container, version);
// Return if image was not found
if (!imageURL) {
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) {
}
}
}
function parseDataSource1() {
const start_search = /AF_initDataCallback\({key:\s'ds:1',\sisError:\s{2}false\s,\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) {
parseDataSource1();
} else if (document.documentElement.innerHTML.indexOf('key: \'ds:2\'') != -1) {
parseDataSource2();
} else {
throw 'Could not determine data source type.';
}
}
catch (error) {
}
// Define the mutation observers
var observer = new MutationObserver(function (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);
}
}
}
});
// Get options and start adding links
observer.observe(document.body,
{
childList: true,
subtree: true,
attributes: 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,
.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);
@yourduskquibbles

Copy link
Copy Markdown

FYI for anyone else having trouble getting this to work on firefox with Violentmonkey.

You need to set Default injection mode: to auto under Violentmonkey Settings -> Advanced -> General and it should work again as google now sets a CSP script execution policy that blocks it from loading on the default Violentmonkey page injection mode.

@Mactastic1-5

Mactastic1-5 commented Jun 3, 2023

Copy link
Copy Markdown

It's no longer working in Google Chrome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment