const config = window.SeasonalsPluginConfig?.Birthday || {}; const birthday = config.EnableBirthday !== undefined ? config.EnableBirthday : true; const symbolCount = config.SymbolCount || 25; const useRandomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true; const enableRandomMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false; const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true; const enableGarland = config.EnableGarland !== undefined ? config.EnableGarland : true; const birthdayImages = [ '../Seasonals/Resources/birthday_images/balloon_1.gif', '../Seasonals/Resources/birthday_images/balloon_2.gif', '../Seasonals/Resources/birthday_images/balloon_3.gif' ]; let msgPrinted = false; function toggleBirthday() { const container = document.querySelector('.birthday-container'); if (!container) return; const videoPlayer = document.querySelector('.videoPlayerContainer'); const trailerPlayer = document.querySelector('.youtubePlayerContainer'); const isDashboard = document.body.classList.contains('dashboardDocument'); const hasUserMenu = document.querySelector('#app-user-menu'); if (videoPlayer || trailerPlayer || isDashboard || hasUserMenu) { container.style.display = 'none'; if (!msgPrinted) { console.log('Birthday hidden'); msgPrinted = true; } } else { container.style.display = 'block'; if (msgPrinted) { console.log('Birthday visible'); msgPrinted = false; } } } const observer = new MutationObserver(toggleBirthday); observer.observe(document.body, { childList: true, subtree: true, attributes: true }); function createBalloonPopConfetti(container, x, y) { const popConfettiColors = [ '#fce18a', '#ff726d', '#b48def', '#f4306d', '#36c5f0', '#2ccc5d', '#e9b31d', '#9b59b6', '#3498db', '#e74c3c', '#1abc9c', '#f1c40f' ]; // Spawn 15-20 particles const particleCount = Math.floor(Math.random() * 5) + 15; for (let i = 0; i < particleCount; i++) { const particle = document.createElement('div'); particle.classList.add('birthday-burst-confetti'); // Random color const color = popConfettiColors[Math.floor(Math.random() * popConfettiColors.length)]; particle.style.backgroundColor = color; // Random shape const shape = Math.random(); if (shape > 0.66) { particle.classList.add('circle'); const size = Math.random() * 4 + 4; // 4-8px particle.style.width = `${size}px`; particle.style.height = `${size}px`; } else if (shape > 0.33) { particle.classList.add('rect'); const width = Math.random() * 3 + 3; // 3-6px const height = Math.random() * 4 + 6; // 6-10px particle.style.width = `${width}px`; particle.style.height = `${height}px`; } else { particle.classList.add('triangle'); } particle.style.position = 'absolute'; particle.style.left = `${x}px`; particle.style.top = `${y}px`; particle.style.zIndex = '1000'; // Random direction for explosion (circular) const angle = Math.random() * 2 * Math.PI; const distance = Math.random() * 60 + 20; // 20-80px burst radius const xOffset = Math.cos(angle) * distance; const yOffset = Math.sin(angle) * distance; particle.style.setProperty('--burst-x', `${xOffset}px`); particle.style.setProperty('--burst-y', `${yOffset}px`); // Random rotation during fall particle.style.setProperty('--rot-dir', `${(Math.random() > 0.5 ? 1 : -1) * 360}deg`); particle.style.setProperty('--rx', Math.random().toFixed(2)); particle.style.setProperty('--ry', Math.random().toFixed(2)); particle.style.setProperty('--rz', (Math.random() * 0.5).toFixed(2)); container.appendChild(particle); // Remove particle after animation setTimeout(() => particle.remove(), 1500); } } function createBirthday() { const container = document.querySelector('.birthday-container') || document.createElement('div'); if (!document.querySelector('.birthday-container')) { container.className = 'birthday-container'; container.setAttribute("aria-hidden", "true"); document.body.appendChild(container); } // Party Garland if (enableGarland) { const garland = document.createElement('div'); garland.className = 'birthday-garland'; const garlandImg = document.createElement('img'); garlandImg.src = '../Seasonals/Resources/birthday_images/garland.png'; garlandImg.onerror = function() { this.style.display = 'none'; }; garland.appendChild(garlandImg); container.appendChild(garland); } // Spawn Birthday Cake at the bottom const cake = document.createElement('div'); cake.className = 'birthday-cake'; let cakeImg = document.createElement('img'); cakeImg.src = `../Seasonals/Resources/birthday_images/cake.gif`; cakeImg.onerror = function() { this.style.display = 'none'; this.parentElement.innerHTML = '🎂'; }; cake.appendChild(cakeImg); container.appendChild(cake); const standardCount = 15; const totalSymbols = symbolCount + standardCount; let isMobile = window.matchMedia("only screen and (max-width: 768px)").matches; let finalCount = totalSymbols; if (isMobile) { finalCount = enableRandomMobile ? totalSymbols : standardCount; } const useRandomDuration = enableDifferentDuration !== false; // We'll treat all balloons as rising symbols const activeItems = ['balloon_1', 'balloon_2', 'balloon_3']; for (let i = 0; i < finalCount; i++) { let symbol = document.createElement('div'); const randomItem = activeItems[Math.floor(Math.random() * activeItems.length)]; symbol.className = `birthday-symbol birthday-${randomItem}`; // Create inner div for sway let innerDiv = document.createElement('div'); innerDiv.className = 'birthday-inner'; let img = document.createElement('img'); img.src = `../Seasonals/Resources/birthday_images/${randomItem}.gif`; // Use standard pop GIFs img.onerror = function() { this.style.display = 'none'; this.parentElement.innerHTML = getBirthdayEmojiFallback(randomItem); }; innerDiv.appendChild(img); symbol.appendChild(innerDiv); const leftPos = Math.random() * 95; const delaySeconds = Math.random() * 10; // Far away effect const depth = Math.random(); const scale = 0.5 + depth * 0.7; // 0.5 to 1.2 const zIndex = Math.floor(depth * 30) + 10; img.style.transform = `scale(${scale})`; symbol.style.zIndex = zIndex; let durationSeconds = 9; if (useRandomDuration) { // Far strings climb slower durationSeconds = (1 - depth) * 6 + 7 + Math.random() * 4; } const isBalloon = randomItem.startsWith('balloon'); if (isBalloon) { // Sway animation const swayDur = Math.random() * 2 + 3; // 3 to 5s const swayDir = Math.random() > 0.5 ? 'normal' : 'reverse'; innerDiv.style.animation = `birthday-sway ${swayDur}s ease-in-out infinite alternate ${swayDir}`; // Interaction to pop is handled visually by the GIF, but we can still remove it on hover innerDiv.addEventListener('mouseenter', function(e) { if (!this.classList.contains('popped')) { this.classList.add('popped'); this.style.animation = 'birthday-pop 0.2s ease-out forwards'; this.style.pointerEvents = 'none'; // avoid re-triggering // Create confetti burst at balloon's screen position const rect = this.getBoundingClientRect(); const cx = rect.left + rect.width / 2; const cy = rect.top + rect.height / 2; // Ensure the burst container is appended to the main document body or the birthday container createBalloonPopConfetti(document.body, cx, cy); } }, { once: true }); } const startRot = (Math.random() * 20) - 10; // -10 to +10 spread symbol.style.setProperty('--start-rot', `${startRot}deg`); symbol.style.left = `${leftPos}vw`; symbol.style.animationDuration = `${durationSeconds}s`; symbol.style.animationDelay = `${delaySeconds}s`; container.appendChild(symbol); } // Party Confetti const confettiColors = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', '#ffffff', '#000000']; const confettiCount = isMobile ? 40 : 80; for (let i = 0; i < confettiCount; i++) { let confetti = document.createElement('div'); confetti.className = 'birthday-confetti'; const color = confettiColors[Math.floor(Math.random() * confettiColors.length)]; confetti.style.backgroundColor = color; const leftPos = Math.random() * 100; const delaySeconds = Math.random() * 8; const duration = Math.random() * 3 + 4; confetti.style.left = `${leftPos}vw`; confetti.style.animationDuration = `${duration}s`; confetti.style.animationDelay = `${delaySeconds}s`; container.appendChild(confetti); } } function getBirthdayEmojiFallback(type) { if (type.startsWith('balloon')) return '🎈'; if (type === 'gift') return '🎁'; return ''; } function initializeBirthday() { if (!birthday) return; createBirthday(); toggleBirthday(); } initializeBirthday();