TCG Player Sales Display Data

Remove obfuscation around TCG Player Sales Data

当前为 2021-08-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name TCG Player Sales Display Data
  3. // @namespace https://www.tcgplayer.com/
  4. // @version 0.7
  5. // @description Remove obfuscation around TCG Player Sales Data
  6. // @author Peter Creutzberger
  7. // @match https://www.tcgplayer.com/product/*
  8. // @icon https://www.tcgplayer.com/favicon.ico
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14. let timesChecked = 0;
  15.  
  16. const addCondition = () => ({
  17. totalPrice: 0,
  18. totalQtySold: 0,
  19. totalOrders:0,
  20. marketPriceByQty: function() {
  21. return (this.totalPrice / this.totalQtySold).toFixed(2);
  22. },
  23. marketPriceByOrder: function() {
  24. return (this.totalPrice / this.totalOrders).toFixed(2);
  25. }
  26. });
  27.  
  28. const cleanPriceValue = (price) => +price.substring(1);
  29.  
  30. const strToInt = (str) => +str;
  31.  
  32. const checkSaleDate = (salesArray, saleDate, price) => {
  33. if (!salesArray.min || !salesArray.max) {
  34. return {min: {date: saleDate, price: price}, max: {date: saleDate, price: price}};
  35. }
  36. if (new Date(saleDate).getTime() < new Date(salesArray?.min?.date).getTime() || new Date(saleDate).getTime() === new Date(salesArray?.min?.date).getTime()) {
  37. return Object.assign(salesArray, {min: {date: saleDate,price: price}});
  38. }
  39. if (new Date(saleDate).getTime() > new Date(salesArray?.max?.date).getTime() || new Date(saleDate).getTime() === new Date(salesArray?.max?.date).getTime()) {
  40. return Object.assign(salesArray, {min: {date: saleDate,price: price}});
  41. }
  42.  
  43. }
  44.  
  45. const getPriceData = () => {
  46. const salesByCondition = {};
  47. const modalDisplayLength = Array.from(document.getElementsByClassName("is-modal")).length -= 1;
  48. Array.from(document.getElementsByClassName("is-modal")[modalDisplayLength].children).forEach( (children, index) => {
  49. const listOfSales = Array.from(document.getElementsByClassName("is-modal")[modalDisplayLength].children);
  50. if (listOfSales[index]?.children[1]) {
  51. const currentCondition = listOfSales[index]?.children[1].innerText;
  52. if ( !Object.keys(salesByCondition).includes(currentCondition) ) {
  53. salesByCondition[currentCondition] = addCondition();
  54. }
  55. const cleanPrice = cleanPriceValue(listOfSales[index].children[3].innerText);
  56. salesByCondition[currentCondition].totalPrice += cleanPrice;
  57. salesByCondition[currentCondition].totalQtySold += strToInt(listOfSales[index].children[2].innerText);
  58. salesByCondition[currentCondition].totalOrders += 1;
  59. Object.assign(salesByCondition[currentCondition], checkSaleDate(salesByCondition[currentCondition], listOfSales[index].children[0].innerText, cleanPrice));
  60. }
  61. });
  62. return salesByCondition;
  63. }
  64.  
  65. const createSalesToggle = () => {
  66. const div = document.createElement('div');
  67. div.innerHTML = ('<button class="salesDataToggle" style="position:fixed;top:0;left:0;z-index:9999;width:auto;height:20px;padding:0 5px 0 0;background:#0b0;color:#fff;font-weight:bold;" onclick="toggleSalesData()">Toggle Sales Data Display</button>');
  68. document.body.prepend(div);
  69. }
  70.  
  71. const adjustHeight = (div) => (parseInt(div.style.height.replace(/[a-zA-Z]/g,'')) + 115) + "px";
  72.  
  73. const writeSalesDataContainer = () => {
  74. const div = document.createElement('div');
  75. const setBottom = document.getElementsByClassName("_hj_feedback_container") ? 'bottom:100px' : 'bottom:0';
  76. div.innerHTML = (`<div class="salesDataDisplay" style="position:fixed;${setBottom};left:0;z-index:9999;width:auto;height:0;padding:0 5px 0 0;border:1px solid #d00;background:#999;color:#fff;line-height:normal"></div>`);
  77. document.body.prepend(div);
  78. }
  79.  
  80. const displaySalesData = (salesByCondition) => {
  81. const div = document.getElementsByClassName('salesDataDisplay')[0];
  82. salesByCondition.forEach(condition => {
  83. const displayString = `<div class="displayContainer"><strong>${condition[0]}</strong><br />
  84. <span id="conditionData" style="margin-left: 40px;">Total Sold: ${condition[1].totalQtySold} - Total Orders: ${condition[1].totalOrders} - Total Price: ${condition[1].totalPrice.toFixed(2)}</span><br />
  85. <span id="conditionData" style="margin-left: 40px;">Market Price By Qty: ${condition[1].marketPriceByQty()} - Market Price By Order: ${condition[1].marketPriceByOrder()}</span><br />
  86. <span id="minData" style="margin-left: 40px;">Min Sale Date: ${condition[1]?.min?.date} - Min Sale Price ${condition[1]?.min?.price}</span><br />
  87. <span id="maxData" style="margin-left: 40px;">Max Sale Date: ${condition[1]?.max?.date} - Max Sale Price: ${condition[1]?.max?.price}</span></div>`;
  88. div.innerHTML += displayString + "<br />";
  89. div.style.height = adjustHeight(div);
  90. });
  91. }
  92.  
  93. window.toggleSalesData = function() {
  94. const display = document.getElementsByClassName('salesDataDisplay')[0].style.display;
  95. document.getElementsByClassName('salesDataDisplay')[0].style.display = display === 'none' ? 'inline' : 'none';
  96. }
  97.  
  98. const displayStatus = () => {
  99. timesChecked++;
  100. if (document.getElementsByClassName("price-guide__latest-sales__more")[0]?.children[0]) {
  101. clearInterval(intervalId);
  102. beginDisplay();
  103. }
  104. if (timesChecked > 100) {
  105. clearInterval(intervalId);
  106. }
  107. }
  108.  
  109. let intervalId = setInterval(displayStatus, 500);
  110.  
  111. async function beginDisplay() {
  112. document.getElementsByClassName("price-guide__latest-sales__more")[0].children[0].click();
  113. await sleep(500);
  114. const salesByCondition = getPriceData();
  115. document.getElementsByClassName("modal__overlay")[0].click();
  116. writeSalesDataContainer();
  117. displaySalesData(Object.entries(salesByCondition).sort((elementOne, elementTwo) => elementOne[1].totalQtySold - elementTwo[1].totalQtySold ).reverse());
  118. createSalesToggle();
  119. }
  120.  
  121. function sleep(milliseconds) {
  122. return new Promise(resolve => setTimeout(resolve, milliseconds));
  123. }
  124.  
  125. })();
  126.