DongleSize2Metric

Convert the toy size chart information from imperial to metric system from some famous adult toy makers

  1. // ==UserScript==
  2. // @name DongleSize2Metric
  3. // @namespace https://github.com/yossi99/DongleSize2Metric
  4. // @version v0.0.4-beta
  5. // @description Convert the toy size chart information from imperial to metric system from some famous adult toy makers
  6. // @author Yossi99
  7. // @match https://twintailcreations.com/products/*
  8. // @match https://bad-dragon.com/products/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=bad-dragon.com
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13.  
  14. const SupportedDomains = Object.freeze({
  15. TwinTail: 'twintailcreations.com',
  16. BadDragon: 'bad-dragon.com'
  17. });
  18. const patchAttribute = 'patched';
  19.  
  20. (function() {
  21. const domain = window.location.host
  22. const supDomains = Object.values(SupportedDomains);
  23. if (!supDomains.find(i => i === domain)) {
  24. console.error('Domain not supported');
  25. return;
  26. }
  27. const domainMap = {
  28. [SupportedDomains.TwinTail]: PatchTwinTailSizeTable,
  29. [SupportedDomains.BadDragon]: PatchBadDragonSizeTable
  30. }
  31. const observer = new MutationObserver((event) => {
  32. domainMap[domain]();
  33. });
  34. observer.observe(document.querySelector('body'), {childList: true, subtree: true});
  35. })();
  36.  
  37. function inches2CmFormated(string) { return `${(+string * 2.54).toFixed(2)} cm`;}
  38.  
  39. function PatchBadDragonSizeTable() {
  40. const toySizeTable = [".table.sizing-chart__table", "table.sizing-chart"]
  41. .map((i) => document.querySelector(i))
  42. .find((i) => i);
  43. if (!toySizeTable) {
  44. console.warn("Toy size table not found");
  45. return false;
  46. }
  47. const tableRows = toySizeTable.querySelectorAll('tbody tr')
  48. for (let row of tableRows) {
  49. const header = row.querySelector('th');
  50. (header ? patchHeader(header) : console.warn ('Header not found'));
  51. const rowData = [...row.querySelectorAll(`td:not([${patchAttribute}])`)]
  52. rowData.filter(i => !Number.isNaN(+i.textContent))
  53. ?.forEach(i => patchElement(i));
  54. }
  55. return true;
  56.  
  57. function patchElement(element) {
  58. if (!(element instanceof Element)) {
  59. return;
  60. }
  61. element.textContent = inches2CmFormated(element.textContent);
  62. element.setAttribute(patchAttribute, true);
  63. }
  64. function patchHeader(header) {
  65. if (!(header instanceof Element)) {
  66. return;
  67. }
  68. const headerExp = /(?:\w+| )+ \([\w]+\)$/s;
  69. if (!headerExp.test(header.textContent)) {
  70. return;
  71. }
  72. header.textContent = header.textContent.replace(/\([\w]+\)$/s, '(cm)');
  73. }
  74. }
  75.  
  76. function PatchTwinTailSizeTable() {
  77. const chartBtnSelector = [
  78. '.ProductForm__Label > button',
  79. '[data-action=open-modal].ProductForm__LabelLink'
  80. ].find(i => document.querySelector(i));
  81. if (!chartBtnSelector) {
  82. console.warn('chart button not found');
  83. return;
  84. }
  85. const chartBtn = document.querySelector(chartBtnSelector);
  86. const chartControlName = chartBtn.getAttribute('aria-controls');
  87. if (!chartControlName) {
  88. console.error('Chart control name not found');
  89. return false;
  90. }
  91. const toySizeTable = document.querySelector(`[id=${chartControlName}]`)?.querySelector('table');
  92. if (!toySizeTable) {
  93. console.warn('Toy size table not found!');
  94. return false;
  95. }
  96. const sizeRegExp = /((?:\d*\.)?\d+)(?:"|”)?(?:$|(?="|”))/s;
  97. // Get rows and skip header (toy name)
  98. const tableRows = [...(document.querySelectorAll('tr') ?? [])].slice(1);
  99. for (let row of tableRows) {
  100. // Skip category header
  101. const rowData = [...(row.querySelectorAll(`td:not([${patchAttribute}])`) ?? [])].slice(1);
  102. rowData.filter(i => sizeRegExp.test(i.textContent))
  103. ?.forEach(i => patchElement(i));
  104. }
  105. return true;
  106.  
  107. function patchElement(element) {
  108. if (!(element instanceof Element)) {
  109. return;
  110. }
  111. const inches = +(sizeRegExp.exec(element.textContent)[1]);
  112. if (Number.isNaN(inches)) {
  113. console.error("Failed to extract size from: ", element);
  114. return;
  115. }
  116. element.textContent = element.textContent.replace(sizeRegExp, inches2CmFormated(inches));
  117. element.setAttribute(patchAttribute, true);
  118. }
  119. }