const config = window.SeasonalsPluginConfig?.Halloween || {}; const halloween = config.EnableHalloween !== undefined ? config.EnableHalloween : true; // enable/disable halloween const randomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true; // enable random symbols const randomSymbolsMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false; // enable random symbols on mobile const enableDiffrentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true; // enable different durations const enableSpiders = config.EnableSpiders !== undefined ? config.EnableSpiders : true; // enable/disable spiders const enableMice = config.EnableMice !== undefined ? config.EnableMice : true; // enable/disable mice const halloweenCount = config.SymbolCount !== undefined ? config.SymbolCount : 25; // count of symbols const images = [ "../Seasonals/Resources/halloween_images/ghost_20x20.png", "../Seasonals/Resources/halloween_images/bat_20x20.png", "../Seasonals/Resources/halloween_images/pumpkin_20x20.png", ]; let msgPrinted = false; // function to check and control the halloween function toggleHalloween() { const halloweenContainer = document.querySelector('.halloween-container'); if (!halloweenContainer) 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'); // hide halloween if video/trailer player is active or dashboard is visible if (videoPlayer || trailerPlayer || isDashboard || hasUserMenu) { halloweenContainer.style.display = 'none'; // hide halloween if (!msgPrinted) { console.log('Halloween hidden'); msgPrinted = true; } } else { halloweenContainer.style.display = 'block'; // show halloween if (msgPrinted) { console.log('Halloween visible'); msgPrinted = false; } } } const observer = new MutationObserver(toggleHalloween); observer.observe(document.body, { childList: true, subtree: true, attributes: true }); function addRandomSymbols(count) { const halloweenContainer = document.querySelector('.halloween-container'); if (!halloweenContainer) return; console.log('Adding random halloween symbols'); for (let i = 0; i < count; i++) { const halloweenDiv = document.createElement("div"); halloweenDiv.className = "halloween"; const imageSrc = images[Math.floor(Math.random() * images.length)]; const img = document.createElement("img"); img.src = imageSrc; halloweenDiv.appendChild(img); const randomLeft = Math.random() * 100; // position (0% to 100%) const randomAnimationDelay = Math.random() * 10; // delay (0s to 10s) const randomAnimationDelay2 = -(Math.random() * 3); // delay (-3s to 0s) // apply styles halloweenDiv.style.left = `${randomLeft}%`; halloweenDiv.style.animationDelay = `${randomAnimationDelay}s, ${randomAnimationDelay2}s`; // set random animation duration if (enableDiffrentDuration) { const randomAnimationDuration = Math.random() * 10 + 6; // delay (6s to 10s) const randomAnimationDuration2 = Math.random() * 5 + 2; // delay (2s to 5s) halloweenDiv.style.animationDuration = `${randomAnimationDuration}s, ${randomAnimationDuration2}s`; } halloweenContainer.appendChild(halloweenDiv); } console.log('Random halloween symbols added'); } function createHalloween() { const container = document.querySelector('.halloween-container') || document.createElement("div"); if (!document.querySelector('.halloween-container')) { container.className = "halloween-container"; container.setAttribute("aria-hidden", "true"); document.body.appendChild(container); } for (let i = 0; i < 4; i++) { images.forEach(imageSrc => { const halloweenDiv = document.createElement("div"); halloweenDiv.className = "halloween"; const img = document.createElement("img"); img.src = imageSrc; // set random animation duration if (enableDiffrentDuration) { const randomAnimationDuration = Math.random() * 10 + 6; // delay (6s to 10s) const randomAnimationDuration2 = Math.random() * 5 + 2; // delay (2s to 5s) halloweenDiv.style.animationDuration = `${randomAnimationDuration}s, ${randomAnimationDuration2}s`; } halloweenDiv.appendChild(img); container.appendChild(halloweenDiv); }); } } // create fog layer function createFog(container) { const fogContainer = document.createElement('div'); fogContainer.className = 'halloween-fog-layer'; const fog1 = document.createElement('div'); fog1.className = 'halloween-fog-blob'; const fog2 = document.createElement('div'); fog2.className = 'halloween-fog-blob'; fogContainer.appendChild(fog1); fogContainer.appendChild(fog2); container.appendChild(fogContainer); } // create dropping spiders function createSpider(container) { const wrapper = document.createElement('div'); wrapper.className = 'halloween-spider-wrapper'; wrapper.innerHTML = `
`; wrapper.style.left = `${10 + Math.random() * 80}%`; const dropHeight = 30 + Math.random() * 50; // 30vh to 80vh wrapper.style.setProperty('--drop-height', `${dropHeight}vh`); const duration = Math.random() * 6 + 6; // 6-12s drop wrapper.style.animation = `spider-drop ${duration}s ease-in-out forwards`; // Start the sway animation only after the drop completes (30% of total duration) const sway = wrapper.querySelector('.halloween-sway'); sway.style.animation = `wind-sway 8s ease-in-out ${duration * 0.3}s infinite`; // Spider retreat logic let isRetreating = false; wrapper.addEventListener('mouseenter', () => { if (isRetreating) return; isRetreating = true; // Retreat smoothly by pushing margin up wrapper.style.transition = 'margin-top 0.4s ease-in'; wrapper.style.marginTop = '-100vh'; setTimeout(() => { wrapper.remove(); if (document.body.contains(container)) { setTimeout(() => createSpider(container), Math.random() * 5000 + 1000); } }, 500); }); wrapper.addEventListener('animationend', () => { if (isRetreating) return; wrapper.remove(); if (document.body.contains(container)) { setTimeout(() => createSpider(container), Math.random() * 5000 + 1000); } }); container.appendChild(wrapper); } // create scurrying mice function createMouse(container) { const mouse = document.createElement('div'); mouse.className = 'halloween-mouse'; mouse.innerHTML = ` `; const direction = Math.random() > 0.5 ? 'right' : 'left'; const duration = Math.random() * 3 + 2; // 2-5s run (fast) if (direction === 'right') { mouse.style.animation = `mouse-run-right ${duration}s linear forwards`; mouse.style.transform = 'scaleX(1)'; } else { mouse.style.animation = `mouse-run-left ${duration}s linear forwards`; mouse.style.transform = 'scaleX(-1)'; } mouse.style.bottom = `5px`; // Fixated bottom edge mouse.addEventListener('animationend', () => { mouse.remove(); if (document.body.contains(container)) { setTimeout(() => createMouse(container), Math.random() * 4000 + 2000); } }); container.appendChild(mouse); } function initializeHalloween() { if (!halloween) return; createHalloween(); toggleHalloween(); const container = document.querySelector('.halloween-container'); if (container) { createFog(container); // Add a few spiders if (enableSpiders) { for (let i = 0; i < 4; i++) { setTimeout(() => createSpider(container), Math.random() * 5000); } } // Add a few mice if (enableMice) { for (let i = 0; i < 3; i++) { setTimeout(() => createMouse(container), Math.random() * 3000); } } } const screenWidth = window.innerWidth; // get the screen width to detect mobile devices if (randomSymbols && (screenWidth > 768 || randomSymbolsMobile)) { addRandomSymbols(halloweenCount); } } initializeHalloween();