// Art Gallery Data const artworks = [ { id: 1, title: "Cyberpunk City Nights", artist: "NeonDreamer", category: "digital", image: "https://static.photos/technology/640x480/1", avatar: "https://static.photos/people/200x200/1", likes: 2453, views: "12.5K", comments: 89, description: "A futuristic cityscape inspired by Blade Runner and cyberpunk aesthetics. Created in Blender and Photoshop over 3 weeks.", tags: ["cyberpunk", "cityscape", "digital", "scifi"] }, { id: 2, title: "Ethereal Forest Spirit", artist: "MysticArt", category: "digital", image: "https://static.photos/nature/640x360/2", avatar: "https://static.photos/people/200x200/2", likes: 1892, views: "8.2K", comments: 56, description: "Digital painting of an ancient forest guardian. Exploring light and atmosphere in fantasy environments.", tags: ["fantasy", "forest", "spirit", "digitalpainting"] }, { id: 3, title: "Portrait Study #42", artist: "ArtStudent_99", category: "traditional", image: "https://static.photos/people/640x480/3", avatar: "https://static.photos/people/200x200/3", likes: 567, views: "2.1K", comments: 23, description: "Oil on canvas. 40x50cm. Working on skin tones and lighting from natural window light.", tags: ["oilpainting", "portrait", "traditional", "study"] }, { id: 4, title: "Abstract Emotions", artist: "ColorSplash", category: "digital", image: "https://static.photos/abstract/640x360/4", avatar: "https://static.photos/people/200x200/4", likes: 3201, views: "15K", comments: 134, description: "Exploring the relationship between color and emotion. Digital abstract composition using custom brushes.", tags: ["abstract", "colors", "emotional", "digital"] }, { id: 5, title: "Mountain Mist", artist: "NatureLens", category: "photography", image: "https://static.photos/nature/640x480/5", avatar: "https://static.photos/people/200x200/5", likes: 892, views: "4.5K", comments: 45, description: "Early morning shot in the Alps. Sony A7III with 24-70mm lens. f/8, 1/125s, ISO 100.", tags: ["photography", "landscape", "mountains", "nature"] }, { id: 6, title: "Mecha Warrior", artist: "RobotDesigner", category: "3d", image: "https://static.photos/technology/640x360/6", avatar: "https://static.photos/people/200x200/6", likes: 4521, views: "22K", comments: 201, description: "Hard surface modeling practice. Rendered in Octane. Concept inspired by Gundam and Pacific Rim.", tags: ["3d", "mecha", "robot", "scifi", "blender"] }, { id: 7, title: "Cherry Blossom Dreams", artist: "SakuraArtist", category: "anime", image: "https://static.photos/nature/640x480/7", avatar: "https://static.photos/people/200x200/7", likes: 5678, views: "28K", comments: 312, description: "Original character illustration. Clip Studio Paint. Focusing on soft lighting and spring atmosphere.", tags: ["anime", "manga", "illustration", "sakura"] }, { id: 8, title: "Urban Decay", artist: "StreetWalker", category: "photography", image: "https://static.photos/cityscape/640x360/8", avatar: "https://static.photos/people/200x200/8", likes: 1234, views: "6.7K", comments: 78, description: "Abandoned factory exploration. High contrast black and white processing to emphasize texture.", tags: ["urban", "abandoned", "blackwhite", "photography"] }, { id: 9, title: "Dragon's Lair", artist: "FantasyMaster", category: "digital", image: "https://static.photos/abstract/640x480/9", avatar: "https://static.photos/people/200x200/9", likes: 3892, views: "18K", comments: 156, description: "Epic fantasy scene. Photoshop and Wacom Cintiq. 40+ hours of work on the scales alone!", tags: ["dragon", "fantasy", "epic", "creature"] }, { id: 10, title: "Minimalist Architecture", artist: "CleanLines", category: "photography", image: "https://static.photos/minimal/640x360/10", avatar: "https://static.photos/people/200x200/10", likes: 2156, views: "9.3K", comments: 67, description: "Geometry in modern architecture. Shot in Tokyo. Minimal post-processing to maintain clean lines.", tags: ["architecture", "minimal", "geometry", "modern"] }, { id: 11, title: "Space Station Omega", artist: "StarTraveler", category: "3d", image: "https://static.photos/technology/640x480/11", avatar: "https://static.photos/people/200x200/11", likes: 1789, views: "7.8K", comments: 89, description: "Environment design for sci-fi game project. Blender and Unreal Engine 5. Real-time rendering.", tags: ["3d", "environment", "scifi", "space"] }, { id: 12, title: "Watercolor Sunset", artist: "BrushAndPaint", category: "traditional", image: "https://static.photos/nature/640x360/12", avatar: "https://static.photos/people/200x200/12", likes: 923, views: "3.4K", comments: 34, description: "Wet-on-wet technique. Winsor & Newton watercolors. Arches cold press 300gsm.", tags: ["watercolor", "traditional", "sunset", "seascape"] } ]; let currentCategory = 'all'; let currentSort = 'newest'; let likedArtworks = new Set(); // Initialize document.addEventListener('DOMContentLoaded', () => { lucide.createIcons(); renderGallery(); setupEventListeners(); checkNullUndefined(); }); // Error handling demonstration for undefined/null function checkNullUndefined() { // Simulating safe data access patterns const safeGet = (obj, path, defaultValue = null) => { try { return path.split('.').reduce((acc, part) => acc && acc[part] !== undefined ? acc[part] : defaultValue, obj); } catch (e) { console.warn('Null/Undefined value encountered:', path); return defaultValue; } }; // Example of handling potentially undefined data const testData = { artwork: { title: "Test", stats: null } }; // Safe access without errors const title = safeGet(testData, 'artwork.title', 'Untitled'); const likes = safeGet(testData, 'artwork.stats.likes', 0); console.log('Null/Undefined handling demo:', { title, likes }); } function renderGallery() { const gallery = document.getElementById('gallery'); const filtered = filterArtworks(); const sorted = sortArtworks(filtered); gallery.innerHTML = sorted.map(artwork => createArtworkCard(artwork)).join(''); lucide.createIcons(); // Update count document.getElementById('resultCount').textContent = `Showing ${sorted.length} artworks`; } function createArtworkCard(artwork) { const isLiked = likedArtworks.has(artwork.id); const likeClass = isLiked ? 'text-red-500 fill-current' : 'text-white'; return `
${artwork.title}

${artwork.title}

by ${artwork.artist}

${formatNumber(artwork.likes)} ${artwork.comments}
${artwork.category}
`; } function filterArtworks() { if (currentCategory === 'all') return artworks; return artworks.filter(art => art.category === currentCategory); } function sortArtworks(artworks) { const sorted = [...artworks]; switch(currentSort) { case 'popular': return sorted.sort((a, b) => b.likes - a.likes); case 'comments': return sorted.sort((a, b) => b.comments - a.comments); default: return sorted.sort((a, b) => b.id - a.id); } } function formatNumber(num) { if (num >= 1000) { return (num / 1000).toFixed(1) + 'K'; } return num; } function toggleLike(event, id) { event.stopPropagation(); if (likedArtworks.has(id)) { likedArtworks.delete(id); showToast('Removed from favorites'); } else { likedArtworks.add(id); showToast('Added to favorites'); // Add animation class const btn = event.currentTarget; btn.classList.add('like-anim'); setTimeout(() => btn.classList.remove('like-anim'), 300); } renderGallery(); } function openModal(id) { const artwork = artworks.find(a => a.id === id); if (!artwork) { console.error('Artwork not found:', id); return; } document.getElementById('modalImage').src = artwork.image; document.getElementById('modalTitle').textContent = artwork.title; document.getElementById('modalCategory').textContent = artwork.category.charAt(0).toUpperCase() + artwork.category.slice(1); document.getElementById('modalArtist').textContent = artwork.artist; document.getElementById('modalAvatar').src = artwork.avatar; document.getElementById('modalDescription').textContent = artwork.description; document.getElementById('modalViews').textContent = artwork.views; document.getElementById('modalComments').textContent = artwork.comments; document.getElementById('modalLikes').textContent = artwork.likes; // Render tags const tagsContainer = document.getElementById('modalTags'); tagsContainer.innerHTML = artwork.tags.map(tag => `#${tag}` ).join(''); // Setup like button in modal const likeBtn = document.getElementById('modalLikeBtn'); const isLiked = likedArtworks.has(id); updateModalLikeButton(likeBtn, isLiked); likeBtn.onclick = () => { if (likedArtworks.has(id)) { likedArtworks.delete(id); updateModalLikeButton(likeBtn, false); showToast('Removed from favorites'); } else { likedArtworks.add(id); updateModalLikeButton(likeBtn, true); showToast('Added to favorites'); } renderGallery(); }; document.getElementById('imageModal').classList.remove('hidden'); document.body.style.overflow = 'hidden'; lucide.createIcons(); } function updateModalLikeButton(btn, isLiked) { const icon = btn.querySelector('i'); if (isLiked) { icon.classList.add('text-red-500', 'fill-current'); icon.classList.remove('text-white'); } else { icon.classList.remove('text-red-500', 'fill-current'); icon.classList.add('text-white'); } } function closeModal() { document.getElementById('imageModal').classList.add('hidden'); document.body.style.overflow = ''; } function showToast(message) { const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toastMessage'); toastMessage.textContent = message; toast.classList.remove('translate-y-20', 'opacity-0'); setTimeout(() => { toast.classList.add('translate-y-20', 'opacity-0'); }, 3000); } function setupEventListeners() { // Filter buttons document.querySelectorAll('.filter-btn').forEach(btn => { btn.addEventListener('click', (e) => { document.querySelectorAll('.filter-btn').forEach(b => { b.classList.remove('active', 'bg-accent', 'text-dark'); b.classList.add('bg-card', 'border-gray-700'); }); e.target.classList.remove('bg-card', 'border-gray-700'); e.target.classList.add('active', 'bg-accent', 'text-dark'); currentCategory = e.target.dataset.category; renderGallery(); }); }); // Sort select document.getElementById('sortSelect').addEventListener('change', (e) => { currentSort = e.target.value; renderGallery(); }); // Search document.getElementById('searchInput').addEventListener('input', (e) => { const query = e.target.value.toLowerCase(); if (query === '') { renderGallery(); return; } const filtered = artworks.filter(art => art.title.toLowerCase().includes(query) || art.artist.toLowerCase().includes(query) || art.tags.some(tag => tag.toLowerCase().includes(query)) ); const gallery = document.getElementById('gallery'); gallery.innerHTML = filtered.map(artwork => createArtworkCard(artwork)).join(''); lucide.createIcons(); document.getElementById('resultCount').textContent = `Found ${filtered.length} artworks`; }); // Modal close document.getElementById('closeModal').addEventListener('click', closeModal); document.getElementById('imageModal').addEventListener('click', (e) => { if (e.target.id === 'imageModal') closeModal(); }); // Keyboard navigation document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); }); // Load more (simulation) document.getElementById('loadMoreBtn').addEventListener('click', () => { const loadingState = document.getElementById('loadingState'); const btn = document.getElementById('loadMoreBtn'); btn.classList.add('hidden'); loadingState.classList.remove('hidden'); setTimeout(() => { loadingState.classList.add('hidden'); btn.classList.remove('hidden'); showToast('More artworks loaded!'); // Duplicate existing artworks with new IDs to simulate more data const newArtworks = artworks.map(art => ({ ...art, id: art.id + artworks.length, likes: Math.floor(Math.random() * 5000) })); artworks.push(...newArtworks); renderGallery(); }, 1500); }); // Theme toggle let isDark = true; document.getElementById('themeToggle').addEventListener('click', () => { isDark = !isDark; const icon = document.querySelector('#themeToggle i'); if (isDark) { document.documentElement.classList.remove('light'); icon.setAttribute('data-lucide', 'moon'); } else { document.documentElement.classList.add('light'); icon.setAttribute('data-lucide', 'sun'); } lucide.createIcons(); }); } // Handle null/undefined image errors globally window.addEventListener('error', (e) => { if (e.target.tagName === 'IMG') { console.warn('Image failed to load:', e.target.src); e.target.src = 'https://static.photos/abstract/640x480/999'; e.target.onerror = null; // Prevent infinite loop } }, true);