Google Images View Button

At the Google Images preview pan the script adds a button that opens an image in a new tab

// ==UserScript==
// @name          Google Images View Button
// @description   At the Google Images preview pan the script adds a button that opens an image in a new tab
// @author        Konf
// @namespace     https://gf.qytechs.cn/users/424058
// @icon          https://www.google.com/s2/favicons?sz=64&domain=google.com
// @version       2.0.0
// @match         *://*.google.ad/search*
// @match         *://*.google.ae/search*
// @match         *://*.google.al/search*
// @match         *://*.google.am/search*
// @match         *://*.google.as/search*
// @match         *://*.google.at/search*
// @match         *://*.google.az/search*
// @match         *://*.google.ba/search*
// @match         *://*.google.be/search*
// @match         *://*.google.bf/search*
// @match         *://*.google.bg/search*
// @match         *://*.google.bi/search*
// @match         *://*.google.bj/search*
// @match         *://*.google.bs/search*
// @match         *://*.google.bt/search*
// @match         *://*.google.by/search*
// @match         *://*.google.ca/search*
// @match         *://*.google.cat/search*
// @match         *://*.google.cd/search*
// @match         *://*.google.cf/search*
// @match         *://*.google.cg/search*
// @match         *://*.google.ch/search*
// @match         *://*.google.ci/search*
// @match         *://*.google.cl/search*
// @match         *://*.google.cm/search*
// @match         *://*.google.cn/search*
// @match         *://*.google.co.ao/search*
// @match         *://*.google.co.bw/search*
// @match         *://*.google.co.ck/search*
// @match         *://*.google.co.cr/search*
// @match         *://*.google.co.id/search*
// @match         *://*.google.co.il/search*
// @match         *://*.google.co.in/search*
// @match         *://*.google.co.jp/search*
// @match         *://*.google.co.ke/search*
// @match         *://*.google.co.kr/search*
// @match         *://*.google.co.ls/search*
// @match         *://*.google.co.ma/search*
// @match         *://*.google.co.mz/search*
// @match         *://*.google.co.nz/search*
// @match         *://*.google.co.th/search*
// @match         *://*.google.co.tz/search*
// @match         *://*.google.co.ug/search*
// @match         *://*.google.co.uk/search*
// @match         *://*.google.co.uz/search*
// @match         *://*.google.co.ve/search*
// @match         *://*.google.co.vi/search*
// @match         *://*.google.co.za/search*
// @match         *://*.google.co.zm/search*
// @match         *://*.google.co.zw/search*
// @match         *://*.google.com/search*
// @match         *://*.google.com.af/search*
// @match         *://*.google.com.ag/search*
// @match         *://*.google.com.ai/search*
// @match         *://*.google.com.ar/search*
// @match         *://*.google.com.au/search*
// @match         *://*.google.com.bd/search*
// @match         *://*.google.com.bh/search*
// @match         *://*.google.com.bn/search*
// @match         *://*.google.com.bo/search*
// @match         *://*.google.com.br/search*
// @match         *://*.google.com.bz/search*
// @match         *://*.google.com.co/search*
// @match         *://*.google.com.cu/search*
// @match         *://*.google.com.cy/search*
// @match         *://*.google.com.do/search*
// @match         *://*.google.com.ec/search*
// @match         *://*.google.com.eg/search*
// @match         *://*.google.com.et/search*
// @match         *://*.google.com.fj/search*
// @match         *://*.google.com.gh/search*
// @match         *://*.google.com.gi/search*
// @match         *://*.google.com.gt/search*
// @match         *://*.google.com.hk/search*
// @match         *://*.google.com.jm/search*
// @match         *://*.google.com.kh/search*
// @match         *://*.google.com.kw/search*
// @match         *://*.google.com.lb/search*
// @match         *://*.google.com.ly/search*
// @match         *://*.google.com.mm/search*
// @match         *://*.google.com.mt/search*
// @match         *://*.google.com.mx/search*
// @match         *://*.google.com.my/search*
// @match         *://*.google.com.na/search*
// @match         *://*.google.com.ng/search*
// @match         *://*.google.com.ni/search*
// @match         *://*.google.com.np/search*
// @match         *://*.google.com.om/search*
// @match         *://*.google.com.pa/search*
// @match         *://*.google.com.pe/search*
// @match         *://*.google.com.pg/search*
// @match         *://*.google.com.ph/search*
// @match         *://*.google.com.pk/search*
// @match         *://*.google.com.pr/search*
// @match         *://*.google.com.py/search*
// @match         *://*.google.com.qa/search*
// @match         *://*.google.com.sa/search*
// @match         *://*.google.com.sb/search*
// @match         *://*.google.com.sg/search*
// @match         *://*.google.com.sl/search*
// @match         *://*.google.com.sv/search*
// @match         *://*.google.com.tj/search*
// @match         *://*.google.com.tr/search*
// @match         *://*.google.com.tw/search*
// @match         *://*.google.com.ua/search*
// @match         *://*.google.com.uy/search*
// @match         *://*.google.com.vc/search*
// @match         *://*.google.com.vn/search*
// @match         *://*.google.cv/search*
// @match         *://*.google.cz/search*
// @match         *://*.google.de/search*
// @match         *://*.google.dj/search*
// @match         *://*.google.dk/search*
// @match         *://*.google.dm/search*
// @match         *://*.google.dz/search*
// @match         *://*.google.ee/search*
// @match         *://*.google.es/search*
// @match         *://*.google.fi/search*
// @match         *://*.google.fm/search*
// @match         *://*.google.fr/search*
// @match         *://*.google.ga/search*
// @match         *://*.google.ge/search*
// @match         *://*.google.gg/search*
// @match         *://*.google.gl/search*
// @match         *://*.google.gm/search*
// @match         *://*.google.gr/search*
// @match         *://*.google.gy/search*
// @match         *://*.google.hk/search*
// @match         *://*.google.hn/search*
// @match         *://*.google.hr/search*
// @match         *://*.google.ht/search*
// @match         *://*.google.hu/search*
// @match         *://*.google.ie/search*
// @match         *://*.google.im/search*
// @match         *://*.google.iq/search*
// @match         *://*.google.is/search*
// @match         *://*.google.it/search*
// @match         *://*.google.je/search*
// @match         *://*.google.jo/search*
// @match         *://*.google.jp/search*
// @match         *://*.google.kg/search*
// @match         *://*.google.ki/search*
// @match         *://*.google.kz/search*
// @match         *://*.google.la/search*
// @match         *://*.google.li/search*
// @match         *://*.google.lk/search*
// @match         *://*.google.lt/search*
// @match         *://*.google.lu/search*
// @match         *://*.google.lv/search*
// @match         *://*.google.md/search*
// @match         *://*.google.me/search*
// @match         *://*.google.mg/search*
// @match         *://*.google.mk/search*
// @match         *://*.google.ml/search*
// @match         *://*.google.mn/search*
// @match         *://*.google.ms/search*
// @match         *://*.google.mu/search*
// @match         *://*.google.mv/search*
// @match         *://*.google.mw/search*
// @match         *://*.google.ne/search*
// @match         *://*.google.nl/search*
// @match         *://*.google.no/search*
// @match         *://*.google.nr/search*
// @match         *://*.google.nu/search*
// @match         *://*.google.pl/search*
// @match         *://*.google.pn/search*
// @match         *://*.google.ps/search*
// @match         *://*.google.pt/search*
// @match         *://*.google.ro/search*
// @match         *://*.google.rs/search*
// @match         *://*.google.ru/search*
// @match         *://*.google.rw/search*
// @match         *://*.google.sc/search*
// @match         *://*.google.se/search*
// @match         *://*.google.sh/search*
// @match         *://*.google.si/search*
// @match         *://*.google.sk/search*
// @match         *://*.google.sm/search*
// @match         *://*.google.sn/search*
// @match         *://*.google.so/search*
// @match         *://*.google.sr/search*
// @match         *://*.google.st/search*
// @match         *://*.google.td/search*
// @match         *://*.google.tg/search*
// @match         *://*.google.tl/search*
// @match         *://*.google.tm/search*
// @match         *://*.google.tn/search*
// @match         *://*.google.to/search*
// @match         *://*.google.tt/search*
// @match         *://*.google.vg/search*
// @match         *://*.google.vu/search*
// @match         *://*.google.ws/search*
// @compatible    Chrome
// @compatible    Opera
// @compatible    Firefox
// @run-at        document-body
// @grant         GM_addStyle
// @grant         GM_setValue
// @grant         GM_getValue
// @grant         GM_registerMenuCommand
// @grant         GM_unregisterMenuCommand
// @noframes
// ==/UserScript==

/**
 * Hi! Don't change (or even resave) anything here because
 * doing so in Tampermonkey will turn off the script updates.
 * Not sure about other script managers.
 * This can be restored in settings, but it might be hard to find,
 * so it's better to reinstall the script if you're not sure.
*/

/* jshint esversion: 11 */

(function() {
  'use strict';

  // https://icons8.com/icon/43740/linking
  // https://img.icons8.com/small/96/ffffff/external-link-squared.png
  const viewBtnIconBase64 = [
    'data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4A',
    'AAACXBIWXMAAAsTAAALEwEAmpwYAAAC9klEQVR4nO2dy04UQRSGawUmEFdGQRPjWnGJ+',
    'ARKiM8hlydxA2pivLyFlxF9EtGFibwAhkvEAOYznTkmxEz1MNBTp7rr/9bddbr+r6d6Z',
    'mBOhSCEEEIIIYQQQgghxDkAbgBrwCawBRzQfg5sLtWcVqs5ZndzAFeA58Ax3ecYeAPMh',
    'hwAloBdyuMnsOgd/gpwQrmcAMte4T8sPPx//AEepQ5/ttBlp245upZSwOvopcAvYB2YB',
    '6ZCywGmgHvABnBYM++XKd9qxt7t/ABuh44CzAHbkbkfAddTXMRazZ3f2fD/kxB7JayEc',
    'WMfSAaxHgoBeBrJoJei+LdI8flQCPSfCYPYSlF8P1K89Q/cswJMRzLYC+MmUhivuuMmt',
    'xwkwJCARIQIox7fGF6FcSK3HCTAkIBEhAijHt8YboUbApgA3ktAC8KvqBlrICkm4VPYI',
    'XwJcA5fAhoAmAQ+Dsk59gWjlqAEd/5n4JIE+IT/qQrfjtcroOHw3501fDtHArzCt/Mkw',
    'Ct8O1cCvMK38yXAK3wbQwK8wrdxJMArfBtLArzCt/EkwCt8G1MCvMK3cSXAK3wbWwK8w',
    'rfxJeCC4W+eN3yrIQE14b8dZ/hWRwK8wrdaEuAVvtWTAK/wraYEjBj+ZHBkVGGtKQy8G',
    'hJ+9WfGiabqXeA6OyvgJvA91zu/8wJqJGQTfucFDJCQVfhFCKgAbtkzwX3NL1JAzkiAM',
    'xLgjAQ4IwHOSIAzEuCMBDgjAc5IgDMS4IwEFCxgL1J7OhQCcDmSwW6K4l8jxUtqWbbg2',
    'bIs9pvbjVAIwLNIBh8821ZWrRzvhI4D3PVuW1nXuLVqajoXuh3+dk3j1jQt7Yf858Kh9',
    'dVc6MKDmX6HxPu27PyumfeLlBc1Yw2rRZ8d4GoyASbhQSE7ZgyjauG/lDT8UxKWC99D4',
    'AR47BL+KQmLhS5HO9UmFiGjTXyeDHlIdYUj28RnJuRG1T/f9pXpAV9q+ky3iX2bS8/ml',
    'sfuSUIIIYQQQgghhBAitI2/ZYk4Uk/wyKQAAAAASUVORK5CYII='
  ].join('');

  let ignoreThumbnails = GM_getValue('ignoreThumbnails', true);
  let menuId = null;

  (function updateMenu() {
    if (menuId) GM_unregisterMenuCommand(menuId);

    menuId = GM_registerMenuCommand(`Ignore thumbnails: ${ignoreThumbnails}`, () => {
      ignoreThumbnails = !ignoreThumbnails;

      GM_setValue('ignoreThumbnails', ignoreThumbnails);
      updateMenu();
    });
  }());

  // Skip if it is not an image search section
  if ((new URLSearchParams(window.location.search)).get('udm') !== '2') return;

  waitForElement('div[data-viewer-id] a > img', {
    existing: true,
  }, (image) => {
    // Recursion skip
    if (image.matches('.GIVB-icon')) return;

    // Remove existing view button, if present
    image.parentElement.querySelector('a.GIVB-btn')?.remove();

    // Might be not reliable enough, but it's the best I've found
    const imageIsThumbnail = [
      ...image.parentElement.children
    ].filter(n => n.nodeName === 'IMG').length === 1;

    if (imageIsThumbnail && ignoreThumbnails) return;

    const viewBtn = document.createElement('a');
    const viewBtnIcon = document.createElement('img');

    viewBtn.addEventListener('click', (ev) => {
      ev.preventDefault();
      window.open(viewBtn.href, '_blank');
    });

    viewBtn.href = image.src;
    viewBtn.title = 'Open in a new tab';
    viewBtn.className = 'GIVB-btn';
    viewBtnIcon.className = 'GIVB-icon';
    viewBtnIcon.draggable = false;
    viewBtnIcon.src = viewBtnIconBase64;

    viewBtn.append(viewBtnIcon);
    image.parentElement.append(viewBtn);
  });

  GM_addStyle([`
    .GIVB-btn {
      position: absolute;
      top: 16px;
      right: 16px;
      height: 36px;
      width: 36px;
      background-color: #0009;
      border-radius: 50%;
    }

    .GIVB-btn:hover {
      background-color: #000c;
    }

    .GIVB-icon {
      position: absolute;
      top: 6px;
      right: 6px;
      height: 24px;
      width: 24px;
    }
  `][0]);


  // utils > -----------------------------------------------------------------------

  function waitForElement(query, {
    callbackOnTimeout = false,
    existing = false,
    onceOnly = false,
    rootElement = document.documentElement,
    timeout,

    // "attributes" prop is not supported
    observerOptions = {
      childList: true,
      subtree: true,
    },
  }, callback) {
    if (!query) throw new Error('Query is needed');
    if (!callback) throw new Error('Callback is needed');

    observerOptions = Object.assign({}, observerOptions);

    const handledElements = new WeakSet();
    const existingElements = rootElement.querySelectorAll(query);
    let timeoutId = null;

    if (existingElements.length) {
      // Mark all as handled for a proper work when `existing` is false
      // to ignore them later on
      for (const node of existingElements) {
        handledElements.add(node);
      }

      if (existing) {
        if (onceOnly) {
          try {
            callback(existingElements[0]);
          } catch (e) {
            console.error(e);
          }

          return;
        } else {
          for (const node of existingElements) {
            try {
              callback(node);
            } catch (e) {
              console.error(e);
            }
          }
        }
      }
    }

    const observer = new MutationObserver((mutations, observer) => {
      for (const node of rootElement.querySelectorAll(query)) {
        if (handledElements.has(node)) continue;

        handledElements.add(node);

        try {
          callback(node);
        } catch (e) {
          console.error(e);
        }

        if (onceOnly) {
          observer.disconnect();

          if (timeoutId) clearTimeout(timeoutId);

          return;
        }
      }
    });

    observer.observe(rootElement, {
      attributes: false,
      childList: observerOptions.childList || false,
      subtree: observerOptions.subtree || false,
    });

    if (timeout !== undefined) {
      timeoutId = setTimeout(() => {
        observer.disconnect();

        if (callbackOnTimeout) {
          try {
            callback(null);
          } catch (e) {
            console.error(e);
          }
        }
      }, timeout);
    }

    return observer;
  }

  // < utils -----------------------------------------------------------------------
}());

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址