Last active
June 16, 2026 10:54
-
-
Save CodeAlDente/cd0351ba38aa71ca5865ebc01de84024 to your computer and use it in GitHub Desktop.
Adds a toggle button to display Netcup Deals prices as monthly or yearly amounts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Netcup Deals Yearly Price Toggle | |
| // @author CodeAlDente (https://github.com/CodeAlDente) | |
| // @namespace https://gist.github.com/CodeAlDente/cd0351ba38aa71ca5865ebc01de84024 | |
| // @version 1.1.0 | |
| // @description Adds a toggle button to the Netcup Deals page that switches displayed prices between monthly and yearly values by multiplying monthly prices by 12 and changing the billing period accordingly. | |
| // @match https://www.netcup.com/de/deals* | |
| // @match https://www.netcup.com/en/deals* | |
| // @run-at document-idle | |
| // @grant none | |
| // @license MIT | |
| // @homepageURL https://gist.github.com/CodeAlDente/cd0351ba38aa71ca5865ebc01de84024 | |
| // @supportURL https://gist.github.com/CodeAlDente/cd0351ba38aa71ca5865ebc01de84024 | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| let yearlyMode = false; | |
| const isEnglish = location.pathname.startsWith('/en/'); | |
| function parsePrice(text) { | |
| let value = text.replace(/[^\d.,]/g, ''); | |
| if (value.includes(',') && value.includes('.')) { | |
| if (value.lastIndexOf(',') > value.lastIndexOf('.')) { | |
| // German: 1.234,56 | |
| value = value.replace(/\./g, '').replace(',', '.'); | |
| } else { | |
| // English: 1,234.56 | |
| value = value.replace(/,/g, ''); | |
| } | |
| } else if (value.includes(',')) { | |
| // German: 0,13 | |
| value = value.replace(',', '.'); | |
| } | |
| return parseFloat(value); | |
| } | |
| function formatPrice(value) { | |
| if (isEnglish) { | |
| return '€' + value.toFixed(2); | |
| } | |
| return value.toFixed(2).replace('.', ',') + ' €'; | |
| } | |
| function animatePriceChange(element) { | |
| element.style.transition = 'opacity 0.2s ease'; | |
| element.style.opacity = '0.4'; | |
| requestAnimationFrame(() => { | |
| setTimeout(() => { | |
| element.style.opacity = '1'; | |
| }, 50); | |
| }); | |
| } | |
| function updateCards() { | |
| document | |
| .querySelectorAll('.deals-container .deal-card-container') | |
| .forEach(card => { | |
| const footer = card.querySelector('.card-footer'); | |
| if (!footer) return; | |
| const firstDiv = footer.querySelector(':scope > div:first-child'); | |
| if (!firstDiv) return; | |
| const periodDiv = firstDiv.querySelector(':scope > div:nth-child(2)'); | |
| if (!periodDiv) return; | |
| const periodText = periodDiv.textContent.trim(); | |
| const isGermanPeriod = | |
| periodText === 'pro Monat' || | |
| periodText === 'pro Jahr'; | |
| const isEnglishPeriod = | |
| periodText === 'per month' || | |
| periodText === 'per year'; | |
| if (!isGermanPeriod && !isEnglishPeriod) { | |
| return; | |
| } | |
| const priceDiv = firstDiv.querySelector('.text-price'); | |
| if (!priceDiv) return; | |
| if (!priceDiv.dataset.originalPrice) { | |
| priceDiv.dataset.originalPrice = priceDiv.textContent.trim(); | |
| } | |
| const monthlyPrice = parsePrice( | |
| priceDiv.dataset.originalPrice | |
| ); | |
| if (isNaN(monthlyPrice)) return; | |
| animatePriceChange(priceDiv); | |
| animatePriceChange(periodDiv); | |
| if (yearlyMode) { | |
| priceDiv.textContent = formatPrice(monthlyPrice * 12); | |
| periodDiv.textContent = isEnglishPeriod | |
| ? 'per year' | |
| : 'pro Jahr'; | |
| } else { | |
| priceDiv.textContent = priceDiv.dataset.originalPrice; | |
| periodDiv.textContent = isEnglishPeriod | |
| ? 'per month' | |
| : 'pro Monat'; | |
| } | |
| }); | |
| } | |
| function createButton() { | |
| const btn = document.createElement('button'); | |
| btn.textContent = isEnglish | |
| ? 'Display: Month' | |
| : 'Anzeige: Monat'; | |
| Object.assign(btn.style, { | |
| position: 'fixed', | |
| bottom: '20px', | |
| left: '20px', | |
| zIndex: '999999', | |
| padding: '10px 15px', | |
| border: 'none', | |
| borderRadius: '8px', | |
| cursor: 'pointer', | |
| boxShadow: '0 2px 8px rgba(0,0,0,0.2)', | |
| fontSize: '14px' | |
| }); | |
| btn.addEventListener('click', () => { | |
| yearlyMode = !yearlyMode; | |
| btn.textContent = yearlyMode | |
| ? (isEnglish ? 'Display: Year' : 'Anzeige: Jahr') | |
| : (isEnglish ? 'Display: Month' : 'Anzeige: Monat'); | |
| updateCards(); | |
| }); | |
| document.body.appendChild(btn); | |
| } | |
| window.addEventListener('load', () => { | |
| createButton(); | |
| }); | |
| })(); |
Author
Author
Changelog
v1.1.0
- Added English language support (
/en/deals). - Added automatic detection and handling of German and English price formats.
- Added localized billing period labels (
pro Monat/pro Jahr,per month/per year). - Added localized toggle button labels (
Anzeige/Display). - Restricted script execution to German and English Netcup Deals pages only.
- Added smooth fade animation when switching between monthly and yearly prices.
v1.0.0
- Initial release.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How to install