diff --git a/themes/one-pager/assets/js/scripts.js b/themes/one-pager/assets/js/scripts.js index f42a1c2..917d2b0 100644 --- a/themes/one-pager/assets/js/scripts.js +++ b/themes/one-pager/assets/js/scripts.js @@ -1,188 +1,16 @@ -document.addEventListener('DOMContentLoaded', function() { - // Sticky Navigation - const stickyNav = document.getElementById('stickyNav'); - const mainHeader = document.querySelector('.main-header'); - const themeToggle = document.getElementById('themeToggle'); - const themeToggleSmall = document.getElementById('themeToggleSmall'); - let lastScrollY = window.scrollY; - let ticking = false; - - // Function to update sticky nav visibility - function updateStickyNav() { - const scrollY = window.scrollY; - const headerHeight = mainHeader ? mainHeader.offsetHeight : 400; - - if (scrollY > headerHeight - 100) { - stickyNav.classList.add('visible'); - if (themeToggle) { - themeToggle.classList.add('hidden'); - } - } else { - stickyNav.classList.remove('visible'); - if (themeToggle) { - themeToggle.classList.remove('hidden'); - } - } - lastScrollY = scrollY; - } - - // Throttled scroll listener - window.addEventListener('scroll', function() { - if (!ticking) { - window.requestAnimationFrame(function() { - updateStickyNav(); - ticking = false; - }); - ticking = true; - } - }); - - // Initial check - setTimeout(updateStickyNav, 100); - +// ============================================ +// 1. UTILITY FUNCTIONS +// ============================================ - - - // Read More toggle for blog posts - const readMoreButtons = document.querySelectorAll('.read-more-toggle'); - - readMoreButtons.forEach(button => { - button.addEventListener('click', function() { - const content = this.nextElementSibling; - if (content.style.display === 'none') { - content.style.display = 'block'; - this.innerHTML = 'Read Less'; - } else { - content.style.display = 'none'; - this.innerHTML = 'Read More'; - } - }); - }); - - - // Smooth scrolling for navigation links (both main nav and sticky nav) - document.querySelectorAll('nav a').forEach(link => { - link.addEventListener('click', function(e) { - e.preventDefault(); - const targetId = this.getAttribute('href'); - const targetElement = document.querySelector(targetId); - if (targetElement) { - // Calculate offset for sticky nav - const navHeight = stickyNav.offsetHeight; - const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - navHeight - 20; - - window.scrollTo({ - top: targetPosition, - behavior: 'smooth' - }); - } - }); - }); - - // ============ CUSTOM LIGHTBOX ============ - const lightbox = document.getElementById('customLightbox'); - const lightboxImage = document.getElementById('lightboxImage'); - const lightboxCaption = document.getElementById('lightboxCaption'); - const lightboxMeta = document.getElementById('lightboxMeta'); - const lightboxCounter = document.getElementById('lightboxCounter'); - const lightboxClose = document.getElementById('lightboxClose'); - const lightboxPrev = document.getElementById('lightboxPrev'); - const lightboxNext = document.getElementById('lightboxNext'); - - let currentPhotos = []; - let currentIndex = 0; - - // Get all photo items - const photoItems = document.querySelectorAll('.photo-item'); - - // Build photo array - photoItems.forEach(item => { - const image = item.querySelector('img'); - const date = item.dataset.date || ''; - const pubkey = item.dataset.pubkey || ''; - const alt = item.dataset.alt || ''; - - if (image) { - currentPhotos.push({ - url: image.src, - alt: alt, - date: date, - pubkey: pubkey - }); - } - }); - - // Open lightbox - function openLightbox(index) { - if (index < 0) index = currentPhotos.length - 1; - if (index >= currentPhotos.length) index = 0; - currentIndex = index; - - const photo = currentPhotos[currentIndex]; - lightboxImage.src = photo.url; - lightboxImage.alt = photo.alt; - lightboxCaption.textContent = photo.alt; - lightboxMeta.textContent = `📅 ${photo.date} | 🖊️ ${photo.pubkey}`; - lightboxCounter.textContent = `${currentIndex + 1} / ${currentPhotos.length}`; - - lightbox.classList.add('active'); - document.body.style.overflow = 'hidden'; - } - - // Close lightbox - function closeLightbox() { - lightbox.classList.remove('active'); - document.body.style.overflow = ''; - } - - // Navigate - function prevPhoto() { - openLightbox(currentIndex - 1); - } - - function nextPhoto() { - openLightbox(currentIndex + 1); - } - - // Event listeners for photo clicks - photoItems.forEach((item, index) => { - const img = item.querySelector('img'); - if (img) { - img.addEventListener('click', function(e) { - e.preventDefault(); - openLightbox(index); - }); - } - }); - - // Event listeners for lightbox controls - lightboxClose.addEventListener('click', closeLightbox); - lightboxPrev.addEventListener('click', prevPhoto); - lightboxNext.addEventListener('click', nextPhoto); - - // Keyboard controls - document.addEventListener('keydown', function(e) { - if (!lightbox.classList.contains('active')) return; - - if (e.key === 'Escape') { - closeLightbox(); - } else if (e.key === 'ArrowLeft') { - prevPhoto(); - } else if (e.key === 'ArrowRight') { - nextPhoto(); - } - }); - - // Click outside image to close - lightbox.addEventListener('click', function(e) { - if (e.target === lightbox) { - closeLightbox(); - } - }); -}); - -// No external lightbox library needed anymore! -console.log('Custom lightbox initialized successfully!'); +// Function to shuffle array (Fisher-Yates) +function shuffleArray(array) { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; +} // Artist filter list const allowedArtists = [ @@ -194,58 +22,163 @@ const allowedArtists = [ // Function to check if a track is by an allowed artist function isAllowedArtist(artist) { if (!artist) return false; - // Normalize the artist name for comparison (remove extra spaces, lowercase) const normalizedArtist = artist.trim().toLowerCase(); - return allowedArtists.some(allowed => { - // Normalize the allowed artist name too const normalizedAllowed = allowed.trim().toLowerCase(); - // Check if the artist name contains the allowed artist name or vice versa return normalizedArtist.includes(normalizedAllowed) || normalizedAllowed.includes(normalizedArtist); }); } -// Render music tracks (randomized, 6 at a time, filtered by artist) +// ============================================ +// 2. CUSTOM LIGHTBOX +// ============================================ + +let currentPhotos = []; +let currentIndex = 0; + +// Build photo array from DOM +function buildPhotoArray() { + const photoItems = document.querySelectorAll('.photo-item'); + currentPhotos = []; + photoItems.forEach(item => { + const image = item.querySelector('img'); + const date = item.dataset.date || ''; + const pubkey = item.dataset.pubkey || ''; + const alt = item.dataset.alt || ''; + if (image) { + currentPhotos.push({ + url: image.src, + alt: alt, + date: date, + pubkey: pubkey + }); + } + }); +} + +// Open lightbox +function openLightbox(index) { + const lightbox = document.getElementById('customLightbox'); + const lightboxImage = document.getElementById('lightboxImage'); + const lightboxCaption = document.getElementById('lightboxCaption'); + const lightboxMeta = document.getElementById('lightboxMeta'); + const lightboxCounter = document.getElementById('lightboxCounter'); + + if (!lightbox || !lightboxImage || currentPhotos.length === 0) return; + + if (index < 0) index = currentPhotos.length - 1; + if (index >= currentPhotos.length) index = 0; + currentIndex = index; + + const photo = currentPhotos[currentIndex]; + lightboxImage.src = photo.url; + lightboxImage.alt = photo.alt; + lightboxCaption.textContent = photo.alt; + lightboxMeta.textContent = `📅 ${photo.date}`; + lightboxCounter.textContent = `${currentIndex + 1} / ${currentPhotos.length}`; + + lightbox.classList.add('active'); + document.body.style.overflow = 'hidden'; +} + +// Close lightbox +function closeLightbox() { + const lightbox = document.getElementById('customLightbox'); + if (!lightbox) return; + lightbox.classList.remove('active'); + document.body.style.overflow = ''; +} + +// Navigate +function prevPhoto() { openLightbox(currentIndex - 1); } +function nextPhoto() { openLightbox(currentIndex + 1); } + +// Initialize lightbox event listeners +function initLightbox() { + const lightbox = document.getElementById('customLightbox'); + const lightboxClose = document.getElementById('lightboxClose'); + const lightboxPrev = document.getElementById('lightboxPrev'); + const lightboxNext = document.getElementById('lightboxNext'); + + if (!lightbox) return; + + buildPhotoArray(); + + // Photo click listeners + document.querySelectorAll('.photo-item img').forEach((img, index) => { + img.removeEventListener('click', img._lightboxClick); + img._lightboxClick = function(e) { + e.preventDefault(); + openLightbox(index); + }; + img.addEventListener('click', img._lightboxClick); + }); + + // Control listeners + lightboxClose.removeEventListener('click', closeLightbox); + lightboxClose.addEventListener('click', closeLightbox); + + lightboxPrev.removeEventListener('click', prevPhoto); + lightboxPrev.addEventListener('click', prevPhoto); + + lightboxNext.removeEventListener('click', nextPhoto); + lightboxNext.addEventListener('click', nextPhoto); + + // Keyboard controls + document.removeEventListener('keydown', handleLightboxKeydown); + document.addEventListener('keydown', handleLightboxKeydown); + + // Click outside to close + lightbox.removeEventListener('click', handleLightboxClick); + lightbox.addEventListener('click', handleLightboxClick); +} + +function handleLightboxKeydown(e) { + const lightbox = document.getElementById('customLightbox'); + if (!lightbox || !lightbox.classList.contains('active')) return; + if (e.key === 'Escape') closeLightbox(); + else if (e.key === 'ArrowLeft') prevPhoto(); + else if (e.key === 'ArrowRight') nextPhoto(); +} + +function handleLightboxClick(e) { + const lightbox = document.getElementById('customLightbox'); + if (e.target === lightbox) closeLightbox(); +} + +// ============================================ +// 3. MUSIC RENDERER +// ============================================ + function renderMusicTracks(tracks, containerId) { const container = document.getElementById(containerId); if (!container) return; - // Filter tracks by artist const filteredTracks = tracks.filter(track => { - // Extract artist from tags let artist = ''; if (track.tags) { track.tags.forEach(tag => { - if (tag[0] === 'artist') { - artist = tag[1]; - } + if (tag[0] === 'artist') artist = tag[1]; }); } return isAllowedArtist(artist); }); - // Check if we have any filtered tracks if (filteredTracks.length === 0) { container.innerHTML = '

No tracks found for the selected artists.

'; return; } - // Shuffle and get first 6 - const shuffled = filteredTracks.sort(() => 0.5 - Math.random()); + const shuffled = shuffleArray(filteredTracks); const selected = shuffled.slice(0, 6); - - // Clear container container.innerHTML = ''; - // Render each track (same as before) selected.forEach(track => { - // Extract data from tags let title = '', url = '', image = '', artist = '', album = '', trackNumber = '', duration = '', released = '', language = '', tags = [], alt = '', content = ''; - // Parse tags if (track.tags) { track.tags.forEach(tag => { if (tag[0] === 'title') title = tag[1]; @@ -261,10 +194,8 @@ function renderMusicTracks(tracks, containerId) { else if (tag[0] === 'alt') alt = tag[1]; }); } - content = track.content || ''; - // Create music card HTML const card = document.createElement('div'); card.className = 'music-card'; @@ -295,13 +226,10 @@ function renderMusicTracks(tracks, containerId) { container.appendChild(card); }); - // Re-initialize event listeners for new buttons initMusicEventListeners(); } -// Initialize music event listeners function initMusicEventListeners() { - // Lyrics toggle document.querySelectorAll('.lyrics-toggle').forEach(button => { button.removeEventListener('click', handleLyricsToggle); button.addEventListener('click', handleLyricsToggle); @@ -321,195 +249,19 @@ function handleLyricsToggle() { } } -// Refresh music (randomize) -function refreshMusic() { - if (window.allMusicData) { - renderMusicTracks(window.allMusicData, 'musicGrid'); - } -} - -// Call this when page loads -document.addEventListener('DOMContentLoaded', function() { - if (window.allMusicData && window.allMusicData.length > 0) { - renderMusicTracks(window.allMusicData, 'musicGrid'); - } -}); - -// ============ CUSTOM BLURHASH DECODER ============ -// Based on the official blurhash implementation, but compact - -const Blurhash = { - // Base83 character set - chars: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+,;<=>?@[]^_`{|}~', - - // Decode base83 to integer - decode83: function(str) { - let value = 0; - for (let i = 0; i < str.length; i++) { - const c = str[i]; - const digit = this.chars.indexOf(c); - if (digit === -1) { - throw new Error('Invalid base83 character: ' + c); - } - value = value * 83 + digit; - } - return value; - }, - - // Decode blurhash to pixel data - decode: function(blurhash, width, height, punch = 1) { - // Parse metadata - const sizeFlag = this.decode83(blurhash[0]); - const numX = (sizeFlag % 9) + 1; - const numY = Math.floor(sizeFlag / 9) + 1; - - // Calculate maximum AC component value - const maxAC = this.decode83(blurhash[1]) + 1; - - // Decode DC component - const dcValue = this.decode83(blurhash.slice(2, 6)); - const dc = this.decodeDC(dcValue); - - // Decode AC components - const ac = []; - let acIndex = 6; - for (let y = 0; y < numY; y++) { - for (let x = 0; x < numX; x++) { - if (x === 0 && y === 0) continue; - const encoded = this.decode83(blurhash.slice(acIndex, acIndex + 2)); - acIndex += 2; - const acValue = this.decodeAC(encoded, maxAC * punch); - ac.push({ x, y, value: acValue }); - } - } - - // Render pixels - const pixels = new Uint8ClampedArray(width * height * 4); - const size = width * height; - - // Pre-calculate cos values for performance - const cosX = []; - const cosY = []; - for (let x = 0; x < width; x++) { - cosX[x] = Math.cos(Math.PI * x / width); - } - for (let y = 0; y < height; y++) { - cosY[y] = Math.cos(Math.PI * y / height); - } - - for (let i = 0; i < size; i++) { - const x = i % width; - const y = Math.floor(i / width); - let r = dc[0]; - let g = dc[1]; - let b = dc[2]; - - for (const acVal of ac) { - const basis = cosX[x] * cosY[y]; - const value = acVal.value; - r += value[0] * basis; - g += value[1] * basis; - b += value[2] * basis; - } - - const index = i * 4; - pixels[index] = this.clamp(r + 128); - pixels[index + 1] = this.clamp(g + 128); - pixels[index + 2] = this.clamp(b + 128); - pixels[index + 3] = 255; - } - - return pixels; - }, - - // Decode DC component - decodeDC: function(value) { - return [ - this.signPow((value >> 16) & 0x3ff, 2), - this.signPow((value >> 8) & 0xff, 2), - this.signPow(value & 0xff, 2) - ]; - }, - - // Decode AC component - decodeAC: function(value, maxVal) { - const quantR = Math.floor(value / (19 * 19)); - const quantG = Math.floor((value / 19) % 19); - const quantB = value % 19; - return [ - this.signPow((quantR - 9) / 9, 2) * maxVal, - this.signPow((quantG - 9) / 9, 2) * maxVal, - this.signPow((quantB - 9) / 9, 2) * maxVal - ]; - }, - - // Sign power function - signPow: function(val, exp) { - return Math.sign(val) * Math.pow(Math.abs(val), exp); - }, - - // Clamp value to 0-255 - clamp: function(value) { - return Math.min(255, Math.max(0, value)); - }, - - // Render blurhash to canvas - renderToCanvas: function(blurhash, canvas, punch = 1) { - if (!blurhash) return false; - - try { - const width = canvas.width; - const height = canvas.height; - const pixels = this.decode(blurhash, width, height, punch); - const ctx = canvas.getContext('2d'); - const imageData = ctx.createImageData(width, height); - imageData.data.set(pixels); - ctx.putImageData(imageData, 0, 0); - return true; - } catch (e) { - console.warn('Failed to decode blurhash:', e); - return false; - } - }, - - // Get blurhash as data URL - toDataURL: function(blurhash, width = 64, height = 64, punch = 1) { - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - const success = this.renderToCanvas(blurhash, canvas, punch); - return success ? canvas.toDataURL('image/png') : null; - } -}; - -// ============ RANDOMIZED GALLERIES ============ - -// Function to shuffle array (Fisher-Yates) -function shuffleArray(array) { - const shuffled = [...array]; - for (let i = shuffled.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; - } - return shuffled; -} - -// Render random pictures (6 max) +// ============================================ +// 4. PICTURES RENDERER +// ============================================ function renderRandomPictures(picturesData, containerId) { const container = document.getElementById(containerId); if (!container) return; - // Shuffle and get first 6 const shuffled = shuffleArray(picturesData); const selected = shuffled.slice(0, 6); - - // Clear container container.innerHTML = ''; - // Render each picture selected.forEach(picture => { - // Parse imeta tag to get image URL let imageUrl = ''; let alt = picture.content || ''; @@ -517,21 +269,14 @@ function renderRandomPictures(picturesData, containerId) { picture.tags.forEach(tag => { if (tag[0] === 'imeta') { const imetaStr = tag[1]; - // Extract URL const urlMatch = imetaStr.match(/url ([^\s]+)/); - if (urlMatch) { - imageUrl = urlMatch[1]; - } - // Extract alt + if (urlMatch) imageUrl = urlMatch[1]; const altMatch = imetaStr.match(/alt ([^\s]+)/); - if (altMatch) { - alt = altMatch[1]; - } + if (altMatch) alt = altMatch[1]; } }); } - // Create picture item const item = document.createElement('div'); item.className = 'photo-item'; item.dataset.image = imageUrl; @@ -539,232 +284,36 @@ function renderRandomPictures(picturesData, containerId) { const date = new Date(picture.created_at * 1000); const dateStr = date.toLocaleDateString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric' + year: 'numeric', month: 'short', day: 'numeric' }); item.innerHTML = ` - ${imageUrl ? ` - ${alt} - ` : ''} -
- 📅 ${dateStr} -
- `; + ${imageUrl ? `${alt}` : ''} +
📅 ${dateStr}
+ `; - container.appendChild(item); + container.appendChild(item); }); - // Re-initialize the custom lightbox for new images - initCustomLightbox(); + initLightbox(); } -// Initialize custom lightbox (call this after any DOM updates) -function initCustomLightbox() { - const lightbox = document.getElementById('customLightbox'); - if (!lightbox) return; +// ============================================ +// 5. VIDEOS RENDERER +// ============================================ - const lightboxImage = document.getElementById('lightboxImage'); - const lightboxCaption = document.getElementById('lightboxCaption'); - const lightboxMeta = document.getElementById('lightboxMeta'); - const lightboxCounter = document.getElementById('lightboxCounter'); - const lightboxClose = document.getElementById('lightboxClose'); - const lightboxPrev = document.getElementById('lightboxPrev'); - const lightboxNext = document.getElementById('lightboxNext'); - - // Get all photo items - const photoItems = document.querySelectorAll('.photo-item'); - - // Build photo array - let currentPhotos = []; - photoItems.forEach(item => { - const image = item.querySelector('img'); - const date = item.dataset.date || ''; - const pubkey = item.dataset.pubkey || ''; - const alt = item.dataset.alt || ''; - - if (image) { - currentPhotos.push({ - url: image.src, - alt: alt, - date: date, - pubkey: pubkey - }); - } - }); - - let currentIndex = 0; - - // Remove old event listeners by cloning and replacing - const newLightboxImage = lightboxImage.cloneNode(true); - const newLightboxCaption = lightboxCaption.cloneNode(true); - const newLightboxMeta = lightboxMeta.cloneNode(true); - const newLightboxCounter = lightboxCounter.cloneNode(true); - const newLightboxClose = lightboxClose.cloneNode(true); - const newLightboxPrev = lightboxPrev.cloneNode(true); - const newLightboxNext = lightboxNext.cloneNode(true); - - lightboxImage.parentNode.replaceChild(newLightboxImage, lightboxImage); - lightboxCaption.parentNode.replaceChild(newLightboxCaption, lightboxCaption); - lightboxMeta.parentNode.replaceChild(newLightboxMeta, lightboxMeta); - lightboxCounter.parentNode.replaceChild(newLightboxCounter, lightboxCounter); - lightboxClose.parentNode.replaceChild(newLightboxClose, lightboxClose); - lightboxPrev.parentNode.replaceChild(newLightboxPrev, lightboxPrev); - lightboxNext.parentNode.replaceChild(newLightboxNext, lightboxNext); - - // Update references - const newImage = document.getElementById('lightboxImage'); - const newCaption = document.getElementById('lightboxCaption'); - const newMeta = document.getElementById('lightboxMeta'); - const newCounter = document.getElementById('lightboxCounter'); - const newClose = document.getElementById('lightboxClose'); - const newPrev = document.getElementById('lightboxPrev'); - const newNext = document.getElementById('lightboxNext'); - - // Open lightbox - function openLightbox(index) { - if (index < 0) index = currentPhotos.length - 1; - if (index >= currentPhotos.length) index = 0; - currentIndex = index; - - const photo = currentPhotos[currentIndex]; - newImage.src = photo.url; - newImage.alt = photo.alt; - newCaption.textContent = photo.alt; - newMeta.textContent = `📅 ${photo.date} | 🖊️ ${photo.pubkey}`; - newCounter.textContent = `${currentIndex + 1} / ${currentPhotos.length}`; - - lightbox.classList.add('active'); - document.body.style.overflow = 'hidden'; - } - - // Close lightbox - function closeLightbox() { - lightbox.classList.remove('active'); - document.body.style.overflow = ''; - } - - // Navigate - function prevPhoto() { - openLightbox(currentIndex - 1); - } - - function nextPhoto() { - openLightbox(currentIndex + 1); - } - - // Event listeners for photo clicks - photoItems.forEach((item, index) => { - const img = item.querySelector('img'); - if (img) { - // Remove old listener by cloning - const newImg = img.cloneNode(true); - img.parentNode.replaceChild(newImg, img); - newImg.addEventListener('click', function(e) { - e.preventDefault(); - openLightbox(index); - }); - } - }); - - // Event listeners for lightbox controls - newClose.addEventListener('click', closeLightbox); - newPrev.addEventListener('click', prevPhoto); - newNext.addEventListener('click', nextPhoto); - - // Keyboard controls - document.removeEventListener('keydown', handleLightboxKeydown); - document.addEventListener('keydown', handleLightboxKeydown); - - function handleLightboxKeydown(e) { - if (!lightbox.classList.contains('active')) return; - - if (e.key === 'Escape') { - closeLightbox(); - } else if (e.key === 'ArrowLeft') { - prevPhoto(); - } else if (e.key === 'ArrowRight') { - nextPhoto(); - } - } - - // Click outside image to close - lightbox.removeEventListener('click', handleLightboxClick); - lightbox.addEventListener('click', handleLightboxClick); - - function handleLightboxClick(e) { - if (e.target === lightbox) { - closeLightbox(); - } - } -} - -// Update the DOMContentLoaded event listener -document.addEventListener('DOMContentLoaded', function() { - // ... (your existing code for theme toggle, sticky nav, etc.) ... - - // Initialize Music (if you already have this) - if (window.allMusicData && window.allMusicData.length > 0) { - renderMusicTracks(window.allMusicData, 'musicGrid'); - } - - // Initialize Pictures - if (window.allPicturesData && window.allPicturesData.length > 0) { - renderRandomPictures(window.allPicturesData, 'photoGrid'); - } else { - // If no data, still initialize lightbox for static content - initCustomLightbox(); - } - - // Initialize Videos - if (window.allVideosData && window.allVideosData.length > 0) { - renderRandomVideos(window.allVideosData, 'videoGrid'); - } -}); - -// Refresh functions -function refreshMusic() { - if (window.allMusicData) { - renderMusicTracks(window.allMusicData, 'musicGrid'); - } -} - -function refreshPictures() { - if (window.allPicturesData) { - renderRandomPictures(window.allPicturesData, 'photoGrid'); - } -} - -function refreshVideos() { - if (window.allVideosData) { - renderRandomVideos(window.allVideosData, 'videoGrid'); - } -} - -// Render random videos (2 max) function renderRandomVideos(videosData, containerId) { const container = document.getElementById(containerId); if (!container) return; - // Shuffle and get first 2 const shuffled = shuffleArray(videosData); const selected = shuffled.slice(0, 2); - - // Clear container container.innerHTML = ''; - // Static poster image const posterImage = '/images/videoposter.png'; - - let html = ''; - selected.forEach(video => { - let videoUrl = ''; - let mimeType = 'video/mp4'; - let alt = video.content || ''; - let dim = ''; + let videoUrl = '', mimeType = 'video/mp4', alt = video.content || '', dim = ''; let isVertical = false; if (video.tags) { @@ -773,13 +322,10 @@ function renderRandomVideos(videosData, containerId) { const imetaStr = tag[1]; const urlMatch = imetaStr.match(/url ([^\s]+)/); if (urlMatch) videoUrl = urlMatch[1]; - const mimeMatch = imetaStr.match(/m ([^\s]+)/); if (mimeMatch) mimeType = mimeMatch[1]; - const altMatch = imetaStr.match(/alt ([^\s]+)/); if (altMatch) alt = altMatch[1]; - const dimMatch = imetaStr.match(/dim ([^\s]+)/); if (dimMatch) { dim = dimMatch[1]; @@ -792,49 +338,118 @@ function renderRandomVideos(videosData, containerId) { const date = new Date(video.created_at * 1000); const dateStr = date.toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric' + year: 'numeric', month: 'long', day: 'numeric' }); const wrapperStyle = isVertical - ? 'style="position:relative;padding-bottom:177.78%;height:0;overflow:hidden;background:var(--accent-black);"' - : ''; - + ? 'style="position:relative;padding-bottom:177.78%;height:0;overflow:hidden;background:var(--accent-black);"' + : ''; const videoStyle = isVertical - ? 'style="position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;"' - : 'style="width:100%;"'; + ? 'style="position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;"' + : 'style="width:100%;"'; - html += ` -
- ${videoUrl ? ` -
- -
- ` : ''} -
-

${alt}

-
- 📅 ${dateStr} - ${dim ? `📐 ${dim}` : ''} -
-
+ const html = ` +
+ ${videoUrl ? ` +
+
- `; + ` : ''} +
+

${alt}

+
+ 📅 ${dateStr} + ${dim ? `📐 ${dim}` : ''} +
+
+
+ `; + container.insertAdjacentHTML('beforeend', html); }); - - container.innerHTML = html; } -// ============ INITIALIZATION ============ +// ============================================ +// 6. REFRESH FUNCTIONS +// ============================================ + +function refreshMusic() { + if (window.allMusicData) renderMusicTracks(window.allMusicData, 'musicGrid'); +} + +function refreshPictures() { + if (window.allPicturesData) renderRandomPictures(window.allPicturesData, 'photoGrid'); +} + +function refreshVideos() { + if (window.allVideosData) renderRandomVideos(window.allVideosData, 'videoGrid'); +} + +// ============================================ +// 7. DOM CONTENT LOADED +// ============================================ document.addEventListener('DOMContentLoaded', function() { - // ... (your existing code for theme toggle, sticky nav, etc.) ... + // Sticky Navigation + const stickyNav = document.getElementById('stickyNav'); + const mainHeader = document.querySelector('.main-header'); + let lastScrollY = window.scrollY; + let ticking = false; - // Initialize Music (if you already have this) + function updateStickyNav() { + const scrollY = window.scrollY; + const headerHeight = mainHeader ? mainHeader.offsetHeight : 400; + if (scrollY > headerHeight - 100) { + stickyNav.classList.add('visible'); + } else { + stickyNav.classList.remove('visible'); + } + lastScrollY = scrollY; + } + + window.addEventListener('scroll', function() { + if (!ticking) { + window.requestAnimationFrame(function() { + updateStickyNav(); + ticking = false; + }); + ticking = true; + } + }); + + setTimeout(updateStickyNav, 100); + + // Read More toggle for blog posts + document.querySelectorAll('.read-more-toggle').forEach(button => { + button.addEventListener('click', function() { + const content = this.nextElementSibling; + if (content.style.display === 'none') { + content.style.display = 'block'; + this.innerHTML = 'Read Less'; + } else { + content.style.display = 'none'; + this.innerHTML = 'Read More'; + } + }); + }); + + // Smooth scrolling for navigation links + document.querySelectorAll('nav a').forEach(link => { + link.addEventListener('click', function(e) { + e.preventDefault(); + const targetId = this.getAttribute('href'); + const targetElement = document.querySelector(targetId); + if (targetElement) { + const navHeight = stickyNav.offsetHeight; + const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - navHeight - 20; + window.scrollTo({ top: targetPosition, behavior: 'smooth' }); + } + }); + }); + + // Initialize Music if (window.allMusicData && window.allMusicData.length > 0) { renderMusicTracks(window.allMusicData, 'musicGrid'); } @@ -842,6 +457,8 @@ document.addEventListener('DOMContentLoaded', function() { // Initialize Pictures if (window.allPicturesData && window.allPicturesData.length > 0) { renderRandomPictures(window.allPicturesData, 'photoGrid'); + } else { + initLightbox(); } // Initialize Videos @@ -850,21 +467,4 @@ document.addEventListener('DOMContentLoaded', function() { } }); -// Refresh functions for randomize buttons -function refreshMusic() { - if (window.allMusicData) { - renderMusicTracks(window.allMusicData, 'musicGrid'); - } -} - -function refreshPictures() { - if (window.allPicturesData) { - renderRandomPictures(window.allPicturesData, 'photoGrid'); - } -} - -function refreshVideos() { - if (window.allVideosData) { - renderRandomVideos(window.allVideosData, 'videoGrid'); - } -} +console.log('✅ Site initialized successfully!');