Amazon - Show Seller Info

Shows name, country of origin and ratings for third party sellers on Amazon (and highlights Chinese sellers)

As of 2020-04-26. See the latest version.

// ==UserScript==
// @namespace       https://openuserjs.org/users/Taddiboy
// @name            Amazon - Show Seller Info
// @name:de         Amazon - Verkäuferinformationen anzeigen
// @name:fr         Amazon - Afficher les informations sur le vendeur
// @name:es         Amazon - Mostrar información del vendedor
// @name:it         Amazon - Mostra info venditore
// @description     Shows name, country of origin and ratings for third party sellers on Amazon (and highlights Chinese sellers)
// @description:de  Zeigt Name, Herkunftsland und Bewertungen von Drittanbietern auf Amazon (und hebt chinesische Anbieter hervor)
// @description:fr  Montre le nom, le pays d'origine et les évaluations des vendeurs tiers sur Amazon (et met en évidence les vendeurs chinois)
// @description:es  Muestra el nombre, el país de origen y las valoraciones de los vendedores de terceros en el Amazon (y destaca los vendedores chinos)
// @description:it  Mostra il nome, il paese di origine e le valutazioni per i venditori di terze parti su Amazon (e mette in evidenza i venditori cinesi)
// @copyright       2020, Taddiboy (https://openuserjs.org/users/Taddiboy)
// @license         MIT
// @author          Taddiboy
// @version         1.0.1
// @icon            https://i.imgur.com/u4v223v.png
// @include         https://smile.amazon.co.uk/*
// @include         https://www.amazon.co.uk/*
// @include         https://smile.amazon.de/*
// @include         https://www.amazon.de/*
// @include         https://www.amazon.es/*
// @include         https://www.amazon.fr/*
// @include         https://www.amazon.it/*
// @grant           GM_addStyle
// @grant           GM_xmlhttpRequest
// ==/UserScript==
(function() {
    'use strict';
  
    // CONFIG BEGIN
  
    const highlightedCountries = ['CN', 'HK'];
    // Country codes as per ISO 3166 ALPHA-2
    // Set to [] to highlight no sellers at all
    // Set to ['FR'] to highlight sellers from France
    // Supported country codes: https://www.countryflags.io/#countries
    // Default: ['CN', 'HK']
  
    const redirectToSmile = false;
    // Available for amazon.de and amazon.co.uk
    // Set to true to redirect to Amazon Smile
    // Set to false for no redirect
    // Default: false
  
    const showConsoleLog = false;
    // Show errors in console log
    // Default: false
  
    // CONFIG END
  
    if (redirectToSmile && top.location.href.match(/^https:\/\/smile\.amazon\.(de|co\.uk)\//i) === null) {
      window.location.replace(window.location.href.replace(/^https:\/\/www\./i, "https://smile."));
    }
  
    // Check URLs for page type (search result page and best sellers page)
    const isSearchResultPage = window.location.href.match(/.*\.amazon\..*\/s\?.*/);
    const isBestsellersPage = window.location.href.match(/.*\.amazon\..*\/gp\/bestsellers\/.*/) || window.location.href.match(/.*\.amazon\..*\/Best\-Sellers\-.*/);
  
    if (isSearchResultPage || isBestsellersPage) {
      function showSellerCountry() {
        'use strict';
  
        const products = isSearchResultPage ?
          document.querySelectorAll('h2.a-size-mini.a-spacing-none.a-color-base a.a-link-normal.a-text-normal:not([data-seller])') :
          document.querySelectorAll('span.aok-inline-block.zg-item>a.a-link-normal:not([data-seller])');
  
        for (let i = 0; i < products.length; i++) {
          const product = products[i];
          product.setAttribute("data-seller", "set");
  
          if (product.href && product.href.match(/.*\.amazon\..*\/(.*\/dp|gp\/slredirect)\/.*/)) {
            GM_xmlhttpRequest({
              method: "GET",
              url: product.href,
              responseType: "document",
              onload: function(responseProduct) {
                const productPage = parse(responseProduct);
                const thirdPartySeller = productPage.querySelector('#qualifiedBuybox #sellerProfileTriggerId, #newAccordionRow #sellerProfileTriggerId, #shipsFromSoldBy_feature_div #sellerProfileTriggerId');
                const isThirdPartySeller = thirdPartySeller !== null;
  
                if (isThirdPartySeller) {
                  thirdPartySeller.textContent = thirdPartySeller.textContent.trim();
                  thirdPartySeller.href = thirdPartySeller.href.replace(/gp\/help\/seller.*/g, 'sp?' + thirdPartySeller.href.match(/(seller=.*?)(&|$)/)[1]);
                  const sellerInfoLink = document.createElement("a");
                  sellerInfoLink.href = thirdPartySeller.href;
                  const sellerInfoContent = document.createTextNode(thirdPartySeller.textContent);
                  sellerInfoLink.appendChild(sellerInfoContent);
                  sellerInfoLink.classList.add("seller-info");
  
                  isSearchResultPage ?
                    product.parentNode.parentNode.appendChild(sellerInfoLink) :
                    product.parentNode.insertBefore(sellerInfoLink, product.parentNode.querySelector('.a-icon-row.a-spacing-none'));
  
                  GM_xmlhttpRequest({
                    method: "GET",
                    url: thirdPartySeller.href,
                    responseType: "document",
                    onload: function(responseSeller) {
                      const sellerPage = parse(responseSeller);
  
                      // Get seller rating
                      let rating = sellerPage.getElementById('seller-feedback-summary');
                      if (rating !== null) {
                        let ratingPercentage = '';
                        let ratingCount = '';
                        if (sellerPage.getElementById('feedback-no-rating')) {
                          ratingPercentage = sellerPage.getElementById("feedback-no-rating").textContent;
                        } else {
                          ratingPercentage = sellerPage.getElementsByClassName('feedback-detail-description')[0].textContent.match(/\d+%/);
                        }
                        if (rating.contains(sellerPage.getElementById('feedback-no-review'))) {
                          ratingCount = sellerPage.getElementById("feedback-no-review").textContent;
                        } else {
                          ratingCount = sellerPage.getElementsByClassName('feedback-detail-description')[0].textContent.match(/\(([^)]+)\)/)[1];
                        }
                        const sellerInfoRatingText = ' (' + ratingPercentage + ' | ' + ratingCount + ')';
                        const sellerInfoRating = document.createTextNode(sellerInfoRatingText);
                        sellerInfoLink.appendChild(sellerInfoRating);
                      } else {
                        if (showConsoleLog) {
                          console.log("Too many requests. Cannot  open seller pages  anymore :-\(")
                        }
                      }
  
                      // Get seller country & flag
                      const sellerUl = sellerPage.querySelectorAll("ul.a-unordered-list.a-nostyle.a-vertical"); //get all ul
                      if (sellerUl !== 'undefined' || 0 !== sellerUl.length) {
                        const sellerUlLast = sellerUl[sellerUl.length - 1]; //get last list
                        const sellerLi = sellerUlLast.querySelectorAll("li"); //get all li
                        const sellerLiLast = sellerLi[sellerLi.length - 1]; //get last li
                        const sellerCountry = sellerLiLast.textContent;
  
                        if (sellerCountry.length == 2) {
                          var flag = document.createElement("img");
                          flag.setAttribute("src", "https://www.countryflags.io/" + sellerCountry.toLowerCase() + "/flat/32.png");
                          flag.setAttribute("width", "16");
                          flag.setAttribute("height", "16");
                          flag.setAttribute("style", "margin-right: 5px;");
                          flag.title = sellerCountry;
                          sellerInfoLink.prepend(flag);
  
                          // Highlight sellers from countries defined in 'highlightedCountries'
                          if (highlightedCountries.includes(sellerCountry)) {
                            const outercontainer = isSearchResultPage ?
                              product.closest('.a-carousel-card, .s-result-item') :
                              product.closest('.zg-item-immersion');
  
                            const productImage = isSearchResultPage ?
                              outercontainer.querySelector('.s-image') :
                              outercontainer.querySelector('.zg-text-center-align img');
  
                            outercontainer.style.background = "linear-gradient(180deg, rgba(222,41,14,0.33) 0%, rgba(222,41,14,0) 100%)";
                            productImage.style.opacity = "0.66";
                          }
                        } else {
                          if (showConsoleLog) {
                            console.log("Seller \"" + thirdPartySeller.textContent + "\" has no valid imprint.")
                          }
                        }
                      } else {
                        if (showConsoleLog) {
                          console.log("Something went wrong while getting seller info of \"" + thirdPartySeller.textContent + "\".")
                        }
                      }
                    }
                  });
                } else {
                  const sellerInfoDiv = document.createElement("div");
                  let soldbyAmazon = productPage.querySelector('#merchant-info').textContent.trim();
                  if (!soldbyAmazon.replace(/\s/g, '').length) {
                    soldbyAmazon = '? ? ?';
                  } else {
                    const svg = document.createElement("span");
                    svg.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="12" height="12" style="vertical-align: bottom;margin-right: 4px;"><path fill="#f90" d="M110.2 103.3c-51.8 24.7-84 4-104.6-8.5-1.2-.8-3.4.2-1.5 2.4 6.8 8.3 29.3 28.3 58.6 28.3 29.4 0 46.9-16 49-18.8 2.2-2.7.7-4.3-1.5-3.4zm14.5-8c-1.3-1.8-8.4-2.1-12.9-1.6-4.4.5-11.1 3.3-10.5 4.9.3.6.9.3 4 0 3-.3 11.6-1.3 13.4 1 1.8 2.4-2.7 13.6-3.6 15.4-.8 1.8.3 2.3 1.8 1 1.5-1.1 4.2-4.3 6-8.7 1.8-4.4 2.9-10.6 1.8-12zm0 0"></path><path fill-rule="evenodd" d="M75.4 53c0 6.5.1 11.9-3.2 17.6-2.6 4.7-6.8 7.6-11.4 7.6-6.4 0-10.1-4.9-10.1-12 0-14.2 12.6-16.8 24.7-16.8zM92 93.5c-1.1 1-2.7 1-4 .4-5.4-4.6-6.4-6.7-9.5-11-9 9.2-15.5 12-27.3 12-14 0-24.9-8.6-24.9-25.9 0-13.5 7.3-22.6 17.7-27.1 9-4 21.6-4.7 31.3-5.8V34c0-4 .3-8.7-2-12-2-3.1-6-4.4-9.4-4.4-6.3 0-12 3.3-13.3 10-.3 1.5-1.4 3-3 3l-16-1.7c-1.4-.3-2.9-1.4-2.5-3.5C32.9 6 50.5 0 66.3 0c8.1 0 18.7 2.1 25 8.3 8.1 7.5 7.4 17.6 7.4 28.5v26c0 7.7 3.2 11.1 6.2 15.3 1.1 1.5 1.3 3.3 0 4.4L92 93.5"></path></svg>';
                    sellerInfoDiv.appendChild(svg);
                  }
                  const sellerInfoContent = document.createTextNode(soldbyAmazon);
                  sellerInfoDiv.appendChild(sellerInfoContent);
                  sellerInfoDiv.classList.add("seller-info");
  
                  isSearchResultPage ?
                    product.parentNode.parentNode.appendChild(sellerInfoDiv) :
                    product.parentNode.insertBefore(sellerInfoDiv, product.parentNode.querySelector('.a-icon-row.a-spacing-none'));
                }
              }
            });
          }
        }
      }
  
      // Run script once on document ready
      showSellerCountry();
  
      // Initialize new MutationObserver
      const mutationObserver = new MutationObserver(showSellerCountry);
  
      // Let MutationObserver target the grid containing all thumbnails
      const targetNode = document.body;
  
      const mutationObserverOptions = {
        childList: true,
        subtree: true
      }
  
      // Run MutationObserver
      mutationObserver.observe(targetNode, mutationObserverOptions);
  
      function parse(targetPage) {
        const parser = new DOMParser();
        return parser.parseFromString(targetPage.responseText, "text/html");
      }
  
      // Add Google's own CSS used for image dimensions
      GM_addStyle(`
        .seller-info {
          display: inline-block;
          background: #fff;
          color: #1d1d1d !important;
          font-size: 11px;
          line-height: 15px;
          padding: 2px 5px;
          font-weight: 400;
          border: 1px solid #E0E0E0;
          margin-top: 4px;
          height: 22px;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
          max-width: 100%;
        }
        a.seller-info:hover {
          border-color: #D0D0D0;
          text-decoration: none;
          background-color: #F3F3F3;
        }
        #zg-center-div .zg-item-immersion {
          height: 390px;
        }
      `);
    }
  })();