266 lines
10 KiB
JavaScript
266 lines
10 KiB
JavaScript
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();
|