Price History - Amazon UK

A non-intrusive way to display Amazon UK item's history price, featuring third-party price history.

Before you install, Greasy Fork would like you to know that this script contains antifeatures, which are things there for the script author's benefit, rather than yours.

This script contains code that will track your browsing. The author of this script explains: Third-party site can collect/store/guess what you are going to purchase.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name               Price History - Amazon UK
// @description        A non-intrusive way to display Amazon UK item's history price, featuring third-party price history.
// @version            1.0.3

// @name:zh-CN         价格历史 - 亚马逊英国
// @description:zh-CN  以非侵入式的方式来展示亚马逊英国商品的历史价格,使用第三方价格服务。

// @antifeature        tracking Third-party site can collect/store/guess what you are going to purchase.
// @antifeature:zh-CN  tracking 第三方网站可能会搜集、储存或推测你将要购买的物品。

// @namespace          uk.jixun
// @match              https://amazon.co.uk/*
// @match              https://www.amazon.co.uk/*
// @match              https://smile.amazon.co.uk/*
// @author             Jixun
// @license            bsd-3-clause
// @run-at             document-start
// @grant              none
// ==/UserScript==

let serviceEl = [];
const services = [{
  name: 'CCC',
  inline: false,
  url: asin => `https://uk.camelcamelcamel.com/product/${asin}`,
}, {
  name: 'Keepa',
  inline: asin => () => {
    const prevImage = $('keepa-history-embed');
    if (prevImage) {
      prevImage.style.display = prevImage.style.display === 'none' ? 'block' : 'none';
      return;
    }
    
    const src = `https://graph.keepa.com/pricehistory.png?asin=${asin}&domain=co.uk`;
    const $img = h('img', {src, id: 'keepa-history-embed', 'referrerPolicy': 'no-referrer'});
    const $price = $('price');
    if ($price) {
      $price.parentNode.insertBefore($img, $price.nextSibling);      
    } else {
      $('olp_feature_div').appendChild($img);
    }
    serviceEl.push($img);
  },
  url: asin => `https://keepa.com/#!product/2-${asin}`,
}];

const serviceStyle = {
  'marginRight': '0.5em',
  'display': 'inline-block',
};

const inlineStyle = {
  background: 'none',
  border: 'none',
};

function insertServices(asin) {
  return services.map(service => h('li', { style: serviceStyle }, [
    service.url && h('a', { href: service.url(asin), target: '_blank', rel: 'noreferrer' }, [service.name]),
    !service.url && h('span', null, [service.name]),
    service.inline && h('span', null, [
      ' (',
      h('a', { href: 'javascript:void(0)', onclick: service.inline(asin) }, 'inline'),
      ')',
    ])
  ]));
}

function $(id) {
  return document.getElementById(id);
}

function insertChildren(root, children = []) {
  if (!children) return;
  if (Array.isArray(children)) {
    for (const c of children) {
      insertChildren(root, c);
    }
    return;
  }
  if (typeof children === 'string') {
    children = document.createTextNode(children);
  }
  root.appendChild(children);
}

function h(tag, attr, ...children) {
  const el = document.createElement(tag);
  if (attr) {
    for(const [name, value] of Object.entries(attr)) {
      if (name === 'style') {
        Object.assign(el.style, value);
      } else {
        el[name] = value;
      }
    }
  }
  insertChildren(el, children);
  return el;
}

function remove(node) {
  node?.parentNode?.removeChild(node);
}

let lastRoot;
function injectPriceCompare() {
  const $asin = $('ASIN');
  if (!$asin || $asin.tagName !== 'INPUT') {
    console.info('asin not found.');
    return;
  }

  // clean up
  serviceEl.forEach(remove);

  const asin = $asin.value;
  const $priceTable = document.querySelector('#price table > tbody') || $('olp_feature_div');
  const root = h('tr', null, [
    h('td', { className: 'a-color-secondary' }, ['History']),
    h('td', null, [
      h('ul', { style: {cssText: 'margin:0'} }, insertServices(asin)),
    ])
  ]);
  $priceTable.appendChild(root);
  serviceEl = [root];
}

function init() {
  const root = $('desktop_buybox');
  if (!root) {
    return;
  }

  const observer = new MutationObserver((mutationsList, observer) => {
    for(const {removedNodes} of mutationsList) {
      console.info(removedNodes);
      if (!removedNodes) continue;
      
      for (const node of removedNodes) {
        if (node.id === 'buybox') {
          injectPriceCompare();
          return;
        }
      }
    }
  });

  // Start observing the target node for configured mutations
  observer.observe(root, { childList: true });
  injectPriceCompare();
}

addEventListener('DOMContentLoaded', init);