RSS: TLDR/Bloomberg Date Cycler

Filter sponsored articles, remove margin classes, and add date navigation on tldr.tech

  1. // ==UserScript==
  2. // @name RSS: TLDR/Bloomberg Date Cycler
  3. // @version 1.5
  4. // @description Filter sponsored articles, remove margin classes, and add date navigation on tldr.tech
  5. // @author Jerry with Claude
  6. // @match https://tldr.tech/*
  7. // @match https://www.bloomberg.com/news/articles/*/stock-market-today-dow-s-p-live-updates
  8. // @grant none
  9. // @homepage https://greasyfork.org/en/scripts/525303
  10.  
  11. // @namespace http://tampermonkey.net/
  12. // ==/UserScript==
  13. // https://tldr.tech/api/latest/ai
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. // Function to get the next business day
  19. function getNextBusinessDay(dateStr) {
  20. const parts = dateStr.split('-').map(Number);
  21. let nextDay = new Date(Date.UTC(parts[0], parts[1] - 1, parts[2]));
  22.  
  23. do {
  24. nextDay = new Date(nextDay.getTime() + 86400000);
  25. } while (nextDay.getUTCDay() === 0 || nextDay.getUTCDay() === 6);
  26.  
  27. return nextDay;
  28. }
  29.  
  30. // Function to get the previous business day
  31. function getPreviousBusinessDay(dateStr) {
  32. const parts = dateStr.split('-').map(Number);
  33. let prevDay = new Date(Date.UTC(parts[0], parts[1] - 1, parts[2]));
  34.  
  35. do {
  36. prevDay = new Date(prevDay.getTime() - 86400000);
  37. } while (prevDay.getUTCDay() === 0 || prevDay.getUTCDay() === 6);
  38.  
  39. return prevDay;
  40. }
  41.  
  42. // Function to format date as YYYY-MM-DD
  43. function formatDate(date) {
  44. return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}-${String(date.getUTCDate()).padStart(2, '0')}`;
  45. }
  46.  
  47. // Function to add navigation buttons
  48. function addNavigationButtons() {
  49. const currentPath = window.location.pathname;
  50. const dateMatch = currentPath.match(/\d{4}-\d{2}-\d{2}/);
  51.  
  52. // Already exists, don't add again
  53. if (document.querySelector('#tldr-nav-container')) {
  54. return;
  55. }
  56.  
  57. if (dateMatch) {
  58. // Create container for buttons
  59. const container = document.createElement('div');
  60. container.id = 'tldr-nav-container';
  61. container.style.cssText = `
  62. position: fixed;
  63. bottom: 20px;
  64. right: 16px;
  65. display: flex;
  66. gap: 8px;
  67. z-index: 9999;
  68. background: rgba(255, 255, 255, 0.9);
  69. padding: 4px;
  70. border-radius: 8px;
  71. box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  72. `;
  73.  
  74. // Common button styles
  75. const buttonStyle = `
  76. padding: 4px 12px;
  77. background: #6366f1;
  78. color: white;
  79. border: none;
  80. border-radius: 6px;
  81. cursor: pointer;
  82. font-size: 14px;
  83. font-weight: 500;
  84. transition: background 0.2s;
  85. `;
  86.  
  87. // Create previous day button
  88. const prevButton = document.createElement('button');
  89. prevButton.textContent = '← Next';
  90. prevButton.style.cssText = buttonStyle;
  91.  
  92. // Create next day button
  93. const nextButton = document.createElement('button');
  94. nextButton.textContent = 'Prev →';
  95. nextButton.style.cssText = buttonStyle;
  96.  
  97. // Add hover effects
  98. [prevButton, nextButton].forEach(button => {
  99. button.addEventListener('mouseover', () => {
  100. button.style.background = '#4f46e5';
  101. });
  102. button.addEventListener('mouseout', () => {
  103. button.style.background = '#6366f1';
  104. });
  105. });
  106.  
  107. // Add click handlers
  108. nextButton.onclick = () => {
  109. const prevDay = getPreviousBusinessDay(dateMatch[0]);
  110. window.location.href = currentPath.replace(dateMatch[0], formatDate(prevDay));
  111. };
  112.  
  113. prevButton.onclick = () => {
  114. const nextDay = getNextBusinessDay(dateMatch[0]);
  115. window.location.href = currentPath.replace(dateMatch[0], formatDate(nextDay));
  116. };
  117.  
  118. // Assemble and insert
  119. container.appendChild(prevButton);
  120. container.appendChild(nextButton);
  121.  
  122. // Insert at the top of the body
  123. const targetElement = document.body;
  124. if (targetElement) {
  125. targetElement.insertBefore(container, targetElement.firstChild);
  126. }
  127. }
  128. }
  129.  
  130. // Function to remove unwanted content
  131. function removeContent() {
  132. // Remove sponsored articles
  133. const articles = document.querySelectorAll('article');
  134. articles.forEach(article => {
  135. const heading = article.querySelector('h3');
  136. if (heading && heading.textContent.includes('(Sponsor)')) {
  137. article.style.display = 'none';
  138. }
  139. });
  140.  
  141. // Remove elements with specific margin classes
  142. const marginElements = document.querySelectorAll('.mb-2');
  143. marginElements.forEach(element => {
  144. element.style.display = 'none';
  145. });
  146. }
  147.  
  148. // Run immediately and also after a short delay to ensure DOM is ready
  149. function initialize() {
  150. removeContent();
  151. addNavigationButtons();
  152.  
  153. // Try again after a short delay
  154. setTimeout(() => {
  155. removeContent();
  156. addNavigationButtons();
  157. }, 2000);
  158. }
  159.  
  160. // Run when page loads
  161. if (document.readyState === 'loading') {
  162. document.addEventListener('DOMContentLoaded', initialize);
  163. } else {
  164. initialize();
  165. }
  166.  
  167. // Watch for dynamic content changes
  168. const observer = new MutationObserver(() => {
  169. removeContent();
  170. addNavigationButtons();
  171. });
  172.  
  173. observer.observe(document.body, {
  174. childList: true,
  175. subtree: true
  176. });
  177. })();