Amazon International Links

Add international links to Amazon product pages

  1. // ==UserScript==
  2. // @name Amazon International Links
  3. // @description Add international links to Amazon product pages
  4. // @author chocolateboy
  5. // @copyright chocolateboy
  6. // @version 4.0.0
  7. // @namespace https://github.com/chocolateboy/userscripts
  8. // @license GPL
  9. // @include https://smile.amazon.tld/*
  10. // @include https://www.amazon.com.be/*
  11. // @include https://www.amazon.tld/*
  12. // @require https://code.jquery.com/jquery-3.7.1.slim.min.js
  13. // @require https://cdn.jsdelivr.net/gh/sizzlemctwizzle/GM_config@43fd0fe4de1166f343883511e53546e87840aeaf/gm_config.js
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // ==/UserScript==
  18.  
  19. // XXX GM_getValue and GM_setValue are used by GM_config
  20.  
  21. /*
  22. *
  23. * further reading:
  24. *
  25. * https://helpful.knobs-dials.com/index.php/Amazon_notes#Links
  26. */
  27.  
  28. /*********************** Constants ********************************/
  29.  
  30. /*
  31. * a map from the Amazon TLD to the corresponding two-letter country code
  32. *
  33. * XXX technically, UK should be GB: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
  34. */
  35. const SITES = {
  36. 'com.au': 'AU', // Australia
  37. 'com.be': 'BE', // Belgium
  38. 'com.br': 'BR', // Brazil
  39. 'ca': 'CA', // Canada
  40. 'cn': 'CN', // China
  41. 'fr': 'FR', // France
  42. 'de': 'DE', // Germany
  43. 'in': 'IN', // India
  44. 'it': 'IT', // Italy
  45. 'co.jp': 'JP', // Japan
  46. 'com.mx': 'MX', // Mexico
  47. 'nl': 'NL', // Netherlands
  48. 'es': 'ES', // Spain
  49. 'se': 'SE', // Sweden
  50. 'com.tr': 'TR', // Turkey
  51. 'ae': 'AE', // UAE
  52. 'co.uk': 'UK', // UK
  53. 'com': 'US', // US
  54. }
  55.  
  56. /*
  57. * Amazon TLDs which support the "smile.amazon" subdomain
  58. */
  59. const SMILE = new Set(['com', 'co.uk', 'de'])
  60.  
  61. /*********************** Functions and Classes ********************************/
  62.  
  63. /*
  64. * A class which encapsulates the logic for creating and updating cross-site links
  65. */
  66. class Linker {
  67. /*
  68. * get the unique identifier (ASIN - Amazon Standard Identification Number)
  69. * for this product, or return a falsey value if it's not found
  70. */
  71. static getASIN () {
  72. const $asin = $('input#ASIN, input[name="ASIN"], input[name="ASIN.0"]')
  73. let asin
  74.  
  75. if ($asin.length) {
  76. asin = $asin.val()
  77. } else { // if there's a canonical link, try to retrieve the ASIN from its URI
  78. // <link rel="canonical" href="https://www.amazon.com/Follows-Movie-Poster-18-28/dp/B01BKUBARA" />
  79. const canonical = $('link[rel="canonical"][href]').attr('href')
  80. let match
  81.  
  82. if (canonical && (match = canonical.match('/dp/(\\w+)$'))) {
  83. asin = match[1]
  84. }
  85. }
  86.  
  87. return asin
  88. }
  89.  
  90. constructor (asin) {
  91. // the unique Amazon identifier for this product
  92. this.asin = asin
  93.  
  94. // the navbar to add the cross-site links to
  95. this.navbar = $('#nav-xshop .nav-ul')
  96.  
  97. // an array of our added elements - jQuery wrappers of child elements of
  98. // the cross-site links navbar
  99. //
  100. // we keep a reference to these elements so we can easily remove them
  101. // from the DOM (and replace them with new elements) whenever the
  102. // country selection changes
  103. this.links = []
  104.  
  105. // extract and store 1) the subdomain (e.g. "www.amazon") and 2) the TLD
  106. // (e.g. "co.uk") of the current site
  107. const parts = location.hostname.split('.')
  108.  
  109. // 1) the subdomain (part before the TLD) of the current site, e.g.
  110. // "www.amazon" or "smile.amazon"
  111. this.subdomain = parts.slice(0, 2).join('.')
  112.  
  113. // 2) the TLD of the current site, e.g. "co.uk" or "com"
  114. this.tld = parts.slice(2).join('.')
  115. }
  116.  
  117. /*
  118. * add a child element to the internal `links` array
  119. */
  120. addLink (tld, country) {
  121. const attrs = {
  122. class: 'nav-a',
  123. title: `amazon.${tld}`
  124. }
  125.  
  126. // XXX we can't always preserve the "smile.amazon" subdomain as it's not
  127. // available for most Amazon TLDs
  128. const subdomain = SMILE.has(tld) ? this.subdomain : 'www.amazon'
  129.  
  130. let tag
  131.  
  132. if (tld === this.tld) {
  133. tag = 'strong'
  134. } else {
  135. tag = 'a'
  136. attrs.href = `//${subdomain}.${tld}/dp/${this.asin}`
  137. }
  138.  
  139. // serialize the attributes, e.g. { title: 'amazon.com' } -> `title="amazon.com"`
  140. const $attrs = Object.entries(attrs)
  141. .map(([key, value]) => `${key}=${JSON.stringify(value)}`)
  142. .join(' ')
  143.  
  144. const link =
  145. `<li class="nav-li">
  146. <div class="nav-div">
  147. <${tag} ${$attrs}>${country}</${tag}>
  148. </div>
  149. </li>`
  150.  
  151. this.links.push($(link))
  152. }
  153.  
  154. /*
  155. * populate the array of links and display them by prepending them to the
  156. * body of the cross-site navigation bar
  157. */
  158. addLinks () {
  159. // create the subset of the TLD -> country-code map (SITES)
  160. // corresponding to the enabled sites
  161. const sites = Object.entries(SITES)
  162. .filter(([tld]) => GM_config.get(tld))
  163. .reduce((obj, [key, val]) => { return obj[key] = val, obj }, {})
  164.  
  165. if (!$.isEmptyObject(sites)) {
  166. // sort the sites by the country code (e.g. AU) rather than the TLD
  167. // (e.g. com.au)
  168. // const tlds = sortBy(Object.keys(sites), tld => sites[tld])
  169. const tlds = Object.keys(sites).sort((a, b) => sites[a].localeCompare(sites[b]))
  170.  
  171. // populate the `links` array with jQuery wrappers for each link
  172. // element (e.g. <li>...</li>)
  173. for (const tld of tlds) {
  174. this.addLink(tld, sites[tld])
  175. }
  176.  
  177. // prepend the cross-site links to the body of the navbar
  178. this.navbar.prepend(...this.links)
  179. }
  180. }
  181.  
  182. /*
  183. * build the underlying data model used by the GM_config utility
  184. */
  185. initializeConfig () {
  186. const checkboxes = {}
  187.  
  188. // sort by country code
  189. for (const tld of Object.keys(SITES).sort((a, b) => SITES[a].localeCompare(SITES[b]))) {
  190. const country = SITES[tld]
  191.  
  192. checkboxes[tld] = {
  193. type: 'checkbox',
  194. label: country,
  195. title: `amazon.${tld}`,
  196. default: (country === 'UK' || country === 'US')
  197. }
  198. }
  199.  
  200. // re-render the links when the settings are updated
  201. const save = () => {
  202. this.removeLinks()
  203. this.addLinks()
  204. GM_config.close()
  205. }
  206.  
  207. const callbacks = { save }
  208.  
  209. GM_config.init('Amazon International Links Settings', checkboxes, callbacks)
  210. }
  211.  
  212. /*
  213. * remove all added links from the DOM and clear the array referencing them
  214. */
  215. removeLinks () {
  216. const { links } = this
  217.  
  218. for (const $link of links) {
  219. $link.remove() // remove from the DOM...
  220. }
  221.  
  222. links.length = 0 // ...and empty the array
  223. }
  224. }
  225.  
  226. /*********************** Main ********************************/
  227.  
  228. const run = () => {
  229. const asin = Linker.getASIN()
  230.  
  231. if (asin) {
  232. const showConfig = () => GM_config.open() // display the settings manager
  233. const linker = new Linker(asin)
  234.  
  235. linker.initializeConfig()
  236. linker.addLinks()
  237.  
  238. GM_registerMenuCommand('Configure Amazon International Links', showConfig)
  239. }
  240. }
  241.  
  242. run()

QingJ © 2025

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