-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathKudoAll-Strava-Garmin.user.js
More file actions
684 lines (617 loc) · 30.4 KB
/
KudoAll-Strava-Garmin.user.js
File metadata and controls
684 lines (617 loc) · 30.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
// ==UserScript==
// @name Strava and Garmin Kudos All (Working)
// @namespace nick2bad4u.github.io
// @version 2.3
// @description Adds a button to give kudos to all visible activities on Strava and Garmin Connect.
// @author Nick2bad4u
// @license Unlicense
// @homepage https://github.com/Nick2bad4u/UserStyles/
// @homepageURL https://github.com/Nick2bad4u/UserStyles/
// @grant none
// @run-at document-end
// @match https://www.strava.com/*
// @match https://connect.garmin.com/*
// @icon https://i.gyazo.com/e2fabcfc9e9fd6d011e98215764c109c.png
// @tag garmin
// @tag strava
// @downloadURL https://update.greasyfork.org/scripts/520059/Strava%20and%20Garmin%20Kudos%20All%20%28Working%29.user.js
// @updateURL https://update.greasyfork.org/scripts/520059/Strava%20and%20Garmin%20Kudos%20All%20%28Working%29.meta.js
// ==/UserScript==
(function () {
'use strict';
// Function to get localized message
function getMessage(messageName, substitutions) {
const messages = {
en: { kudo_all: 'Kudo All' },
es: { kudo_all: 'Dar Kudos a Todos' },
fr: { kudo_all: 'Féliciter Tout le Monde' },
de: { kudo_all: 'Allen Kudos geben' },
it: { kudo_all: 'Dai Kudos a Tutti' },
pt: { kudo_all: 'Dar Kudos a Todos' },
nl: { kudo_all: 'Geef Kudos aan Iedereen' },
ru: { kudo_all: 'Похвалить всех' },
zh: { kudo_all: '赞所有' },
ja: { kudo_all: '全員にKudos' },
ko: { kudo_all: '모두에게 칭찬하기' },
ar: { kudo_all: 'إعطاء Kudos للجميع' },
hi: { kudo_all: 'सभी को कुडोस दें' },
bn: { kudo_all: 'সবাইকে কুডোস দিন' },
ta: { kudo_all: 'அனைவருக்கும் குடோஸ் கொடுக்கவும்' },
kn: { kudo_all: 'ಎಲ್ಲರಿಗೂ ಕುದೋಸ್ ನೀಡಿ' },
mr: { kudo_all: 'सर्वांना कूडो द्या' },
pa: { kudo_all: 'ਸਭ ਨੂੰ ਕੂਡੋ ਦਿਓ' },
ml: { kudo_all: 'എല്ലാവരെയും കുഡോസ് നൽകുക' },
gu: { kudo_all: 'બધાને કૂડોસ આપો' },
ur: { kudo_all: 'سب کو کڈو دیں' },
vi: { kudo_all: 'Kudo Tất cả' },
zh_tw: { kudo_all: '給所有人Kudos' },
af: { kudo_all: 'Gee Kudos aan almal' },
zu: { kudo_all: 'Nike Kudos bonke' },
xh: { kudo_all: 'Nike Kudos bonke' },
he: { kudo_all: 'תן לכולם קודוס' },
id: { kudo_all: 'Berikan Kudos kepada Semua' },
ms: { kudo_all: 'Berikan Kudos kepada Semua' },
sw: { kudo_all: 'Toa Kudos kwa Wote' },
da: { kudo_all: 'Giv Kudos til Alle' },
no: { kudo_all: 'Gi Kudos til Alle' },
sv: { kudo_all: 'Ge Kudos till Alla' },
fi: { kudo_all: 'Anna Kudos kaikille' },
th: { kudo_all: 'ให้คุดดอกทุกคน' },
tr: { kudo_all: 'Herkese Kudos Ver' },
pl: { kudo_all: 'Pochwal wszystkich' },
cs: { kudo_all: 'Pošli Kudos všem' },
sk: { kudo_all: 'Pošli Kudos všetkým' },
hu: { kudo_all: 'Kudos Mindenkinek' },
ro: { kudo_all: 'Felicită pe Toată Lumea' },
hr: { kudo_all: 'Pošalji Kudos svima' },
sr: { kudo_all: 'Пошаљи Кудос свима' },
sl: { kudo_all: 'Pošlji Kudos vsem' },
et: { kudo_all: 'Saada Kudos kõigile' },
lv: { kudo_all: 'Sūtīt Kudos visiem' },
lt: { kudo_all: 'Siųsti Kudos visiems' },
bg: { kudo_all: 'Изпрати Кудос на всички' },
el: { kudo_all: 'Στείλτε Kudos σε όλους' },
uk: { kudo_all: 'Похвалити всіх' },
ka: { kudo_all: 'ყველას Kudos მიეცი' },
az: { kudo_all: 'Bütün Kudos göndər' },
kk: { kudo_all: 'Барлығына Kudos жіберу' },
tg: { kudo_all: 'Ба ҳама Kudos фиристед' },
tk: { kudo_all: 'Hemme Kudos iber' },
ky: { kudo_all: 'Бардыгына Kudos жөнөтүү' },
mn: { kudo_all: 'Бүгдэд Kudos илгээх' },
ne: { kudo_all: 'सबैलाई कूडोस दिनुहोस्' },
ku: { kudo_all: 'Herkese Kudos Bide' },
am: { kudo_all: 'ለሁሉም Kudos ይስጡ' },
ps: { kudo_all: 'ټولو ته Kudos ورکړئ' },
};
const userLanguage = (navigator.language || navigator.userLanguage || 'en').split('-')[0]; // Get the user's language
const messageSet = messages[userLanguage] || messages['en']; // Default to English if language not supported
return messageSet[messageName] || (typeof substitutions === 'string' ? substitutions : '') || messageName;
}
const Strava = {
getStravaContainer, // Retrieves the Strava container element
findStravaKudosButtons, // Finds Strava kudos buttons within a specified container or the entire document
createStravaFilter, // Creates a filter function for Strava activities based on an athlete's link
getStravaKudosButtons, // Retrieves Strava kudos buttons based on athlete link and activity filtering
createStravaButton, // Creates a Strava button element with a specified label
stravaKudoAllHandler, // Handles the event to click all Strava kudos buttons on the page
stravaStandBy, // Initiates the Strava standby mode
};
/**
* Retrieves the Strava container element.
*
* @returns {HTMLElement | null} The Strava container element, or null if not found.
*/
function getStravaContainer() {
const container = document.querySelector('[class="user-nav nav-group"]');
console.log('Strava: Found container:', container);
return container;
}
/**
* Finds Strava kudos buttons within a specified container or the entire document.
* These buttons are identified by their data-testid attributes for 'kudos_button' and 'unfilled_kudos'.
*
* @param {HTMLElement} [container] - The HTML element to search within. If not provided, the entire document is searched.
* @returns {HTMLElement[]} An array of HTML elements representing the Strava kudos buttons found.
*/
function findStravaKudosButtons(container) {
// Find Strava kudos buttons
const kudosButtonSelector = "button[data-testid='kudos_button']"; // Define the selector for the kudos button
const unfilledKudosSelector = "svg[data-testid='unfilled_kudos']"; // Define the selector for the unfilled kudos icon
const selector = `${kudosButtonSelector} > ${unfilledKudosSelector}`; // Combine the selectors
const buttons = container ? Array.from(container.querySelectorAll(selector)) : document.querySelectorAll(selector); // Find the kudos buttons
console.log('Strava: Found kudos buttons:', buttons); // Log the found kudos buttons
return buttons; // Return the found kudos buttons
}
/**
* Creates a filter function for Strava activities based on an athlete's link.
* The filter function checks if an activity item contains a link to the specified athlete.
* It utilizes caching to improve performance by storing the filter function and athlete link.
*
* @param {HTMLAnchorElement} athleteLink - The anchor element representing the athlete's link.
* @returns {function(HTMLElement): boolean} A filter function that returns true if the activity item does NOT contain a link to the specified athlete, false otherwise.
*/
let cachedAthleteLink = null; // Cache the athlete link
let cachedFilterFunction = null; // Cache the filter function
function createStravaFilter(athleteLink) {
// Create a filter function for Strava activities
if (!athleteLink) {
console.error('Strava: Athlete link is null or undefined.');
return () => false; // Return a default filter function
}
const url = new URL(athleteLink.href); // Get the athlete link URL
const href = url.pathname; // Get the pathname from the URL
if (cachedAthleteLink === href) {
// Check if the filter is already cached
console.log('Strava: Using cached filter for athlete link:', href); // Log the cached filter
return cachedFilterFunction; // Return the cached filter function
}
console.log('Strava: Filter created for athlete link:', href); // Log the new filter
cachedAthleteLink = href; // Cache the athlete link
cachedFilterFunction = (item) => !item.querySelector(`a[href^="${href}"]`); // Create the filter function
return cachedFilterFunction; // Return the filter function
}
/**
* Finds the athlete link on the page. It first attempts to find the link in the athlete profile section.
* If not found, it falls back to searching for any link that starts with '/athletes'.
* As a last resort, it searches for any link that contains '/athletes'.
*
* @returns {HTMLAnchorElement | null} The athlete link element if found, otherwise null.
*/
function findAthleteLink() {
// Find the athlete link
// Attempt to find the athlete link in the profile or fallback locations
let athleteLink = document.querySelector("#athlete-profile a[href^='/athletes']"); // Find the athlete link in the profile
if (!athleteLink) {
// If the athlete link is not found
athleteLink = document.querySelector("a[href^='/athletes']"); // Find the athlete link in the feed
console.log('Strava: Fallback athlete link:', athleteLink); // Log the fallback athlete link
if (!athleteLink) {
// If the fallback athlete link is not found
athleteLink = document.querySelector("a[href*='/athletes']"); // Find the second fallback athlete link
console.log('Strava: Second fallback athlete link:', athleteLink); // Log the second fallback athlete link
}
}
console.log('Strava: Athlete link:', athleteLink); // Log the athlete link
return athleteLink; // Return the athlete link
}
/**
* Finds Strava activities based on the presence of athlete links within feed entries.
*
* @returns {NodeListOf<HTMLAnchorElement>} A NodeList of anchor elements representing activities.
*/
function findActivities() {
// Find activities based on the athlete link
let activities = document.querySelectorAll("div[data-testid='web-feed-entry'] > div > div > div > a[href^='/athletes']"); // Find activities based on the athlete link
console.log('Strava: Found activities:', activities); // Log the found activities
return activities; // Return the found activities
}
/**
* Filters an array of activities based on a given athlete link.
*
* @param {string} athleteLink - The athlete link to filter activities by.
* @param {HTMLElement[]} activities - An array of activity elements to filter.
* @returns {HTMLElement[]} - A new array containing only the activities that match the athlete link.
*/
function filterActivities(athleteLink, activities) {
// Filter activities based on athlete link
activities = Array.from(activities).filter(createStravaFilter(athleteLink)); // Filter activities based on the athlete link
console.log('Strava: Filtered activities:', activities); // Log the filtered activities
return activities; // Return the filtered activities
}
/**
* Collects kudos buttons from a list of activities.
* It flattens the array of activities and finds Strava kudos buttons in each activity.
*
* @param {HTMLElement[]} activities - An array of HTML elements representing activities.
* @returns {HTMLButtonElement[]} - An array of HTML button elements representing the kudos buttons found in the activities.
*/
function collectKudosButtons(activities) {
// Collect kudos buttons from the filtered activities
const buttons = activities.flatMap((activity) => findStravaKudosButtons(activity)); // Collect kudos buttons from the filtered activities
console.log('Strava: Final kudos buttons:', buttons); // Log the final kudos buttons
return buttons; // Return the final kudos buttons
}
/**
* Retrieves Strava kudos buttons based on athlete link and activity filtering.
* It attempts to find kudos buttons associated with activities of a specific athlete.
* If athlete link or activities are not found, or if no activities remain after filtering,
* it falls back to the default method of finding Strava kudos buttons.
*
* @returns {NodeListOf<HTMLButtonElement> | undefined} A collection of kudos buttons,
* or undefined if no buttons are found.
*/
function getStravaKudosButtons() {
const athleteLink = findAthleteLink(); // Find the athlete link
// If athlete link is still not found, use the default kudos button finding method
if (!athleteLink) {
return findStravaKudosButtons(document); // Use the default kudos button finding method
}
const activities = findActivities(); // Find activities based on the athlete link
// If no activities found, use the default kudos button finding method
if (activities.length < 1) {
return findStravaKudosButtons(); // Use the default kudos button finding method
}
const filteredActivities = filterActivities(athleteLink, activities); // Filter activities based on the athlete link
// If no activities left after filtering, use the default kudos button finding method
if (filteredActivities.length < 1) {
return findStravaKudosButtons(); // Use the default kudos button finding method
}
return collectKudosButtons(filteredActivities); // Collect kudos buttons from the filtered activities
}
/**
* Creates a Strava button element with a specified label.
*
* @returns {HTMLLIElement} The created Strava button as an `li` element.
*/
function createStravaButton() {
const label = getMessage('kudo_all', 'Kudo All'); // Get the localized message for the button label
console.log('Strava: Creating button with label:', label); // Log the button label
const navItemLi = document.createElement('li'); // Create the button element
const navItemA = document.createElement('a'); // Create the button link element
navItemLi.className = 'nav-item'; // Set the button class
navItemLi.style.marginRight = '10px'; // Set the button margin
navItemA.href = '#'; // Set the button link href
navItemA.className = 'btn btn-default btn-sm empty'; // Set the button link class
const navItemIcon = document.createElement('span'); // Create the button icon element
navItemIcon.className = 'app-icon icon-kudo'; // Set the button icon class
navItemIcon.style.marginRight = '5px'; // Set the button icon margin
const navItemText = document.createElement('span'); // Create the button text element
navItemText.className = 'ka-progress text-caption1'; // Set the button text class
navItemText.textContent = label;
navItemA.append(navItemIcon); // Append the icon to the button link
navItemA.append(navItemText); // Append the text to the button link
navItemLi.append(navItemA); // Append the link to the button
// Add hover effect
navItemA.addEventListener('mouseover', () => {
navItemA.style.backgroundColor = '#2ea44f'; // Change background color on hover
navItemA.style.color = 'white'; // Change text color on hover
navItemA.style.transform = 'scale(1.1)'; // Slightly enlarge the button on hover
navItemA.style.transition = 'all 0.3s ease'; // Smooth transition for the effects
navItemA.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)'; // Add a shadow effect
});
navItemA.addEventListener('mouseout', () => {
navItemA.style.backgroundColor = ''; // Reset background color
navItemA.style.color = ''; // Reset text color
navItemA.style.transform = 'scale(1)'; // Reset size
navItemA.style.boxShadow = ''; // Remove shadow
});
// Add click effect
navItemA.addEventListener('click', () => {
navItemA.style.transform = 'scale(0.95)'; // Shrink the button slightly on click
navItemA.style.opacity = '0.9'; // Slightly fade the button on click
setTimeout(() => {
navItemA.style.transform = 'scale(1)'; // Reset size after click effect
navItemA.style.opacity = '1'; // Reset opacity after click effect
}, 150); // Duration of the click effect
});
return navItemLi; // Return the button element
}
/**
* Handles the event to click all Strava kudos buttons on the page.
*
* @param {Event} event - The event that triggered the handler (e.g., a button click).
* @returns {void}
*/
function stravaKudoAllHandler(event) {
// Handle the event to click all kudos buttons
event.preventDefault(); // Prevent the default event behavior
const icons = getStravaKudosButtons(); // Get the Strava kudos buttons
console.log('Strava: Clicking all kudos buttons, count:', icons.length); // Log the number of kudos buttons
let likedCount = 0; // Counter for the number of items liked
icons.forEach((item) => {
// Click all kudos buttons
const parentItem = item?.parentElement; // Get the parent element of the kudos button
if (parentItem) {
// If the parent element exists
parentItem.click(); // Click the parent element
likedCount++; // Increment the counter
}
});
// Show a popup with the number of items liked
showPopup(`You gave kudos 👍 to ${likedCount} activities!`);
}
/**
* Initiates the Strava standby mode, which checks for the existence of a Kudo All button.
* If the button does not exist, it creates and prepends the button to the Strava container.
* The button is then attached with a click event listener to the stravaKudoAllHandler function.
*/
function stravaStandBy() {
// Initiate the Strava standby mode
console.log('Strava: Standby initiated'); // Log the initiation of the standby mode
const buttonExisted = document.querySelector('div[class="ka-progress text-caption1"]'); // Check if the button already exists
if (!buttonExisted) {
// If the button does not exist
console.log('Strava: Button does not exist, creating'); // Log the creation of the button
const container = getStravaContainer(); // Get the Strava container
if (container) {
// If the container exists
const button = createStravaButton(); // Create the button
container.prepend(button); // Prepend the button to the container
button.addEventListener('click', stravaKudoAllHandler); // Add a click event listener to the button
}
}
}
/**
* @namespace GC
* @description Namespace containing functions related to Garmin Connect integration.
* @property {function} getGarminContainer - Retrieves the Garmin container element.
* @property {function} findGarminKudosButtons - Finds Garmin kudos buttons within a specified container or the entire document.
* @property {function} createGarminButton - Creates a new kudos button for Garmin.
* @property {function} garminKudoAllHandler - Handles the "kudo all" action on Garmin.
* @property {function} executeGarmin - Executes the Garmin integration logic.
* @property {function} garminConnectStandBy - Sets up a standby observer for Garmin Connect.
*/
const GC = {
getGarminContainer, // Retrieves the Garmin container element
findGarminKudosButtons, // Finds Garmin kudos buttons within a specified container or the entire document
createGarminButton, // Creates a new kudos button for Garmin
garminKudoAllHandler, // Handles the "kudo all" action on Garmin
executeGarmin, // Executes the Garmin integration logic
garminConnectStandBy, // Sets up a standby observer for Garmin Connect
};
/**
* Retrieves the Garmin container element where the Kudo All button will be prepended.
* It searches for a div with the class "header-nav", creates a new div element with specific classes and styles,
* prepends it to the found container, and returns the newly created div element.
*
* @returns {HTMLElement|null} The newly created Kudo All navigation item element, or null if the container is not found.
*/
function getGarminContainer() {
// Get the Garmin container
const container = document.querySelector('div[class="header-nav"]'); // Find the Garmin container
console.log('Garmin: Found container:', container); // Log the found container
if (container) {
const el = document.createElement('div'); // Create the new div element
el.classList.add('kudo-all-nav-item', 'header-nav-item'); // Add classes to the new div element
el.style.height = '60px'; // Set the height of the new div element
el.style.width = '50px'; // Set the width of the new div element
container.prepend(el); // Prepend the new div element to the container
return document.querySelector('div[class^=kudo-all-nav-item]'); // Return the new div element
}
return null; // Return null if the container is not found
}
/**
* Finds Garmin kudos buttons within a specified container or the entire document.
*
* @param {HTMLElement} [container] - The container element to search within. If not provided, the entire document is searched.
* @returns {HTMLElement[]} An array of HTMLElement objects representing the Garmin kudos buttons found.
*/
function findGarminKudosButtons(container) {
// Find Garmin kudos buttons
const selector = `
button[class^="CommentLikeSection_socialIconWrapper"] >
div[class*="CommentLikeSection_animateBox"] >
i[class*=icon-heart-inverted]
`; // Define the selector for the kudos buttons
const buttons = container ? Array.from(container.querySelectorAll(selector)) : Array.from(document.querySelectorAll(selector)); // Find the kudos buttons
console.log('Garmin: Found kudos buttons:', buttons); // Log the found kudos buttons
return buttons; // Return the found kudos buttons
}
/**
* Creates a Garmin-style button element.
*
* @returns {HTMLAnchorElement} The created Garmin-style button element.
*/
function createGarminButton() {
// Create a Garmin-style button
const label = getMessage('kudo_all', 'Kudo All'); // Get the localized message for the button label
console.log('Garmin: Creating button with label:', label); // Log the button label
const link = document.createElement('a'); // Create the button element
link.href = '#'; // Set the button link href
link.className = 'header-nav-link icon-heart-inverted'; // Set the button link class
link.setAttribute('aria-label', label); // Set the button link aria-label
link.setAttribute('data-original-title', label); // Set the button link data-original-title
link.setAttribute('data-rel', 'tooltip'); // Set the button link data-rel
// Add hover effect to fill the heart red
link.addEventListener('mouseover', () => {
link.style.color = 'red'; // Change the heart color to red on hover
link.style.transform = 'scale(1.2)'; // Slightly enlarge the heart on hover
link.style.transition = 'all 0.3s ease'; // Smooth transition for the effects
link.style.boxShadow = '0 4px 8px rgba(255, 0, 0, 0.5)'; // Add a glowing red shadow
});
link.addEventListener('mouseout', () => {
link.style.color = ''; // Reset the heart color when not hovering
link.style.transform = 'scale(1)'; // Reset the size when not hovering
link.style.boxShadow = ''; // Remove the shadow when not hovering
});
// Add click effect to briefly shrink the heart
link.addEventListener('click', () => {
link.style.transform = 'scale(0.9)'; // Shrink the heart slightly on click
link.style.opacity = '0.8'; // Slightly fade the heart on click
setTimeout(() => {
link.style.transform = 'scale(1)'; // Reset the size after the click effect
link.style.opacity = '1'; // Reset the opacity after the click effect
}, 150); // Duration of the click effect
});
// Add a pulsating animation effect
const pulsateKeyframes = `
@keyframes pulsate {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
`;
const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerText = pulsateKeyframes;
document.head.appendChild(styleSheet);
link.addEventListener('mouseenter', () => {
link.style.animation = 'pulsate 1s infinite'; // Start pulsating on hover
});
link.addEventListener('mouseleave', () => {
link.style.animation = ''; // Stop pulsating when not hovering
});
return link;
}
/**
* Handles the event to give all kudos on the Garmin newsfeed page.
* Prevents default event behavior, checks if the current page is the newsfeed,
* finds all kudos buttons, and clicks them.
*
* @param {Event} event - The event that triggered the handler.
* @returns {void}
*/
function garminKudoAllHandler(event) {
// Handle the event to give all kudos
event.preventDefault(); // Prevent the default event behavior
if (window.location.pathname !== '/app/newsfeed') {
// Check if the current page is the newsfeed
console.log('Garmin: Not on the newsfeed page, redirecting'); // Log the redirect message
window.location.href = 'https://connect.garmin.com/app/newsfeed'; // Redirect to the newsfeed page
return; // Abort the function
}
const icons = findGarminKudosButtons(); // Find all kudos buttons
console.log('Garmin: Clicking all kudos buttons, count:', icons.length); // Log the number of kudos buttons
let likedCount = 0; // Counter for the number of items liked
icons.forEach((item) => {
// Click all kudos buttons
if (item) {
item.click(); // Click the kudos button
likedCount++; // Increment the counter
}
});
// Show a popup with the number of items liked
showPopup(`You gave hearts ❤️ to ${likedCount} activities!`);
}
/**
* Executes the Garmin functionality by injecting a "Kudo All" button into the Garmin page.
* It first retrieves the container element where the button will be appended.
* If the container is found, it creates the button, appends it to the container,
* and attaches a click event listener to the button that triggers the garminKudoAllHandler function.
*/
function executeGarmin() {
// Execute the Garmin functionality
console.log('Garmin: Execute function called'); // Log the execution of the function
const container = getGarminContainer(); // Get the Garmin container
if (container) {
// If the container exists
const button = createGarminButton(); // Create the button
container.append(button); // Append the button to the container
button.addEventListener('click', garminKudoAllHandler); // Add a click event listener to the button
}
}
/**
* @function garminConnectStandBy
* @description Initiates a standby mode for Garmin Connect, observing the DOM for the appearance of a specific element (`header-nav`) to trigger the execution of `executeGarmin`.
* @listens MutationObserver
* @returns {void}
*/
function garminConnectStandBy() {
// Initiate the Garmin standby mode
console.log('Garmin: Standby initiated'); // Log the initiation of the standby mode
let loaded = false; // Set the loaded flag to false
var observer = new MutationObserver(function (mutations) {
// Create a new mutation observer
mutations.forEach(function (mutation) {
// For each mutation
if (mutation.addedNodes.length > 0) {
// If nodes are added
mutation.addedNodes.forEach((node) => {
if (
// If the target element is loaded
!loaded && // Ensure it's not already loaded
node.nodeType === 1 && // Ensure it's an element
node.className && // Ensure it has a class name
typeof node.className === 'string' && // Ensure the class name is a string
node.className.startsWith('header-nav') // Ensure the class name starts with 'header-nav'
) {
console.log('Garmin: Target element loaded, executing...'); // Log the execution of the function
loaded = true; // Set the loaded flag to true
executeGarmin(); // Execute the Garmin functionality
observer.disconnect(); // Disconnect the observer
}
});
}
});
});
if (!loaded) {
observer.observe(document.body, {
// Observe the body for mutations
childList: true, // Observe child nodes
subtree: true, // Observe all descendants
});
}
}
// Check if the current host is Strava
/**
* Checks if the current hostname matches a Strava domain pattern.
*
* @returns {boolean} True if the hostname is a Strava domain, false otherwise.
*/
function isHostStrava() {
// Check if the current host is Strava
const currentHostname = window.location.hostname; // Get the current hostname
const stravaDomainPattern = /^.*\.strava\.com$/; // Define the Strava domain pattern
const isStrava = stravaDomainPattern.test(currentHostname); // Check if the current hostname matches the Strava domain pattern
console.log('Host check: Is Strava?', isStrava); // Log the result of the check
return isStrava; // Return the result of the check
}
// Check if the current host is Garmin
/**
* Checks if the current hostname matches a Garmin domain pattern.
*
* @returns {boolean} True if the hostname is a Garmin domain, false otherwise.
*/
function isHostGarmin() {
// Check if the current host is Garmin Connect
const currentHostname = window.location.hostname; // Get the current hostname
const garminDomainPattern = /^.*\.garmin\.com$/; // Define the Garmin domain pattern
const isGarmin = garminDomainPattern.test(currentHostname); // Check if the current hostname matches the Garmin domain pattern 444444444444444442
console.log('Host check: Is Garmin?', isGarmin); // Log the result of the check
return isGarmin; // Return the result of the check
}
// Initialize script on window load
window.onload = function () {
console.log('Kudo All script initialization started.');
/**
* Checks if the current host is Strava.
*
* @returns {boolean} True if the current host is Strava, false otherwise.
*/
const isStravaHost = isHostStrava(); // Check if the current host is Strava
const isGarminHost = isHostGarmin(); // Check if the current host is Garmin Connect
if (isStravaHost) {
// If the current host is Strava
console.log('Detected Strava domain. Initiating Strava standby...'); // Log the initiation of the Strava standby mode
Strava.stravaStandBy(); // Initiate the Strava standby mode
} else if (isGarminHost) {
// If the current host is Garmin Connect
console.log('Detected Garmin domain. Initiating Garmin standby...'); // Log the initiation of the Garmin standby mode
GC.garminConnectStandBy(); // Initiate the Garmin standby mode
} else {
// If the current host is not recognized as Strava or Garmin
console.log('Domain not recognized as Strava or Garmin. No actions will be performed.'); // Log the unrecognized domain
}
};
/**
* Displays a popup message on the page.
*
* @param {string} message - The message to be displayed in the popup.
*/
function showPopup(message) {
// Create a popup element
const popup = document.createElement('div');
// Set the popup message
popup.textContent = message;
// Set the popup styles
popup.style = `
position: fixed;
top: 20px;
right: 20px;
background-color: #2ea44f;
color: white;
padding: 10px 20px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
z-index: 1000;
font-size: 16px;
`;
// Append the popup to the body
document.body.appendChild(popup);
// Automatically remove the popup after 3 seconds
setTimeout(() => {
popup.remove();
}, 3000);
}
})();