Amazon Seller Product Price Loader

Optimized version to load Amazon product prices with customizable settings, minimized RAM usage, and UI.

目前为 2024-11-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Amazon Seller Product Price Loader
  3. // @namespace http://tampermonkey.net/
  4. // @version 4.7
  5. // @description Optimized version to load Amazon product prices with customizable settings, minimized RAM usage, and UI.
  6. // @license MIT https://opensource.org/licenses/MIT
  7. // @match https://sellercentral.amazon.com/*
  8. // @match https://sellercentral.amazon.co.uk/*
  9. // @match https://sellercentral.amazon.de/*
  10. // @match https://sellercentral.amazon.fr/*
  11. // @match https://sellercentral.amazon.it/*
  12. // @match https://sellercentral.amazon.es/*
  13. // @match https://sellercentral.amazon.ca/*
  14. // @match https://sellercentral.amazon.com.mx/*
  15. // @match https://sellercentral.amazon.com.br/*
  16. // @match https://sellercentral.amazon.co.jp/*
  17. // @match https://sellercentral.Amazon.com.br/*
  18. // @match https://sellercentral.amazon.com.au/*
  19. // @grant GM_getValue
  20. // @grant GM_setValue
  21. // @grant GM_registerMenuCommand
  22. // @grant GM_addStyle
  23. // @grant GM_getResourceText
  24. // @grant GM_xmlhttpRequest
  25. // @resource IMPORTED_CSS https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css
  26. // @run-at document-end
  27. // ==/UserScript==
  28.  
  29. // ==UserScript==
  30. // @name Amazon Seller Product Price Loader (Optimized and Debugged)
  31. // @namespace http://tampermonkey.net/
  32. // @version 4.6
  33. // @description Optimized version to load Amazon product prices with customizable settings, minimized RAM usage, and UI.
  34. // @license MIT https://opensource.org/licenses/MIT
  35. // @match https://sellercentral.amazon.com/*
  36. // @match https://sellercentral.amazon.co.uk/*
  37. // @match https://sellercentral.amazon.de/*
  38. // @match https://sellercentral.amazon.fr/*
  39. // @match https://sellercentral.amazon.it/*
  40. // @match https://sellercentral.amazon.es/*
  41. // @match https://sellercentral.amazon.ca/*
  42. // @match https://sellercentral.amazon.com.mx/*
  43. // @match https://sellercentral.amazon.com.br/*
  44. // @match https://sellercentral.amazon.co.jp/*
  45. // @match https://sellercentral.amazon.in/*
  46. // @match https://sellercentral.amazon.com.au/*
  47. // @grant GM_getValue
  48. // @grant GM_setValue
  49. // @grant GM_registerMenuCommand
  50. // @grant GM_addStyle
  51. // @grant GM_getResourceText
  52. // @grant GM_xmlhttpRequest
  53. // @resource IMPORTED_CSS https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css
  54. // @run-at document-end
  55. // ==/UserScript==
  56.  
  57. (function () {
  58. 'use strict';
  59.  
  60. const defaultOptions = {
  61. fontSize: '12px',
  62. fontFamily: 'Arial, sans-serif',
  63. textColor: '#0066c0',
  64. priceColor: '#0066c0',
  65. reviewsColor: '#008800',
  66. sellerColor: '#cc5500',
  67. glowColor: '#ff00ff',
  68. showFrame: true,
  69. frameColor: '#cccccc',
  70. loadDelay: 10,
  71. currency: 'default',
  72. enableNotifications: false
  73. };
  74.  
  75. let options = Object.keys(defaultOptions).reduce((acc, key) => {
  76. acc[key] = GM_getValue(key, defaultOptions[key]);
  77. return acc;
  78. }, {});
  79.  
  80. const MAX_RETRIES = 2;
  81. const RETRY_DELAY = 40;
  82.  
  83. // Add Tailwind CSS
  84. GM_addStyle(GM_getResourceText("IMPORTED_CSS"));
  85.  
  86. function addCustomStyle() {
  87. let styles = [];
  88.  
  89. if (options.fontBold) {
  90. styles.push('font-bold');
  91. }
  92.  
  93. if (options.glowIntensity > 0) {
  94. styles.push(`text-shadow: 0 0 ${options.glowIntensity * 20}px ${options.glowColor}`);
  95. }
  96.  
  97. styles.push(`color: ${options.priceColor}`);
  98. styles.push(`font-family: ${options.fontFamily}`);
  99. styles.push(`font-size: ${options.fontSize}`);
  100.  
  101. const glowBlurRadius = options.glowIntensity * 10;
  102. const glowSpreadRadius = options.glowIntensity * 5;
  103. styles.push(`text-shadow: 0 0 ${glowBlurRadius}px ${options.glowColor}, 0 0 ${glowSpreadRadius}px ${options.glowColor}`);
  104.  
  105. styles.push(`color: ${options.priceColor}`);
  106. styles.push(`font-family: ${options.fontFamily}`);
  107. styles.push(`font-size: ${options.fontSize}`);
  108.  
  109. const customStyle = styles.join('; ');
  110. document.documentElement.style.setProperty('--custom-style', customStyle);
  111.  
  112. if (options.glowIntensity > 0) {
  113. styles.push(`text-shadow: 0 0 ${options.glowIntensity * 20}px ${options.glowColor}`);
  114. }
  115.  
  116. styles.push(`color: ${options.priceColor}`);
  117. styles.push(`font-family: ${options.fontFamily}`);
  118. styles.push(`font-size: ${options.fontSize}`);
  119. const isDarkMode = document.body.classList.contains('dark');
  120.  
  121. function getInvertedColor(color) {
  122. // Convert the hex color to RGB
  123. const r = parseInt(color.slice(1, 3), 16);
  124. const g = parseInt(color.slice(3, 5), 16);
  125. const b = parseInt(color.slice(5, 7), 16);
  126.  
  127. // Invert the RGB values
  128. const invR = 255 - r;
  129. const invG = 255 - g;
  130. const invB = 255 - b;
  131.  
  132. // Convert the inverted RGB values back to hex
  133. return `#${invR.toString(16).padStart(2, '0')}${invG.toString(16).padStart(2, '0')}${invB.toString(16).padStart(2, '0')}`;
  134. }
  135.  
  136. GM_addStyle(`
  137. .price-display-container {
  138. display: block;
  139. margin-top: 4px;
  140. }
  141. .price-display {
  142. display: block;
  143. font-size: ${options.fontSize};
  144. font-family: ${options.fontFamily};
  145. color: ${isDarkMode ? getInvertedColor(options.priceColor) : options.priceColor};
  146. text-shadow: 0 0 ${glowBlurRadius}px ${options.glowColor}, 0 0 ${glowSpreadRadius}px ${options.glowColor};
  147. font-weight: ${options.fontBold ? 'bold' : 'normal'};
  148. }
  149. .rating-display {
  150. display: block;
  151. font-size: ${options.fontSize};
  152. font-family: ${options.fontFamily};
  153. color: ${isDarkMode ? getInvertedColor(options.reviewsColor) : options.reviewsColor};
  154. text-shadow: 0 0 ${glowBlurRadius}px ${options.glowColor}, 0 0 ${glowSpreadRadius}px ${options.glowColor};
  155. font-weight: ${options.fontBold ? 'bold' : 'normal'};
  156. }
  157. .seller-display {
  158. display: block;
  159. font-size: ${options.fontSize};
  160. font-family: ${options.fontFamily};
  161. color: ${isDarkMode ? getInvertedColor(options.sellerColor) : options.sellerColor};
  162. text-shadow: 0 0 ${glowBlurRadius}px ${options.glowColor}, 0 0 ${glowSpreadRadius}px ${options.glowColor};
  163. font-weight: ${options.fontBold ? 'bold' : 'normal'};
  164. }
  165. .loading {
  166. opacity: 0.5;
  167. padding: 1px 8px;
  168. border: 1px solid ${options.frameColor};
  169. display: inline-block;
  170. }
  171.  
  172. #amazon-price-loader-settings {
  173. position: fixed;
  174. top: 50%;
  175. left: 50%;
  176. transform: translate(-50%, -50%);
  177. z-index: 10000;
  178. background: #f9f9f9;
  179. padding: 15px;
  180. border-radius: 8px;
  181. box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
  182. max-width: 400px;
  183. width: 90%;
  184. overflow: hidden;
  185. border: 1px solid #ccc;
  186. display: flex;
  187. flex-wrap: wrap;
  188. justify-content: space-between;
  189. }
  190.  
  191. .settings-overlay {
  192. position: fixed;
  193. top: 0;
  194. left: 0;
  195. right: 0;
  196. bottom: 0;
  197. background: rgba(0, 0, 0, 0.5);
  198. z-index: 9999;
  199. }
  200.  
  201. .tooltip {
  202. position: absolute;
  203. background: white;
  204. border: 1px solid black;
  205. padding: 10px;
  206. z-index: 1000;
  207. }
  208.  
  209. .setting-row {
  210. display: flex;
  211. flex-direction: column;
  212. width: 48%;
  213. margin-bottom: 10px;
  214. }
  215.  
  216. .setting-label {
  217. font-weight: bold;
  218. color: #333;
  219. margin-bottom: 5px;
  220. }
  221.  
  222. .setting-button {
  223. background-color: #007bff;
  224. color: white;
  225. border: none;
  226. border-radius: 5px;
  227. padding: 5px 10px;
  228. cursor: pointer;
  229. transition: background-color 0.3s;
  230. font-size: 14px;
  231. width: 100%;
  232. margin-top: 10px;
  233. }
  234.  
  235. .setting-button:hover {
  236. background-color: #0056b3;
  237. }
  238.  
  239. .color-picker {
  240. width: 100%;
  241. height: 30px;
  242. border: none;
  243. padding: 0;
  244. cursor: pointer;
  245. }
  246.  
  247. .font-picker {
  248. width: 100%;
  249. padding: 5px;
  250. border: 1px solid #ccc;
  251. border-radius: 5px;
  252. }
  253.  
  254. .font-size-input {
  255. width: 100%;
  256. padding: 5px;
  257. border: 1px solid #ccc;
  258. border-radius: 5px;
  259. }
  260. `);
  261. }
  262.  
  263. function createSettingsUI() {
  264. // Check if the settings overlay already exists
  265. if (document.querySelector('.settings-overlay')) {
  266. return; // Exit the function if the overlay is already present
  267. }
  268. const settingsContainer = document.createElement('div');
  269. settingsContainer.className = 'settings-container bg-white shadow-md rounded-lg p-2 w-48';
  270. const title = document.createElement('h2');
  271. title.textContent = 'Settings';
  272. title.classList.add('text-xs', 'font-bold', 'mb-2');
  273. settingsContainer.style.position = 'fixed';
  274. settingsContainer.style.top = '0px'; // Adjust the value to move the menu up or down
  275. settingsContainer.style.right = '70px'; // Adjust the value to move the menu left or right
  276.  
  277. settingsContainer.appendChild(title);
  278.  
  279. // Display Settings
  280. const displaySettings = document.createElement('div');
  281. displaySettings.classList.add('mb-2');
  282.  
  283. const fontSizeRow = document.createElement('div');
  284. fontSizeRow.classList.add('flex', 'items-center', 'justify-between');
  285.  
  286. const fontSizeLabel = document.createElement('label');
  287. fontSizeLabel.textContent = 'Size';
  288. fontSizeLabel.classList.add('text-xxs', 'font-medium');
  289.  
  290. const fontSizeInput = document.createElement('input');
  291. fontSizeInput.type = 'number';
  292. fontSizeInput.value = options.fontSize.replace('px', '');
  293. fontSizeInput.min = 8;
  294. fontSizeInput.max = 36;
  295. fontSizeInput.className = 'w-10 px-1 py-0.5 border rounded text-xxs';
  296. fontSizeInput.onchange = (e) => {
  297. options.fontSize = `${e.target.value}px`;
  298. addCustomStyle();
  299. };
  300.  
  301. fontSizeRow.appendChild(fontSizeLabel);
  302. fontSizeRow.appendChild(fontSizeInput);
  303. displaySettings.appendChild(fontSizeRow);
  304.  
  305. const fontFamilyRow = document.createElement('div');
  306. fontFamilyRow.classList.add('flex', 'items-center', 'justify-between');
  307.  
  308. const fontFamilyLabel = document.createElement('label');
  309. fontFamilyLabel.textContent = 'Font';
  310. fontFamilyLabel.classList.add('text-xxs', 'font-medium');
  311.  
  312. const fontFamilyInput = document.createElement('select');
  313. fontFamilyInput.className = 'w-full px-1 py-0.5 border rounded text-xxs';
  314. fontFamilyInput.onchange = (e) => {
  315. options.fontFamily = e.target.value;
  316. addCustomStyle();
  317. };
  318.  
  319. const fonts = [
  320. 'Arial, sans-serif',
  321. 'Times New Roman, serif',
  322. 'Verdana, sans-serif',
  323. 'Georgia, serif',
  324. 'Courier New, monospace',
  325. 'Roboto, sans-serif',
  326. 'Open Sans, sans-serif',
  327. 'Lato, sans-serif',
  328. 'Montserrat, sans-serif',
  329. 'Poppins, sans-serif'
  330. ];
  331. fonts.forEach(font => {
  332. const option = document.createElement('option');
  333. option.value = font;
  334. option.textContent = font;
  335. option.selected = options.fontFamily === font;
  336. fontFamilyInput.appendChild(option);
  337. });
  338.  
  339. // Bold functionality
  340. const fontBoldRow = document.createElement('div');
  341. fontBoldRow.classList.add('flex', 'items-center', 'justify-between');
  342.  
  343. const fontBoldLabel = document.createElement('label');
  344. fontBoldLabel.textContent = 'Bold';
  345. fontBoldLabel.classList.add('text-xxs', 'font-medium');
  346.  
  347. const fontBoldCheckbox = document.createElement('input');
  348. fontBoldCheckbox.type = 'checkbox';
  349. fontBoldCheckbox.checked = options.fontBold;
  350. fontBoldCheckbox.className = 'w-4 h-4 border rounded';
  351. fontBoldCheckbox.onchange = (e) => {
  352. options.fontBold = e.target.checked;
  353. addCustomStyle();
  354. };
  355.  
  356. fontBoldRow.appendChild(fontBoldLabel);
  357. fontBoldRow.appendChild(fontBoldCheckbox);
  358. displaySettings.appendChild(fontBoldRow);
  359.  
  360. fontFamilyRow.appendChild(fontFamilyLabel);
  361. fontFamilyRow.appendChild(fontFamilyInput);
  362. displaySettings.appendChild(fontFamilyRow);
  363. displaySettings.appendChild(fontBoldCheckbox);
  364. displaySettings.appendChild(fontBoldLabel);
  365.  
  366. settingsContainer.appendChild(displaySettings);
  367.  
  368. // Color Settings
  369. const colorSettings = document.createElement('div');
  370. colorSettings.classList.add('mb-2');
  371.  
  372. const priceColorRow = document.createElement('div');
  373. priceColorRow.classList.add('flex', 'items-center', 'justify-between');
  374.  
  375. const priceColorLabel = document.createElement('label');
  376. priceColorLabel.textContent = 'Price';
  377. priceColorLabel.classList.add('text-xxs', 'font-medium');
  378.  
  379. const priceColorInput = document.createElement('input');
  380. priceColorInput.type = 'color';
  381. priceColorInput.value = options.priceColor;
  382. priceColorInput.className = 'w-5 h-5 border rounded-full';
  383. priceColorInput.onchange = (e) => {
  384. options.priceColor = e.target.value;
  385. addCustomStyle();
  386. };
  387.  
  388. priceColorRow.appendChild(priceColorLabel);
  389. priceColorRow.appendChild(priceColorInput);
  390. colorSettings.appendChild(priceColorRow);
  391.  
  392. const reviewsColorRow = document.createElement('div');
  393. reviewsColorRow.classList.add('flex', 'items-center', 'justify-between');
  394.  
  395. const reviewsColorLabel = document.createElement('label');
  396. reviewsColorLabel.textContent = 'Reviews';
  397. reviewsColorLabel.classList.add('text-xxs', 'font-medium');
  398.  
  399. const reviewsColorInput = document.createElement('input');
  400. reviewsColorInput.type = 'color';
  401. reviewsColorInput.value = options.reviewsColor;
  402. reviewsColorInput.className = 'w-5 h-5 border rounded-full';
  403. reviewsColorInput.onchange = (e) => {
  404. options.reviewsColor = e.target.value;
  405. addCustomStyle();
  406. };
  407.  
  408. reviewsColorRow.appendChild(reviewsColorLabel);
  409. reviewsColorRow.appendChild(reviewsColorInput);
  410. colorSettings.appendChild(reviewsColorRow);
  411.  
  412. const sellerColorRow = document.createElement('div');
  413. sellerColorRow.classList.add('flex', 'items-center', 'justify-between');
  414.  
  415. const sellerColorLabel = document.createElement('label');
  416. sellerColorLabel.textContent = 'Seller';
  417. sellerColorLabel.classList.add('text-xxs', 'font-medium');
  418.  
  419. const sellerColorInput = document.createElement('input');
  420. sellerColorInput.type = 'color';
  421. sellerColorInput.value = options.sellerColor;
  422. sellerColorInput.className = 'w-5 h-5 border rounded-full';
  423. sellerColorInput.onchange = (e) => {
  424. options.sellerColor = e.target.value;
  425. addCustomStyle();
  426. };
  427.  
  428. sellerColorRow.appendChild(sellerColorLabel);
  429. sellerColorRow.appendChild(sellerColorInput);
  430. colorSettings.appendChild(sellerColorRow);
  431.  
  432. settingsContainer.appendChild(colorSettings);
  433.  
  434. // Global Glow Settings
  435. const glowSettings = document.createElement('div');
  436. glowSettings.classList.add('mb-2');
  437.  
  438. const glowColorRow = document.createElement('div');
  439. glowColorRow.classList.add('flex', 'items-center', 'justify-between');
  440.  
  441. const glowColorLabel = document.createElement('label');
  442. glowColorLabel.textContent = 'Glow Color';
  443. glowColorLabel.classList.add('text-xxs', 'font-medium');
  444.  
  445. const glowColorInput = document.createElement('input');
  446. glowColorInput.type = 'color';
  447. glowColorInput.value = options.glowColor;
  448. glowColorInput.className = 'w-5 h-5 border rounded-full';
  449. glowColorInput.onchange = (e) => {
  450. options.glowColor = e.target.value;
  451. GM_setValue('glowColor', options.glowColor); // Save to memory
  452. addCustomStyle();
  453. };
  454. const glowColor = options.glowColor || '#ff00ff';
  455. const glowIntensity = options.glowIntensity || 0.5;
  456. const isBold = options.isBold ? 'bold' : 'normal';
  457. const fontColor = options.fontColor || '#000000';
  458.  
  459. const style = document.createElement('style');
  460. style.innerHTML = `
  461. .price-display, .rating-display, .seller-display {
  462. text-shadow: 0 0 ${glowIntensity * 10}px ${glowColor}; /* Apply glow */
  463. color: ${fontColor}; /* Apply font color */
  464. font-weight: ${isBold}; /* Apply bold if set */
  465. }
  466. `;
  467. document.head.appendChild(style);
  468. addCustomStyle();
  469.  
  470. const glowIntensityInput = document.createElement('input');
  471. glowIntensityInput.type = 'range';
  472. glowIntensityInput.min = 0;
  473. glowIntensityInput.max = 1;
  474. glowIntensityInput.step = 0.1;
  475. glowIntensityInput.value = options.glowIntensity;
  476. glowIntensityInput.className = 'w-full';
  477. glowIntensityInput.onchange = (e) => {
  478. options.glowIntensity = parseFloat(e.target.value);
  479. GM_setValue('glowIntensity', options.glowIntensity); // Save to memory
  480. addCustomStyle();
  481. };
  482.  
  483. const glowIntensityLabel = document.createElement('label');
  484. glowIntensityLabel.textContent = 'Glow Intensity';
  485. glowIntensityLabel.classList.add('text-xxs', 'font-medium');
  486.  
  487. glowColorRow.appendChild(glowColorLabel);
  488. glowColorRow.appendChild(glowColorInput);
  489. glowSettings.appendChild(glowColorRow);
  490. glowSettings.appendChild(glowIntensityLabel);
  491. glowSettings.appendChild(glowIntensityInput);
  492.  
  493. settingsContainer.appendChild(glowSettings);
  494.  
  495. const buttonsContainer = document.createElement('div');
  496. buttonsContainer.classList.add('flex', 'justify-end', 'space-x-2');
  497.  
  498. const saveButton = document.createElement('button');
  499. saveButton.className = 'bg-blue-500 hover:bg-blue-600 text-white font-medium py-0.5 px-2 rounded text-xxs';
  500. saveButton.textContent = 'Save';
  501. saveButton.onclick = () => {
  502. options.fontSize = `${fontSizeInput.value}px`;
  503. options.fontFamily = fontFamilyInput.value;
  504. options.fontBold = fontBoldCheckbox.checked;
  505. options.priceColor = priceColorInput.value;
  506. options.reviewsColor = reviewsColorInput.value;
  507. options.sellerColor = sellerColorInput.value;
  508. options.glowColor = GM_getValue('glowColor', '#ff00ff'); // Default glow color
  509. options.glowIntensity = GM_getValue('glowIntensity', 0.5); // Default glow intensity
  510. options.isBold = GM_getValue('isBold', false); // Default bold setting (false = not bold)
  511. options.fontColor = GM_getValue('fontColor', '#000000'); // Default font color
  512.  
  513.  
  514. Object.keys(options).forEach(key => {
  515. GM_setValue(key, options[key]);
  516. });
  517.  
  518. addCustomStyle();
  519. document.body.removeChild(settingsOverlay);
  520. };
  521.  
  522.  
  523.  
  524. buttonsContainer.appendChild(saveButton);
  525. settingsContainer.appendChild(buttonsContainer);
  526.  
  527. const settingsOverlay = document.createElement('div');
  528. settingsOverlay.className = 'settings-overlay';
  529. settingsOverlay.appendChild(settingsContainer);
  530. settingsOverlay.style.backgroundColor = 'transparent'; // Ensure it's transparent
  531. document.body.appendChild(settingsOverlay);
  532. }
  533.  
  534. function getCurrencySymbol(currency) {
  535. const symbols = {
  536. 'USD': '$ ',
  537. 'EUR': '€ ',
  538. 'GBP': '£ ',
  539. 'NIS': '₪ ',
  540. 'JPY': '¥ '
  541. };
  542. return symbols[currency] || '';
  543. }
  544.  
  545. async function fetchPriceAndRating(url, retries = 0) {
  546. try {
  547. const response = await new Promise((resolve, reject) => {
  548. GM_xmlhttpRequest({
  549. method: 'GET',
  550. url: url,
  551. timeout: 5000,
  552. onload: resolve,
  553. onerror: reject,
  554. ontimeout: reject
  555. });
  556. });
  557.  
  558. const parser = new DOMParser();
  559. const doc = parser.parseFromString(response.responseText, 'text/html');
  560. const priceElement = doc.querySelector('.a-price .a-offscreen');
  561. let price = priceElement ? priceElement.textContent.trim() : 'No price found';
  562.  
  563. if (options.currency !== 'default') {
  564. price = `${getCurrencySymbol(options.currency)} ${price.replace(/[^\d.]/g, '')}`;
  565. }
  566.  
  567. const ratingElement = doc.querySelector('.a-icon-alt');
  568. let rating = ratingElement ? ratingElement.textContent : 'No rating found';
  569. const numberOfRatingsElement = doc.querySelector('#acrCustomerReviewText');
  570. let numberOfRatings = numberOfRatingsElement ? numberOfRatingsElement.textContent : '0 ratings';
  571.  
  572. return { price, rating, numberOfRatings };
  573. } catch (error) {
  574. if (retries < MAX_RETRIES) {
  575. await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
  576. return fetchPriceAndRating(url, retries + 1);
  577. }
  578. return { price: 'Error fetching price', rating: 'Error fetching rating', numberOfRatings: 'Error fetching ratings' };
  579. }
  580. }
  581.  
  582. async function fetchPriceRatingAndSeller(url, retries = 0) {
  583. try {
  584. const response = await new Promise((resolve, reject) => {
  585. GM_xmlhttpRequest({
  586. method: 'GET',
  587. url: url,
  588. timeout: 5000,
  589. onload: resolve,
  590. onerror: reject,
  591. ontimeout: reject
  592. });
  593. });
  594.  
  595. const parser = new DOMParser();
  596. const doc = parser.parseFromString(response.responseText, 'text/html');
  597.  
  598. const priceElement = doc.querySelector('.a-price .a-offscreen');
  599. let price = priceElement ? priceElement.textContent.trim() : 'No price found';
  600.  
  601. if (options.currency !== 'default') {
  602. price = `${getCurrencySymbol(options.currency)} ${price.replace(/[^\d.]/g, '')}`;
  603. }
  604.  
  605. const ratingElement = doc.querySelector('.a-icon-alt');
  606. let rating = ratingElement ? ratingElement.textContent : 'No rating found';
  607.  
  608. const numberOfRatingsElement = doc.querySelector('#acrCustomerReviewText');
  609. let numberOfRatings = numberOfRatingsElement ? numberOfRatingsElement.textContent : '0 ratings';
  610.  
  611. const sellerElement = doc.querySelector('#sellerProfileTriggerId');
  612. let sellerName = sellerElement ? sellerElement.textContent.trim() : 'Seller info not available';
  613.  
  614. return { price, rating, numberOfRatings, sellerName };
  615. } catch (error) {
  616. if (retries < MAX_RETRIES) {
  617. await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
  618. return fetchPriceRatingAndSeller(url, retries + 1);
  619. }
  620. return { price: 'Error fetching price', rating: 'Error fetching rating', numberOfRatings: 'Error fetching ratings', sellerName: 'Error fetching seller' };
  621. }
  622. }
  623.  
  624. async function displayPriceRatingAndSeller(link) {
  625. const existingDisplay = link.nextElementSibling;
  626. if (existingDisplay && existingDisplay.classList.contains('price-display-container')) return;
  627.  
  628. const parentContainer = document.createElement('div');
  629. parentContainer.className = 'price-link-container';
  630. link.parentNode.insertBefore(parentContainer, link);
  631. parentContainer.appendChild(link);
  632.  
  633. const container = document.createElement('div');
  634. container.className = 'price-display-container loading';
  635. container.style.border = options.showFrame ? `1px solid ${options.frameColor}` : 'none';
  636. container.style.padding = '0.1em';
  637.  
  638. const priceDisplay = document.createElement('div');
  639. priceDisplay.className = 'price-display';
  640. priceDisplay.textContent = 'Loading price...';
  641.  
  642. const ratingDisplay = document.createElement('div');
  643. ratingDisplay.className = 'rating-display';
  644. ratingDisplay.textContent = 'Loading rating...';
  645.  
  646. const sellerDisplay = document.createElement('div');
  647. sellerDisplay.className = 'seller-display';
  648. sellerDisplay.textContent = 'Loading seller...';
  649.  
  650. container.appendChild(priceDisplay);
  651. container.appendChild(ratingDisplay);
  652. container.appendChild(sellerDisplay);
  653. parentContainer.appendChild(container);
  654.  
  655. try {
  656. await new Promise(resolve => setTimeout(resolve, options.loadDelay));
  657. const { price, rating, numberOfRatings, sellerName } = await fetchPriceRatingAndSeller(link.href);
  658.  
  659. priceDisplay.textContent = price;
  660. ratingDisplay.textContent = `${rating} (${numberOfRatings})`;
  661. sellerDisplay.textContent = `Seller: ${sellerName}`;
  662.  
  663. priceDisplay.classList.remove('loading');
  664. ratingDisplay.classList.remove('loading');
  665. sellerDisplay.classList.remove('loading');
  666.  
  667. if (options.enableNotifications && price !== 'No price found' && price !== 'Error fetching price') {
  668. alert(`Price fetched: ${price}`);
  669. }
  670. } catch (error) {
  671. console.error('Failed to fetch price and rating', error);
  672. }
  673. }
  674.  
  675. function setupPriceAndRatingDisplay() {
  676. const productLinks = document.querySelectorAll('a[href*="/dp/"]');
  677. productLinks.forEach(link => displayPriceRatingAndSeller(link));
  678. }
  679.  
  680. addCustomStyle();
  681. setupPriceAndRatingDisplay();
  682.  
  683. // Register the command for settings
  684. GM_registerMenuCommand('Amazon Price Loader Settings', createSettingsUI);
  685.  
  686. // MutationObserver to monitor changes on the page
  687. const observer = new MutationObserver(setupPriceAndRatingDisplay);
  688. observer.observe(document.body, { childList: true, subtree: true });
  689. })();