eBay Seller Location Fork

Displays the registration location of eBay sellers on search results and product pages.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         eBay Seller Location Fork
// @namespace    http://tampermonkey.net/
// @version      1.2.2
// @description  Displays the registration location of eBay sellers on search results and product pages.
// @icon         https://raw.githubusercontent.com/sonofevil/eBaySellerLocationFork/main/ebaysellerlocationicon64.png
// @author       sonofevil, master131
// @license      MIT
// @match        https://www.ebay.*.*/sch/*
// @match        https://www.ebay.*/sch/*
// @match        https://www.ebay.*.*/itm/*
// @match        https://www.ebay.*/itm/*
// @require      http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// @noframes
// ==/UserScript==

(async function() {
  'use strict';

  //HTTP request headers
  const reqhead = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'X-Requested-With': 'XMLHttpRequest',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0',
    'Cookie': document.cookie,
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site':	'same-origin',
    'Sec-Fetch-User':	'?1',
    'TE':	'trailers',
    'Upgrade-Insecure-Requests': 1
  }

  // Inline styling for counter
  const counterStyle = "position: fixed; bottom: 10px; right: 10px; border-width: 1px; border-color: black; border-style: solid; padding: 5px; background-color: white"

  // DOM element selectors. First thing to debug when the script stops working.
  const selSrchRes = {
    entry: 'ul.srp-results > li.s-item div.s-item__info',
    link: 'a.s-item__link',
    info: 'span.s-item__seller-info,span.s-item__etrs',
    name: 'span.s-item__seller-info-text,span.s-item__etrs-text'
  }
  const selItmPage = {
    loc: '[class$="nameAndAddress"] [class$="content"] [class$="item"]:last-child',
    usrlnk: '.d-stores-info-categories__container__info__section__title a.ux-action[href]',
    info: 'div.ux-seller-section, div.x-sellercard-atf__info',
  }
  const selSlrPage_loc = 'section.str-about-description__seller-info > span:first-of-type > span.str-text-span.BOLD';

  //Inserts location info element into page
  function insertLocInfo(target, location) {
    //var data = JSON.parse(JSON.parse(info.responseText));
    if (target) {
      console.log("Inserting seller location...");
      var loc = document.createElement("div");
      loc.innerText = 'Location: ' + location.replace("HongKong", "Hong Kong");
      target.insertAdjacentElement('afterend', loc);
    }
  }

  // Creates counter container and returns counter span
  function createCounter() {
      var container = document.createElement("div");
      document.querySelector('html > body').insertAdjacentElement('afterend', container);
      const title = document.createTextNode("Seller locations:");
      container.appendChild(title);
      const br = document.createElement("br");
      container.appendChild(br);
      const counter = document.createElement("span");
      container.appendChild(counter);
      container.setAttribute ("style", counterStyle);
      return counter;
  }

  // Return document from URL (.URL attribute is wrong, but the DOM content is right)
  async function getUrlDocument(url) {
    return await fetch(url, { headers: reqhead })
      .then(response => response.text())
      .then(html => {
        var parser  = new DOMParser ();
        var redoc   = parser.parseFromString(html, "text/html");
        return redoc;
    })
      .catch(error => {
      throw new Error("Error: Failure to fetch page.");
    });
  }

  // Return location string for item doc
  async function getDocLoc(itemDoc) {
    if (await itemDoc !== undefined) {
      // Seek location on /itm/ page
      var locElem = await itemDoc.querySelector(selItmPage.loc);
      if (await locElem !== null) {
        console.log("Location found on item page.")
        // Return location string if found on /itm/ page
        return locElem.innerText;
      } else {
        console.log("Location not found on item page, checking seller info page...")
        // Get seller's user link
        var slrLnk = await itemDoc.querySelector(selItmPage.usrlnk);
        if (await slrLnk !== null) {
          console.log("Seller info URL: " + slrLnk.href + '&_tab=about')
          // Get doc from user info URL
          var infoDoc = await getUrlDocument(slrLnk.href + '&_tab=about');
          console.log(infoDoc);
          // Return location string
          if (await infoDoc !== null) {
            return await infoDoc.querySelector(selSlrPage_loc).innerText;
          } else {
            throw new Error("Failure in loading page.");
          }
        } else {
          throw new Error("Couldn't find location.")
        }
      }
    } else {
      throw new Error("Couldn't find location.")
    }
  }

  // Get doc from /itm/ URL
  async function getUrlLoc(itmUrl) {
    var doc = await getUrlDocument(itmUrl);
    return getDocLoc(doc);
  }

  async function searchResults () {
    //Saves all item links to array
    const srcResults = await document.querySelectorAll(selSrchRes.entry);
    const resNum = await srcResults.length;
    const nameList = [];
    const locaList = [];
    var counter = createCounter();
    var errCount = 0;
    counter.innerText = "0/" + resNum  + "(Err: 0)";
    var i = 0;
    for (const [index, result] of srcResults.entries()) {
      if (result) {
        try{
          let name = result.querySelector(selSrchRes.name).innerText.split(" ")[0];
          let nameIndex = nameList.indexOf(name);
          let sellInfo = result.querySelector(selSrchRes.info);
          if ( nameIndex <= 0 ) {
            nameList[i] = name;
            let itemLink = result.querySelector(selSrchRes.link);
            console.log("Checking search result for seller location: " + itemLink.href)
            let itmDoc = await getUrlDocument(itemLink.href);
            locaList[i] = await getDocLoc(itmDoc);
            console.log("Inserting location " + locaList[i])
            await insertLocInfo(sellInfo, locaList[i]);
            i++;
          } else {
            console.log("Item's seller " + name + " already known. Inserting location " + locaList[nameIndex]);
            await insertLocInfo(sellInfo, locaList[nameIndex]);
          }
        } catch(e) {
          console.log(e);
          errCount++;
        }
        counter.innerText = ( index + 1 ) + "/" + resNum + " (Err: " + errCount + ")";
      }
    }
  }

  //Search results:
  if (document.URL.includes("/sch/")) {
    searchResults();
  //Item pages
  } else if (document.URL.includes("/itm/")) {
    //Selects element with username
    var infoSection = document.querySelector(selItmPage.info);
    insertLocInfo(infoSection, await getDocLoc(document));
  }

})();