Universal Auto-Scroll

Adds auto-scrolling functionality to any website

  1. // ==UserScript==
  2. // @name Universal Auto-Scroll
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Adds auto-scrolling functionality to any website
  6. // @author You
  7. // @match *://*/*
  8. // @grant GM_setValue
  9. // @grant GM_getValue
  10. // @run-at document-idle
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // Wait a bit for page to be fully ready
  17. setTimeout(initAutoScroll, 1000);
  18.  
  19. function initAutoScroll() {
  20. // Configuration
  21. const DEFAULT_SETTINGS = {
  22. isActive: false,
  23. speed: 3,
  24. isMinimized: false,
  25. pixelsPerSecond: 50 // Base scrolling speed - will be modified by speed setting
  26. };
  27.  
  28. // State variables
  29. let isScrolling = false;
  30. let scrollIntervalId = null;
  31. let userScrollDetected = false;
  32. let userScrollTimeout = null;
  33. let lastScrollPosition = window.scrollY;
  34.  
  35. // Load saved settings or use defaults - Using Tampermonkey's built-in storage
  36. let scrollSettings;
  37. try {
  38. scrollSettings = JSON.parse(GM_getValue('autoScrollSettings', JSON.stringify(DEFAULT_SETTINGS)));
  39. } catch (e) {
  40. console.log('Could not load settings, using defaults');
  41. scrollSettings = DEFAULT_SETTINGS;
  42. }
  43.  
  44. // DOM elements references
  45. let autoScrollPanel;
  46. let bubbleView;
  47. let toggleButton;
  48. let speedSlider;
  49. let speedValue;
  50.  
  51. // Create and inject the UI
  52. function createUI() {
  53. // Create a container for our elements with a unique ID
  54. const containerId = 'auto-scroll-container-' + Math.random().toString(36).substr(2, 9);
  55. const container = document.createElement('div');
  56. container.id = containerId;
  57. document.body.appendChild(container);
  58.  
  59. // Add isolated styles to avoid conflicts with page CSS
  60. const styleElement = document.createElement('style');
  61. styleElement.textContent = `
  62. #${containerId} {
  63. all: initial;
  64. font-family: Arial, sans-serif;
  65. color: white;
  66. font-size: 14px;
  67. }
  68. #${containerId} * {
  69. all: unset;
  70. box-sizing: border-box;
  71. }
  72. #${containerId} .auto-scroll-panel {
  73. position: fixed;
  74. bottom: 80px;
  75. right: 20px;
  76. background-color: rgba(0, 0, 0, 0.8);
  77. color: white;
  78. padding: 15px;
  79. border-radius: 8px;
  80. width: 220px;
  81. z-index: 9999999;
  82. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  83. font-family: Arial, sans-serif;
  84. display: ${scrollSettings.isMinimized ? 'none' : 'block'};
  85. }
  86. #${containerId} .panel-header {
  87. display: flex;
  88. justify-content: space-between;
  89. align-items: center;
  90. margin-bottom: 10px;
  91. border-bottom: 1px solid rgba(255, 255, 255, 0.2);
  92. padding-bottom: 8px;
  93. }
  94. #${containerId} .panel-title {
  95. font-weight: bold;
  96. font-size: 16px;
  97. display: block;
  98. }
  99. #${containerId} .minimize-button {
  100. background: none;
  101. border: none;
  102. color: white;
  103. cursor: pointer;
  104. font-size: 20px;
  105. padding: 0;
  106. margin: 0;
  107. display: block;
  108. }
  109. #${containerId} .section {
  110. margin-bottom: 12px;
  111. }
  112. #${containerId} .section-title {
  113. font-size: 14px;
  114. color: #ffcc00;
  115. margin-bottom: 8px;
  116. display: block;
  117. }
  118. #${containerId} .toggle-button {
  119. background-color: #2196F3;
  120. border: none;
  121. color: white;
  122. padding: 10px 15px;
  123. text-align: center;
  124. text-decoration: none;
  125. font-size: 14px;
  126. border-radius: 6px;
  127. width: 100%;
  128. cursor: pointer;
  129. display: flex;
  130. justify-content: center;
  131. align-items: center;
  132. gap: 8px;
  133. margin-bottom: 10px;
  134. }
  135. #${containerId} .toggle-button.active {
  136. background-color: #4CAF50;
  137. }
  138. #${containerId} .slider-container {
  139. width: 100%;
  140. display: flex;
  141. flex-direction: column;
  142. gap: 8px;
  143. }
  144. #${containerId} .slider-top {
  145. display: flex;
  146. justify-content: space-between;
  147. width: 100%;
  148. }
  149. #${containerId} .speed-value {
  150. font-weight: bold;
  151. }
  152. #${containerId} .speed-slider {
  153. width: 100%;
  154. height: 5px;
  155. -webkit-appearance: none;
  156. appearance: none;
  157. background: #444;
  158. outline: none;
  159. border-radius: 3px;
  160. display: block;
  161. }
  162. #${containerId} .speed-slider::-webkit-slider-thumb {
  163. -webkit-appearance: none;
  164. appearance: none;
  165. width: 15px;
  166. height: 15px;
  167. border-radius: 50%;
  168. background: #ffcc00;
  169. cursor: pointer;
  170. }
  171. #${containerId} .speed-slider::-moz-range-thumb {
  172. width: 15px;
  173. height: 15px;
  174. border-radius: 50%;
  175. background: #ffcc00;
  176. cursor: pointer;
  177. }
  178. #${containerId} .slider-labels {
  179. display: flex;
  180. justify-content: space-between;
  181. width: 100%;
  182. }
  183. #${containerId} .auto-scroll-bubble {
  184. position: fixed;
  185. bottom: 80px;
  186. right: 20px;
  187. width: 60px;
  188. height: 60px;
  189. border-radius: 50%;
  190. background-color: rgba(0, 0, 0, 0.8);
  191. display: ${scrollSettings.isMinimized ? 'flex' : 'none'};
  192. flex-direction: column;
  193. justify-content: center;
  194. align-items: center;
  195. cursor: pointer;
  196. z-index: 9999999;
  197. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
  198. font-size: 24px;
  199. }
  200. `;
  201. document.head.appendChild(styleElement);
  202.  
  203. // Create the main panel
  204. autoScrollPanel = document.createElement('div');
  205. autoScrollPanel.className = 'auto-scroll-panel';
  206. container.appendChild(autoScrollPanel);
  207.  
  208. // Create panel header with title and minimize button
  209. const panelHeader = document.createElement('div');
  210. panelHeader.className = 'panel-header';
  211.  
  212. const panelTitle = document.createElement('div');
  213. panelTitle.className = 'panel-title';
  214. panelTitle.textContent = 'Auto-Scroll Controls';
  215.  
  216. const minimizeButton = document.createElement('button');
  217. minimizeButton.className = 'minimize-button';
  218. minimizeButton.textContent = '−';
  219. minimizeButton.addEventListener('click', toggleMinimize);
  220.  
  221. panelHeader.appendChild(panelTitle);
  222. panelHeader.appendChild(minimizeButton);
  223. autoScrollPanel.appendChild(panelHeader);
  224.  
  225. // Create scrolling status section
  226. const statusSection = document.createElement('div');
  227. statusSection.className = 'section';
  228.  
  229. const statusTitle = document.createElement('div');
  230. statusTitle.className = 'section-title';
  231. statusTitle.textContent = 'Scrolling Status';
  232.  
  233. toggleButton = document.createElement('button');
  234. toggleButton.className = 'toggle-button' + (scrollSettings.isActive ? ' active' : '');
  235. toggleButton.innerHTML = scrollSettings.isActive ?
  236. '<span>⏸️</span> Pause Scrolling' :
  237. '<span>▶️</span> Start Scrolling';
  238. toggleButton.addEventListener('click', toggleScrolling);
  239.  
  240. statusSection.appendChild(statusTitle);
  241. statusSection.appendChild(toggleButton);
  242. autoScrollPanel.appendChild(statusSection);
  243.  
  244. // Create speed control section
  245. const speedSection = document.createElement('div');
  246. speedSection.className = 'section';
  247.  
  248. const speedTitle = document.createElement('div');
  249. speedTitle.className = 'section-title';
  250. speedTitle.textContent = 'Scroll Speed';
  251.  
  252. const sliderContainer = document.createElement('div');
  253. sliderContainer.className = 'slider-container';
  254.  
  255. const sliderTop = document.createElement('div');
  256. sliderTop.className = 'slider-top';
  257.  
  258. const speedLabel = document.createElement('span');
  259. speedLabel.textContent = 'Speed:';
  260.  
  261. speedValue = document.createElement('span');
  262. speedValue.className = 'speed-value';
  263. updateSpeedDisplay(scrollSettings.speed);
  264.  
  265. sliderTop.appendChild(speedLabel);
  266. sliderTop.appendChild(speedValue);
  267.  
  268. speedSlider = document.createElement('input');
  269. speedSlider.type = 'range';
  270. speedSlider.className = 'speed-slider';
  271. speedSlider.min = '1';
  272. speedSlider.max = '5';
  273. speedSlider.value = scrollSettings.speed;
  274. speedSlider.addEventListener('input', handleSpeedChange);
  275.  
  276. const sliderLabels = document.createElement('div');
  277. sliderLabels.className = 'slider-labels';
  278.  
  279. const slowLabel = document.createElement('span');
  280. slowLabel.textContent = 'Slow';
  281.  
  282. const fastLabel = document.createElement('span');
  283. fastLabel.textContent = 'Fast';
  284.  
  285. sliderLabels.appendChild(slowLabel);
  286. sliderLabels.appendChild(fastLabel);
  287.  
  288. sliderContainer.appendChild(sliderTop);
  289. sliderContainer.appendChild(speedSlider);
  290. sliderContainer.appendChild(sliderLabels);
  291.  
  292. speedSection.appendChild(speedTitle);
  293. speedSection.appendChild(sliderContainer);
  294. autoScrollPanel.appendChild(speedSection);
  295.  
  296. // Create bubble view (minimized version)
  297. bubbleView = document.createElement('div');
  298. bubbleView.className = 'auto-scroll-bubble';
  299. bubbleView.innerHTML = '📜';
  300. bubbleView.addEventListener('click', toggleMinimize);
  301. container.appendChild(bubbleView);
  302.  
  303. // Start scrolling if it was active before
  304. if (scrollSettings.isActive) {
  305. startScrolling();
  306. }
  307. }
  308.  
  309. // Toggle between expanded panel and minimized bubble
  310. function toggleMinimize() {
  311. scrollSettings.isMinimized = !scrollSettings.isMinimized;
  312.  
  313. if (scrollSettings.isMinimized) {
  314. autoScrollPanel.style.display = 'none';
  315. bubbleView.style.display = 'flex';
  316. } else {
  317. autoScrollPanel.style.display = 'block';
  318. bubbleView.style.display = 'none';
  319. }
  320.  
  321. saveSettings();
  322. }
  323.  
  324. // Toggle scrolling on/off
  325. function toggleScrolling() {
  326. scrollSettings.isActive = !scrollSettings.isActive;
  327.  
  328. if (scrollSettings.isActive) {
  329. // Start scrolling
  330. toggleButton.classList.add('active');
  331. toggleButton.innerHTML = '<span>⏸️</span> Pause Scrolling';
  332. startScrolling();
  333. } else {
  334. // Pause scrolling
  335. toggleButton.classList.remove('active');
  336. toggleButton.innerHTML = '<span>▶️</span> Start Scrolling';
  337. stopScrolling();
  338. }
  339.  
  340. saveSettings();
  341. }
  342.  
  343. // Handle speed slider changes
  344. function handleSpeedChange() {
  345. const value = parseInt(speedSlider.value);
  346. scrollSettings.speed = value;
  347. updateSpeedDisplay(value);
  348.  
  349. // If currently scrolling, update the scroll speed
  350. if (isScrolling) {
  351. stopScrolling();
  352. startScrolling();
  353. }
  354.  
  355. saveSettings();
  356. }
  357.  
  358. // Update speed display text
  359. function updateSpeedDisplay(value) {
  360. let speedText;
  361.  
  362. if (value === 1) speedText = "Very Slow";
  363. else if (value === 2) speedText = "Slow";
  364. else if (value === 3) speedText = "Medium";
  365. else if (value === 4) speedText = "Fast";
  366. else speedText = "Very Fast";
  367.  
  368. speedValue.textContent = `${speedText} (${value})`;
  369. }
  370.  
  371. // Start auto-scrolling
  372. function startScrolling() {
  373. if (isScrolling) return;
  374.  
  375. isScrolling = true;
  376. lastScrollPosition = window.scrollY;
  377.  
  378. // Calculate scroll speed based on setting (1-5)
  379. // The base speed (pixelsPerSecond) is multiplied by a factor based on the speed setting
  380. const speedFactors = [0.5, 0.75, 1, 1.5, 2.5]; // Factors for speeds 1-5
  381. const pixelsPerSecond = DEFAULT_SETTINGS.pixelsPerSecond * speedFactors[scrollSettings.speed - 1];
  382.  
  383. // Convert pixels per second to interval delay in milliseconds
  384. // We'll scroll 1px at a time for smoothness
  385. const scrollDelay = Math.floor(1000 / pixelsPerSecond);
  386.  
  387. // Start scroll interval
  388. scrollIntervalId = setInterval(function() {
  389. // Only scroll if user isn't manually scrolling
  390. if (!userScrollDetected) {
  391. window.scrollBy(0, 1);
  392. }
  393. }, scrollDelay);
  394. }
  395.  
  396. // Stop auto-scrolling
  397. function stopScrolling() {
  398. if (!isScrolling) return;
  399.  
  400. isScrolling = false;
  401. if (scrollIntervalId !== null) {
  402. clearInterval(scrollIntervalId);
  403. scrollIntervalId = null;
  404. }
  405. }
  406.  
  407. // Save settings using Tampermonkey's storage
  408. function saveSettings() {
  409. try {
  410. GM_setValue('autoScrollSettings', JSON.stringify(scrollSettings));
  411. } catch (e) {
  412. console.error('Failed to save settings:', e);
  413. }
  414. }
  415.  
  416. // Handle user manual scrolling
  417. function handleUserScroll() {
  418. const currentPosition = window.scrollY;
  419.  
  420. // If position changed significantly (more than what our auto-scroll would do)
  421. // and we're currently auto-scrolling, pause temporarily
  422. if (isScrolling && Math.abs(currentPosition - lastScrollPosition) > 3) {
  423. userScrollDetected = true;
  424.  
  425. // Clear existing timeout if there is one
  426. if (userScrollTimeout) {
  427. clearTimeout(userScrollTimeout);
  428. }
  429.  
  430. // Set timeout to resume auto-scrolling after user stops scrolling
  431. userScrollTimeout = setTimeout(function() {
  432. userScrollDetected = false;
  433. lastScrollPosition = window.scrollY;
  434. }, 5000); // 5-second delay before resuming
  435. }
  436.  
  437. lastScrollPosition = currentPosition;
  438. }
  439.  
  440. // Only create UI if we're on a page with scrollable content
  441. if (document.body && document.body.scrollHeight > window.innerHeight) {
  442. createUI();
  443.  
  444. // Add scroll event listener to detect manual user scrolling
  445. window.addEventListener('scroll', handleUserScroll, { passive: true });
  446.  
  447. // Clean up when page is unloaded
  448. window.addEventListener('beforeunload', function() {
  449. if (scrollIntervalId !== null) {
  450. clearInterval(scrollIntervalId);
  451. }
  452. });
  453. }
  454. }
  455. })();