Price History - Amazon UK

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

  1. // ==UserScript==
  2. // @name Price History - Amazon UK
  3. // @description A non-intrusive way to display Amazon UK item's history price, featuring third-party price history.
  4. // @version 1.0.3
  5.  
  6. // @name:zh-CN 价格历史 - 亚马逊英国
  7. // @description:zh-CN 以非侵入式的方式来展示亚马逊英国商品的历史价格,使用第三方价格服务。
  8.  
  9. // @antifeature tracking Third-party site can collect/store/guess what you are going to purchase.
  10. // @antifeature:zh-CN tracking 第三方网站可能会搜集、储存或推测你将要购买的物品。
  11.  
  12. // @namespace uk.jixun
  13. // @match https://amazon.co.uk/*
  14. // @match https://www.amazon.co.uk/*
  15. // @match https://smile.amazon.co.uk/*
  16. // @author Jixun
  17. // @license bsd-3-clause
  18. // @run-at document-start
  19. // @grant none
  20. // ==/UserScript==
  21.  
  22. let serviceEl = [];
  23. const services = [{
  24. name: 'CCC',
  25. inline: false,
  26. url: asin => `https://uk.camelcamelcamel.com/product/${asin}`,
  27. }, {
  28. name: 'Keepa',
  29. inline: asin => () => {
  30. const prevImage = $('keepa-history-embed');
  31. if (prevImage) {
  32. prevImage.style.display = prevImage.style.display === 'none' ? 'block' : 'none';
  33. return;
  34. }
  35. const src = `https://graph.keepa.com/pricehistory.png?asin=${asin}&domain=co.uk`;
  36. const $img = h('img', {src, id: 'keepa-history-embed', 'referrerPolicy': 'no-referrer'});
  37. const $price = $('price');
  38. if ($price) {
  39. $price.parentNode.insertBefore($img, $price.nextSibling);
  40. } else {
  41. $('olp_feature_div').appendChild($img);
  42. }
  43. serviceEl.push($img);
  44. },
  45. url: asin => `https://keepa.com/#!product/2-${asin}`,
  46. }];
  47.  
  48. const serviceStyle = {
  49. 'marginRight': '0.5em',
  50. 'display': 'inline-block',
  51. };
  52.  
  53. const inlineStyle = {
  54. background: 'none',
  55. border: 'none',
  56. };
  57.  
  58. function insertServices(asin) {
  59. return services.map(service => h('li', { style: serviceStyle }, [
  60. service.url && h('a', { href: service.url(asin), target: '_blank', rel: 'noreferrer' }, [service.name]),
  61. !service.url && h('span', null, [service.name]),
  62. service.inline && h('span', null, [
  63. ' (',
  64. h('a', { href: 'javascript:void(0)', onclick: service.inline(asin) }, 'inline'),
  65. ')',
  66. ])
  67. ]));
  68. }
  69.  
  70. function $(id) {
  71. return document.getElementById(id);
  72. }
  73.  
  74. function insertChildren(root, children = []) {
  75. if (!children) return;
  76. if (Array.isArray(children)) {
  77. for (const c of children) {
  78. insertChildren(root, c);
  79. }
  80. return;
  81. }
  82. if (typeof children === 'string') {
  83. children = document.createTextNode(children);
  84. }
  85. root.appendChild(children);
  86. }
  87.  
  88. function h(tag, attr, ...children) {
  89. const el = document.createElement(tag);
  90. if (attr) {
  91. for(const [name, value] of Object.entries(attr)) {
  92. if (name === 'style') {
  93. Object.assign(el.style, value);
  94. } else {
  95. el[name] = value;
  96. }
  97. }
  98. }
  99. insertChildren(el, children);
  100. return el;
  101. }
  102.  
  103. function remove(node) {
  104. node?.parentNode?.removeChild(node);
  105. }
  106.  
  107. let lastRoot;
  108. function injectPriceCompare() {
  109. const $asin = $('ASIN');
  110. if (!$asin || $asin.tagName !== 'INPUT') {
  111. console.info('asin not found.');
  112. return;
  113. }
  114.  
  115. // clean up
  116. serviceEl.forEach(remove);
  117.  
  118. const asin = $asin.value;
  119. const $priceTable = document.querySelector('#price table > tbody') || $('olp_feature_div');
  120. const root = h('tr', null, [
  121. h('td', { className: 'a-color-secondary' }, ['History']),
  122. h('td', null, [
  123. h('ul', { style: {cssText: 'margin:0'} }, insertServices(asin)),
  124. ])
  125. ]);
  126. $priceTable.appendChild(root);
  127. serviceEl = [root];
  128. }
  129.  
  130. function init() {
  131. const root = $('desktop_buybox');
  132. if (!root) {
  133. return;
  134. }
  135.  
  136. const observer = new MutationObserver((mutationsList, observer) => {
  137. for(const {removedNodes} of mutationsList) {
  138. console.info(removedNodes);
  139. if (!removedNodes) continue;
  140. for (const node of removedNodes) {
  141. if (node.id === 'buybox') {
  142. injectPriceCompare();
  143. return;
  144. }
  145. }
  146. }
  147. });
  148.  
  149. // Start observing the target node for configured mutations
  150. observer.observe(root, { childList: true });
  151. injectPriceCompare();
  152. }
  153.  
  154. addEventListener('DOMContentLoaded', init);

QingJ © 2025

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