Convert Ascent and Descent to Feet

Converts ascent and descent values from meters to feet on the for Strava Sauce users.

  1. // ==UserScript==
  2. // @name Convert Ascent and Descent to Feet
  3. // @namespace typpi.online
  4. // @version 1.02
  5. // @description Converts ascent and descent values from meters to feet on the for Strava Sauce users.
  6. // @author Nick2bad4u
  7. // @license UnLicense
  8. // @match https://www.strava.com/activities/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=strava.com
  10. // @grant none
  11. // @run-at document-end
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. // Function to convert meters to feet
  18. const metersToFeet = (meters) => {
  19. // Return converted value or 0 if invalid
  20. if (isNaN(meters) || meters === null || meters === '') {
  21. return 0; // Return 0 if value is invalid
  22. }
  23. return (meters * 3.28084).toFixed(2);
  24. };
  25.  
  26. // Function to update the values in feet and insert them into the correct grid
  27. const updateValues = () => {
  28. console.log('updateValues called');
  29.  
  30. // Select the elements using the given selectors
  31. const ascentElement = document.querySelector(
  32. 'body > div.fancybox-overlay.fancybox-overlay-fixed > div > div > div > div > div > div:nth-child(17) > div.grid > table > tbody > tr:nth-child(1) > td:nth-child(2) > span',
  33. );
  34. const descentElement = document.querySelector(
  35. 'body > div.fancybox-overlay.fancybox-overlay-fixed > div > div > div > div > div > div:nth-child(17) > div.grid > table > tbody > tr:nth-child(1) > td:nth-child(3) > span',
  36. );
  37.  
  38. // Check if the elements exist on the page
  39. if (ascentElement && descentElement) {
  40. console.log(
  41. 'Ascent and descent elements found:',
  42. ascentElement,
  43. descentElement,
  44. );
  45.  
  46. // Extract the numeric values and convert them to feet
  47. const ascentValueInMeters = parseFloat(ascentElement.textContent.trim());
  48. const descentValueInMeters = parseFloat(
  49. descentElement.textContent.trim(),
  50. );
  51.  
  52. // Convert ascent value and check if valid
  53. const ascentValueInFeet = metersToFeet(ascentValueInMeters);
  54. if (ascentValueInFeet !== 0) {
  55. console.log('Converted ascent value (feet):', ascentValueInFeet);
  56. ascentElement.innerHTML = `${ascentValueInFeet} <span class="gridUnits">ft</span><br><span class="gridTitle">Ascent</span>`;
  57. } else {
  58. console.log('Invalid Ascent value detected:', ascentValueInMeters);
  59. }
  60.  
  61. // Convert descent value and check if valid
  62. const descentValueInFeet = metersToFeet(descentValueInMeters);
  63. if (descentValueInFeet !== 0) {
  64. console.log('Converted descent value (feet):', descentValueInFeet);
  65. descentElement.innerHTML = `${descentValueInFeet} <span class="gridUnits">ft</span><br><span class="gridTitle">Descent</span>`;
  66. } else {
  67. console.log('Invalid Descent value detected:', descentValueInMeters);
  68. }
  69.  
  70. // Add the ascent and descent data to the same row in the grid
  71. const grid = document.querySelector(
  72. '#heading > div > div.row.no-margins.activity-summary-container > div.spans8.activity-stats.mt-md.mb-md > div.summaryGrid > table > tbody',
  73. );
  74. console.log('Adding ascent and descent values to the grid:', grid);
  75.  
  76. const newRow = document.createElement('tr');
  77. newRow.innerHTML = `
  78. <td data-column="0" data-row="8">
  79. <span class="summaryGridDataContainer" onclick="javascript:window.open(&quot;chrome-extension:/dhiaggccakkgdfcadnklkbljcgicpckn/app/index.html#/globalSettings?viewOptionHelperId=displayAdvancedPowerData&quot;,&quot;_blank&quot;);">
  80. ${ascentValueInFeet} <span class="summaryGridUnits">ft</span><br><span class="summaryGridTitle">Ascent</span>
  81. </span>
  82. </td>
  83. <td data-column="1" data-row="8">
  84. <span class="summaryGridDataContainer" onclick="javascript:window.open(&quot;chrome-extension:/dhiaggccakkgdfcadnklkbljcgicpckn/app/index.html#/globalSettings?viewOptionHelperId=displayAdvancedPowerData&quot;,&quot;_blank&quot;);">
  85. ${descentValueInFeet} <span class="summaryGridUnits">ft</span><br><span class="summaryGridTitle">Descent</span>
  86. </span>
  87. </td>
  88. `;
  89. grid.appendChild(newRow);
  90. console.log('Ascent and Descent values added to grid:', newRow);
  91.  
  92. // Disconnect the observer after updating the values
  93. observer.disconnect();
  94. console.log('Observer disconnected after values are updated');
  95. } else {
  96. console.log('Ascent or descent elements not found');
  97. }
  98. };
  99.  
  100. // Debounce function to limit the frequency of function calls
  101. function debounce(func, wait) {
  102. let timeout;
  103. return function (...args) {
  104. clearTimeout(timeout);
  105. timeout = setTimeout(() => func.apply(this, args), wait);
  106. };
  107. }
  108.  
  109. // Create a MutationObserver to watch for changes in the DOM
  110. const observer = new MutationObserver(() => {
  111. console.log('Mutation observed');
  112. // Run the update function when the elements are added to the DOM
  113. updateValuesDebounced();
  114. });
  115.  
  116. // Wrap the updateValues function with debounce
  117. const updateValuesDebounced = debounce(updateValues, 1000); // Adjust the delay as needed
  118.  
  119. // Observe changes to the document body
  120. observer.observe(document.body, {
  121. childList: true,
  122. subtree: true,
  123. });
  124. console.log('Observer is now watching for changes');
  125. })();