diff --git a/themes/one-pager/assets/js/scripts.js b/themes/one-pager/assets/js/scripts.js index 8c9d0a4..f42a1c2 100644 --- a/themes/one-pager/assets/js/scripts.js +++ b/themes/one-pager/assets/js/scripts.js @@ -1,16 +1,188 @@ -// ============================================ -// 1. UTILITY FUNCTIONS -// ============================================ +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); + -// 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; -} + + + // 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!'); // Artist filter list const allowedArtists = [ @@ -22,170 +194,58 @@ 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); }); } -// ============================================ -// 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'); - // FIX: Use dataset values directly - const date = item.dataset.date || ''; - const pubkey = item.dataset.pubkey || ''; - const alt = item.dataset.alt || ''; - if (image) { - currentPhotos.push({ - url: image.src, - alt: alt || image.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; - - // FIX: Show date and pubkey in the meta - let metaText = ''; - if (photo.date) metaText += `📅 ${photo.date}`; - - lightboxMeta.textContent = metaText || '📅 No date available'; - - 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 -// ============================================ - +// Render music tracks (randomized, 6 at a time, filtered by artist) 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; } - const shuffled = shuffleArray(filteredTracks); + // Shuffle and get first 6 + const shuffled = filteredTracks.sort(() => 0.5 - Math.random()); 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]; @@ -201,8 +261,10 @@ 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'; @@ -233,10 +295,13 @@ 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); @@ -256,20 +321,195 @@ function handleLyricsToggle() { } } -// ============================================ -// 4. PICTURES RENDERER -// ============================================ +// 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) + 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 || ''; @@ -277,54 +517,254 @@ 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]; + if (urlMatch) { + imageUrl = urlMatch[1]; + } + // Extract alt 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; item.dataset.alt = alt; - // FIX: Set the date in the dataset 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.dataset.date = dateStr; - item.dataset.pubkey = picture.pubkey || ''; item.innerHTML = ` - ${imageUrl ? `${alt}` : ''} -
📅 ${dateStr}
- `; + ${imageUrl ? ` + ${alt} + ` : ''} +
+ 📅 ${dateStr} +
+ `; - container.appendChild(item); + container.appendChild(item); }); - initLightbox(); + // Re-initialize the custom lightbox for new images + initCustomLightbox(); } -// ============================================ -// 5. VIDEOS RENDERER -// ============================================ +// Initialize custom lightbox (call this after any DOM updates) +function initCustomLightbox() { + const lightbox = document.getElementById('customLightbox'); + if (!lightbox) return; + 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 = '', mimeType = 'video/mp4', alt = video.content || '', dim = ''; + let videoUrl = ''; + let mimeType = 'video/mp4'; + let alt = video.content || ''; + let dim = ''; let isVertical = false; if (video.tags) { @@ -333,10 +773,13 @@ 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]; @@ -349,118 +792,49 @@ 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%;"'; - const html = ` -
- ${videoUrl ? ` -
- + html += ` +
+ ${videoUrl ? ` +
+ +
+ ` : ''} +
+

${alt}

+
+ 📅 ${dateStr} + ${dim ? `📐 ${dim}` : ''} +
+
- ` : ''} -
-

${alt}

-
- 📅 ${dateStr} - ${dim ? `📐 ${dim}` : ''} -
-
-
- `; - container.insertAdjacentHTML('beforeend', html); + `; }); + + container.innerHTML = html; } -// ============================================ -// 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 -// ============================================ +// ============ INITIALIZATION ============ document.addEventListener('DOMContentLoaded', function() { - // Sticky Navigation - const stickyNav = document.getElementById('stickyNav'); - const mainHeader = document.querySelector('.main-header'); - let lastScrollY = window.scrollY; - let ticking = false; + // ... (your existing code for theme toggle, sticky nav, etc.) ... - 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 + // Initialize Music (if you already have this) if (window.allMusicData && window.allMusicData.length > 0) { renderMusicTracks(window.allMusicData, 'musicGrid'); } @@ -468,8 +842,6 @@ document.addEventListener('DOMContentLoaded', function() { // Initialize Pictures if (window.allPicturesData && window.allPicturesData.length > 0) { renderRandomPictures(window.allPicturesData, 'photoGrid'); - } else { - initLightbox(); } // Initialize Videos @@ -478,4 +850,21 @@ document.addEventListener('DOMContentLoaded', function() { } }); -console.log('✅ Site initialized successfully!'); +// 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'); + } +} diff --git a/themes/one-pager/layouts/partials/footer.html b/themes/one-pager/layouts/partials/footer.html index bb23496..283d0c4 100644 --- a/themes/one-pager/layouts/partials/footer.html +++ b/themes/one-pager/layouts/partials/footer.html @@ -49,8 +49,8 @@ Built with for the nostr community