Google Search "View Image" Button

This userscript adds "View Image" button to Google Image Search results.

// ==UserScript==
// @name            Google Search "View Image" Button
// @name:ru         Google Search кнопка "Показать в полном размере"
// @name:sl         Gumb "Ogled slike" na Google Slikah
// @name:uk         Google Search кнопка "Показати зображення"
// @name:lt         Google paieškos mygtukas "Rodyti vaizdą"
// @name:pl         Przycisk "Pokaż obraz" w wyszukiwarce obrazów Google
// @name:ja         Google検索「画像を表示」ボタン
// @name:nl         Google zoeken "Afbeelding bekijken" knop
// @namespace       https://github.com/devunt/make-gis-great-again
// @icon            https://raw.githubusercontent.com/devunt/make-gis-great-again/master/icons/icon.png
// @version         1.4.1
// @description     This userscript adds "View Image" button to Google Image Search results.
// @description:ru  Этот скрипт добавляет кнопку "Показать в полном размере" к результатам Google Image Search.
// @description:sl  Ponovno prikaže gumb "Ogled slike" na Google Slikah.
// @description:uk  Цей скрипт додає кнопку "Показати зображення" до результатів Google Image Search
// @description:lt  Šis vartotojo skriptas prideda mygtuką "Rodyti vaizdą" į Google vaizdo paieškos rezultatus.
// @description:pl  Ten skrypt przywraca przycisk "Pokaż obraz" do wyszukiwarki obrazów Google
// @description:ja  このUserScriptはGoogle検索結果に「画像を表示」ボタンを追加します。
// @description:nl  Voegt de "Afbeelding bekijken" knop aan toe aan Google Afbeeldingen.
// @author          Bae Junehyeon
// @run-at          document-end
// @include         http*://*.google.tld/search*tbm=isch*
// ==/UserScript==

const lang = {
  en: 'View image',
  ru: 'Показать в полном размере',
  'zh-CN': '查看原图',
  ja: '画像を表示',
  he: 'הצג תמונה',
  fr: 'Voir l\'image',
  sl: 'Ogled slike',
  ar: 'عرض الصورة',
  de: 'Bild ansehen',
  tr: 'Resmi görüntüle',
  pt: 'Ver imagem',
  lt: 'Rodyti vaizdą',
  pl: 'Pokaż obraz',
  nl: 'Afbeelding bekijken',
  se: 'Visa bild',
  uk: 'Показати зображення',
  it: 'Apri immagine'
};

const localizedViewImage = lang[(lang[navigator.language] ? navigator.language : 'en')];

function addButton(node) {
  if (node.nodeType === Node.ELEMENT_NODE) {
    if (node.classList.contains('irc_ris')) {
      let container = node.closest('.irc_c');

      let similarImages = node.querySelectorAll('.rg_l');
      [].forEach.call(similarImages, (image) => {
        image.addEventListener('click', updateLinkAfterClickOnSimilar);
      });

      let thumbnail = node.querySelector('.irc_rimask.irc_rist');
      let src = unescape(thumbnail.querySelector('.rg_l').href.match(/imgurl=([^&]+)/)[1]);

      let buttons = container.querySelector('.irc_but_r tr');

      let button = buttons.querySelector('td.mgisga');
      if (button === null) {
        let openButton = buttons.querySelector('td');

        button = openButton.cloneNode(true);
        button.classList.add('mgisga');
        button.querySelector('a span:nth-child(2)').innerText = localizedViewImage;

        let link = button.querySelector('a');
        link.href = src;
        link.className = '';
        link.removeAttribute('data-cthref');
        link.removeAttribute('jsaction');

        openButton.after(button);
      }

      let link = button.querySelector('a');
      link.href = src;
    }
  }
}

function updateLinkAfterClickOnSimilar({target:node}) {
  let src = unescape(node.closest('.rg_l').href.match(/imgurl=([^&]+)/)[1]);
  let container = node.closest('.irc_c');
  let button = container.querySelector('.mgisga');
  let link = button.querySelector('a');
  link.href = src;
}

function addLinks(node) {
    if (!node.closest) return;
    var object = node.closest('.irc_c');
    if (object === null) return;

    const local = {
      en: 'Search by Image',
      ru: 'Поиск по картинке',
      'zh-CN': '以图搜图',
      'zh-TW': '以圖搜圖',
      ja: '画像を検索',
      he: 'חפש לפי תמונה',
      fr: 'Recherche par image',
      sl: 'Iskanje s sliko',
      ar: 'البحث عن الصورة',
      de: 'Zur "Bildersuche"',
      tr: 'Görselle Ara',
      pt: 'Pesquisar por imagem',
      lt: 'Ieškoti paveikslėlio',
      pl: 'Wyszukiwanie obrazem',
      nl: 'Afbeelding zoeken',
      se: 'Fler storlekar',
      uk: 'Шукати зображення',
      it: 'Ricerca tramite immagine'
    };

    var searchByImageMessage = local[(local[navigator.language] ? navigator.language : 'en')];
    var imageText = object.querySelector('._cjj > .irc_it > .irc_hd > ._r3') ||
                    object.querySelector('.Qc8zh > .irc_it > .irc_hd > .rn92ee');

    // Create more sizes button
    var moreSizes = document.createElement('a');
    moreSizes.setAttribute('href', '#'); // TODO: Figure out how to generate a more sizes url
    moreSizes.setAttribute('class', 'ext_addon _ZR irc_hol irc_lth _r3');
    moreSizes.setAttribute('style', 'pointer-events:none'); // Disable click for now

    // Insert text into more sizes button
    var moreSizesText = document.createElement('span');
    moreSizesText.innerHTML = object.querySelector('.irc_idim').innerHTML;
    moreSizes.appendChild(moreSizesText);

    // Create Search by image button
    var searchByImage = document.createElement('a');
    var thumbnail = node.querySelector('.irc_rimask.irc_rist');
    if (!thumbnail) return;
    var src = unescape(thumbnail.querySelector('.rg_l').href.match(/imgurl=([^&]+)/)[1]);
    searchByImage.setAttribute('href', '/searchbyimage?image_url=' + src);
    searchByImage.setAttribute('target', '_blank'); // COMMENT THIS TO NOT OPEN IN NEW TAB
    searchByImage.setAttribute('class', 'ext_addon _ZR irc_hol irc_lth _r3');

    // Insert text into Search by image button
    var searchByImageText = document.createElement('span');
    searchByImageText.textContent = searchByImageMessage;
    searchByImage.appendChild(searchByImageText);

    // Append More sizes & Search by image buttons
    imageText.append('\xa0\xa0\xa0 ');
    imageText.appendChild(moreSizes);
    imageText.append('\xa0\xa0\xa0 ');
    imageText.appendChild(searchByImage);
}

const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    const addedNodes = mutation.addedNodes || [];

    [].forEach.call(addedNodes, (newNode) => {
        addButton(newNode);
        addLinks(newNode);
    });
  });
});

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

addButton(document.body);
addLinks(document.body);