diff --git a/README.md b/README.md new file mode 100644 index 0000000..99a7722 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# Webclock + +En vakker nettbasert klokkeapplikasjon med analog og digital visning for flere norske byer (Trondheim, Ålesund og Gjøvik). Klokken inkluderer dynamiske væranimasjoner og sesongbaserte temaer. + +![screenshot](doc-screenshot.png) + +## Temaer + +Applikasjonen bytter automatisk mellom temaer basert på dagens dato + +## Testmodus + +Testmodus lar deg manuelt overstyre den automatiske temavelgeren og teste ulike temaer uten å vente på spesifikke datoer. + +![Test mode theme selector](doc-testmode.png) + +### Aktivere testmodus + +Legg til `?mode=test` i URL-en: + +``` +http://localhost/index.html?mode=test +``` + +## Legge til nye temaer + +Legg til nye temadefinisjoner i `themes`-objektet: + +```javascript +mytheme: { + dateRange: { startMonth: 0, startDay: 1, endMonth: 0, endDay: 31 }, + clockFace: 'clock-face-mytheme.png', + clockFaceScale: 'cover', + background: 'planks-mytheme.png', + backgroundSize: 'cover', + textColor: '#ffffff', + centerDotColor: 'radial-gradient(...)', + centerDotBorder: '#888', + handColors: { + hour: 'linear-gradient(..s.)', + minute: 'linear-gradient(...)', + second: 'linear-gradient(...)' + } +} +``` + +Legge til tema-valg i tema-velgeren (test-modus): + +```Html +
+ + +
+``` + +## Takk til + +- Værdata levert av [yr.no](https://yr.no) / Meteorologisk institutt +- Måneberegninger basert på astronomiske algoritmer + diff --git a/clock-face-saami.png b/clock-face-saami.png new file mode 100644 index 0000000..5d98332 Binary files /dev/null and b/clock-face-saami.png differ diff --git a/doc-screenshot.png b/doc-screenshot.png new file mode 100644 index 0000000..1236363 Binary files /dev/null and b/doc-screenshot.png differ diff --git a/doc-testmode.png b/doc-testmode.png new file mode 100644 index 0000000..2602759 Binary files /dev/null and b/doc-testmode.png differ diff --git a/index.html b/index.html index bc9e447..9e45526 100644 --- a/index.html +++ b/index.html @@ -14,11 +14,61 @@ padding: 20px; min-height: 100vh; display: flex; + flex-direction: column; justify-content: center; align-items: center; overflow-x: auto; } + .theme-selector { + position: fixed; + top: 20px; + right: 20px; + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(10px); + padding: 15px 20px; + border-radius: 10px; + box-shadow: 0 4px 16px rgba(31, 38, 135, 0.37); + border: 1px solid rgba(255, 255, 255, 0.18); + z-index: 1000; + display: none; /* Hidden by default */ + align-items: center; + gap: 10px; + } + + .theme-selector.visible { + display: flex; + } + + .theme-selector label { + color: white; + font-weight: bold; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); + font-size: 1rem; + } + + .theme-selector select { + padding: 8px 12px; + border-radius: 5px; + border: 2px solid rgba(255, 255, 255, 0.3); + background: rgba(255, 255, 255, 0.9); + color: #333; + font-size: 1rem; + cursor: pointer; + transition: all 0.3s ease; + outline: none; + } + + .theme-selector select:hover { + background: rgba(255, 255, 255, 1); + border-color: rgba(255, 255, 255, 0.5); + } + + .theme-selector select:focus { + border-color: #4CAF50; + box-shadow: 0 0 5px rgba(76, 175, 80, 0.5); + } + .clocks-container { display: flex; gap: 40px; @@ -288,6 +338,24 @@ } @media (max-width: 768px) { + .theme-selector { + position: relative; + top: 0; + right: 0; + margin-bottom: 20px; + width: calc(100% - 40px); + justify-content: center; + } + + .theme-selector label { + font-size: 0.9rem; + } + + .theme-selector select { + font-size: 0.9rem; + padding: 6px 10px; + } + .clocks-container { flex-direction: column; gap: 20px; @@ -535,6 +603,18 @@ +
+ + +
+
@@ -1280,6 +1360,39 @@ second: 'linear-gradient(to bottom, #ffd700, #ffed4e, #ffd700)' // Gold } }, + saami: { + /* + Sámi theme by on@ntnu.no + */ + dateRange: { startMonth: 1, startDay: 1, endMonth: 1, endDay: 28 }, // Feb 1-28 + clockFace: 'clock-face-saami.png', + clockFaceScale: 'cover', + background: 'saami-background_16-9.png', + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + backgroundAttachment: 'fixed', + textColor: '#d7ffa6', + centerDotColor: '#d7ffa6', + centerDotBorder: '#FFF', + handColors: { + hour: '#FFF', + minute: '#FFF', + second: '#FF3300' + }, + clockContainer: { + background: 'rgba(0, 0, 0, 0.15)', + backdropFilter: 'blur(0px)', + borderRadius: '20px', + border: '1px solid rgba(215, 255, 166, 0.5)', + boxShadow: '0 8px 32px rgba(215, 255, 166, 0.3)' + }, + cityNames: { + trondheim: 'Tråante', + alesund: 'Ålesund', + gjovik: 'Gjøvik' + } + }, default: { clockFace: 'clock-face.png', clockFaceScale: 'cover', @@ -1299,6 +1412,13 @@ }; function getCurrentTheme() { + // Check if user has manually selected a theme + const selectedTheme = localStorage.getItem('selectedTheme'); + if (selectedTheme && selectedTheme !== 'auto') { + return themes[selectedTheme] || themes.default; + } + + // Otherwise use date-based automatic theme selection const today = new Date(); const month = today.getMonth(); // 0-11 const day = today.getDate(); @@ -1362,6 +1482,45 @@ element.style.boxShadow = handGlow; }); + // Apply clock container styling if specified in theme + const defaultContainerStyles = { + background: 'rgba(255, 255, 255, 0.1)', + backdropFilter: 'blur(10px)', + borderRadius: '20px', + padding: '30px', + boxShadow: '0 8px 32px rgba(31, 38, 135, 0.37)', + border: '1px solid rgba(255, 255, 255, 0.18)' + }; + + const containerStyles = theme.clockContainer || {}; + document.querySelectorAll('.clock-container').forEach(element => { + // Apply each style, using theme value if provided, otherwise use default + element.style.background = containerStyles.background || defaultContainerStyles.background; + element.style.backdropFilter = containerStyles.backdropFilter || defaultContainerStyles.backdropFilter; + element.style.borderRadius = containerStyles.borderRadius || defaultContainerStyles.borderRadius; + element.style.padding = containerStyles.padding || defaultContainerStyles.padding; + element.style.boxShadow = containerStyles.boxShadow || defaultContainerStyles.boxShadow; + element.style.border = containerStyles.border || defaultContainerStyles.border; + }); + + // Apply city name overrides if specified in theme + if (theme.cityNames) { + Object.keys(theme.cityNames).forEach(cityKey => { + const cityNameElement = document.querySelector(`#clock-${cityKey}`).closest('.clock-container').querySelector('.city-name'); + if (cityNameElement) { + cityNameElement.textContent = theme.cityNames[cityKey]; + } + }); + } else { + // Reset to default city names if no override + Object.keys(clocks).forEach(cityKey => { + const cityNameElement = document.querySelector(`#clock-${cityKey}`).closest('.clock-container').querySelector('.city-name'); + if (cityNameElement) { + cityNameElement.textContent = clocks[cityKey].cityName.toUpperCase(); + } + }); + } + // Update CSS for clock face image (pseudo-element) const existingStyle = document.getElementById('theme-style'); if (existingStyle) { @@ -1384,6 +1543,31 @@ // Check for theme change every hour setInterval(applyTheme, 1000 * 60 * 60); + // Initialize theme dropdown + document.addEventListener('DOMContentLoaded', function() { + const themeDropdown = document.getElementById('theme-dropdown'); + const themeSelector = document.querySelector('.theme-selector'); + + // Check if mode=test is in the URL + const urlParams = new URLSearchParams(window.location.search); + const mode = urlParams.get('mode'); + + if (mode === 'test') { + themeSelector.classList.add('visible'); + } + + // Load saved theme preference + const savedTheme = localStorage.getItem('selectedTheme') || 'auto'; + themeDropdown.value = savedTheme; + + // Handle theme selection + themeDropdown.addEventListener('change', function() { + const selectedTheme = this.value; + localStorage.setItem('selectedTheme', selectedTheme); + applyTheme(); + }); + }); + // Initialize weather effects with default values first initializeWeather(); diff --git a/saami-background_16-9.png b/saami-background_16-9.png new file mode 100644 index 0000000..dc1dfb1 Binary files /dev/null and b/saami-background_16-9.png differ