Strava Text Auto-Selector

Automatically selects text in specific Strava elements and displays a notification near the cursor. Also allows right-click to copy text.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Strava Text Auto-Selector
// @namespace    typpi.online
// @version      1.0.9
// @description  Automatically selects text in specific Strava elements and displays a notification near the cursor. Also allows right-click to copy text.
// @author       Nick2bad4u
// @license      UnLicense
// @homepageURL  https://github.com/Nick2bad4u/UserScripts
// @grant        none
// @include      *://*.strava.com/activities/*
// @include      *://*.strava.com/athlete/training
// @icon         https://www.google.com/s2/favicons?sz=64&domain=strava.com
// ==/UserScript==

(function () {
	'use strict';

	// Log when the script starts
	console.log('Strava Text Auto-Selector script loaded.');

	// Wait for the page to fully load
	window.addEventListener('load', function () {
		console.log('Page fully loaded, initializing script.');

		// Delay script execution for 500 ms
		setTimeout(initializeScript, 500);
	});

	function initializeScript() {
		console.log('Initializing script after delay.');

		const selectors = [
			'#search-results > tbody > tr:nth-child(n) > td.view-col.col-title > a',
			'.summaryGrid .summaryGridDataContainer, .inline-stats strong, .inline-stats b',
			'#heading > div > div.row.no-margins.activity-summary-container > div.spans8.activity-summary.mt-md.mb-md > div.details-container > div > h1',
			'.ride .segment-effort-detail .effort-details table, .swim .segment-effort-detail .effort-details table',
			'.activity-description p:only-child',
			'.activity-description p:first-child',
		];
		const summarySelector = '.summaryGridDataContainer';

		// Function to add the event listener to target elements
		function addContextMenuListener(element) {
			console.log('Adding right-click event listener to element:', element);

			element.addEventListener('contextmenu', function (event) {
				console.log('Right-click detected on element:', element);

				event.preventDefault();
				console.log('Default right-click menu prevented.');

				const range = document.createRange();
				if (element.classList.contains('summaryGridDataContainer')) {
					const textNode = element.childNodes[0];
					range.selectNodeContents(textNode);
					console.log('Text range selected:', textNode.textContent);
				} else {
					range.selectNodeContents(element);
					console.log('Text range selected:', element.textContent);
				}

				const selection = window.getSelection();
				selection.removeAllRanges();
				selection.addRange(range);
				console.log('Text added to selection.');

				const copiedText = selection.toString();
				console.log('Text copied to clipboard:', copiedText);

				navigator.clipboard
					.writeText(copiedText)
					.then(() => {
						console.log('Clipboard write successful.');
						showNotification(event.clientX, event.clientY, 'Text Copied!');
					})
					.catch(() => {
						console.log('Clipboard write failed.');
						showNotification(event.clientX, event.clientY, 'Failed to Copy!');
					});
			});
		}

		// Query elements and add event listeners initially for the first three selectors
		selectors.forEach((selector) => {
			const elements = document.querySelectorAll(selector);
			console.log(`Found ${elements.length} elements for selector: ${selector}`);
			elements.forEach(addContextMenuListener);
		});

		// Function to handle the summaryGridDataContainer elements separately
		function handleSummaryGridDataContainer() {
			const elements = document.querySelectorAll(summarySelector);
			console.log(`Found ${elements.length} elements for selector: ${summarySelector}`);
			elements.forEach(addContextMenuListener);
		}

		// MutationObserver to detect changes in the DOM and add event listeners to new summaryGridDataContainer elements
		const observer = new MutationObserver((mutations) => {
			mutations.forEach((mutation) => {
				mutation.addedNodes.forEach((node) => {
					if (node.nodeType === Node.ELEMENT_NODE) {
						if (node.matches(summarySelector)) {
							addContextMenuListener(node);
						}
						node.querySelectorAll(summarySelector).forEach(addContextMenuListener);
					}
				});
			});
		});

		observer.observe(document.body, {
			childList: true,
			subtree: true,
		});
		console.log('MutationObserver set up to monitor the DOM for summaryGridDataContainer.');

		// Handle existing summaryGridDataContainer elements initially
		handleSummaryGridDataContainer();
	}

	function showNotification(x, y, message) {
		console.log('Displaying notification:', message);

		const notification = document.createElement('div');
		notification.textContent = message;
		notification.style.position = 'absolute';
		notification.style.left = `${x + 10}px`;
		notification.style.top = `${y + 10}px`;
		notification.style.background = 'rgba(0, 0, 0, 0.8)';
		notification.style.color = 'white';
		notification.style.padding = '5px 10px';
		notification.style.borderRadius = '5px';
		notification.style.fontSize = '12px';
		notification.style.zIndex = '1000';
		notification.style.pointerEvents = 'none';

		document.body.appendChild(notification);
		console.log('Notification added to DOM.');

		setTimeout(() => {
			notification.remove();
			console.log('Notification removed from DOM.');
		}, 2000);
	}
})();