Last active
April 20, 2026 10:55
-
-
Save fsalamero/1d4423c579df7ffb5aac8500a699f6c3 to your computer and use it in GitHub Desktop.
Script para visualizar APOD entre fechas
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>Explorador APOD de la NASA</title> | |
| <!-- Configuración para App en iOS --> | |
| <meta name="apple-mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | |
| <meta name="apple-mobile-web-app-title" content="NASA APOD"> | |
| <!-- Icono para la pantalla de inicio (Emoji de cohete como fallback) --> | |
| <link rel="apple-touch-icon" href="https://img.icons8.com/fluency/180/000000/rocket.png"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #0b0d17; | |
| color: #e2e8f0; | |
| /* Evita el rebote de scroll en iOS para que parezca más nativo */ | |
| overscroll-behavior-y: contain; | |
| } | |
| .glass-card { | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(10px); | |
| border: 1px rgba(255, 255, 255, 0.1) solid; | |
| transition: transform 0.3s ease; | |
| } | |
| /* Ajuste para el área segura de iOS (notch) */ | |
| .safe-area-top { | |
| padding-top: env(safe-area-inset-top); | |
| } | |
| .loader { | |
| border: 4px solid rgba(255, 255, 255, 0.1); | |
| border-left-color: #3b82f6; | |
| border-radius: 50%; | |
| width: 40px; | |
| height: 40px; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| </style> | |
| </head> | |
| <body class="min-h-screen p-4 md:p-8 safe-area-top"> | |
| <div class="max-w-7xl mx-auto"> | |
| <header class="text-center mb-8 mt-4"> | |
| <h1 class="text-3xl md:text-5xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-500"> | |
| Archivo APOD | |
| </h1> | |
| </header> | |
| <!-- Controles --> | |
| <div class="glass-card rounded-2xl p-5 mb-8 flex flex-col gap-4"> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div class="flex flex-col"> | |
| <label class="text-[10px] uppercase font-bold mb-1 text-blue-300">Inicio</label> | |
| <input type="date" id="startDate" class="bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-sm text-white appearance-none"> | |
| </div> | |
| <div class="flex flex-col"> | |
| <label class="text-[10px] uppercase font-bold mb-1 text-blue-300">Fin</label> | |
| <input type="date" id="endDate" class="bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-sm text-white appearance-none"> | |
| </div> | |
| </div> | |
| <button onclick="fetchGallery()" class="w-full bg-blue-600 active:bg-blue-800 text-white font-bold py-3 rounded-xl transition-all duration-200 shadow-lg shadow-blue-900/20"> | |
| Actualizar Galería | |
| </button> | |
| </div> | |
| <div id="loading" class="hidden flex flex-col items-center justify-center py-10"> | |
| <div class="loader mb-4"></div> | |
| </div> | |
| <div id="gallery" class="grid grid-cols-1 sm:grid-cols-2 gap-4"></div> | |
| <div id="error-container" class="hidden text-center p-6 bg-red-900/20 border border-red-500/50 rounded-xl text-red-200 mt-4"> | |
| <p id="error-message" class="text-sm"></p> | |
| </div> | |
| </div> | |
| <script> | |
| const API_KEY = 'DEMO_KEY'; | |
| const galleryEl = document.getElementById('gallery'); | |
| const loaderEl = document.getElementById('loading'); | |
| const errorEl = document.getElementById('error-container'); | |
| const errorMessageEl = document.getElementById('error-message'); | |
| function setInitialDates() { | |
| const today = new Date(); | |
| const fifteenDaysAgo = new Date(); | |
| fifteenDaysAgo.setDate(today.getDate() - 14); | |
| const todayStr = today.toISOString().split('T')[0]; | |
| const startStr = fifteenDaysAgo.toISOString().split('T')[0]; | |
| document.getElementById('endDate').value = todayStr; | |
| document.getElementById('startDate').value = startStr; | |
| document.getElementById('endDate').max = todayStr; | |
| document.getElementById('startDate').max = todayStr; | |
| document.getElementById('startDate').min = "1995-06-16"; | |
| } | |
| async function fetchGallery() { | |
| const start = document.getElementById('startDate').value; | |
| const end = document.getElementById('endDate').value; | |
| galleryEl.innerHTML = ''; | |
| loaderEl.classList.remove('hidden'); | |
| errorEl.classList.add('hidden'); | |
| try { | |
| const response = await fetch(`https://api.nasa.gov/planetary/apod?api_key=${API_KEY}&start_date=${start}&end_date=${end}`); | |
| if (!response.ok) throw new Error('Error en la API'); | |
| const data = await response.json(); | |
| displayGallery(Array.isArray(data) ? data.reverse() : [data]); | |
| } catch (err) { | |
| errorMessageEl.innerText = "Error al cargar. Prueba más tarde."; | |
| errorEl.classList.remove('hidden'); | |
| } finally { | |
| loaderEl.classList.add('hidden'); | |
| } | |
| } | |
| function displayGallery(items) { | |
| items.forEach(item => { | |
| const dateParts = item.date.split('-'); | |
| const apodWebUrl = `https://apod.nasa.gov/apod/ap${dateParts[0].substring(2)}${dateParts[1]}${dateParts[2]}.html`; | |
| const card = document.createElement('div'); | |
| card.className = 'glass-card rounded-xl overflow-hidden flex flex-col active:scale-95 transition-transform'; | |
| card.onclick = () => window.open(apodWebUrl, '_blank'); | |
| const isVideo = item.media_type === 'video'; | |
| const mediaUrl = isVideo ? (item.thumbnail_url || '') : item.url; | |
| const mediaHtml = isVideo && !item.thumbnail_url | |
| ? `<div class="h-40 bg-gray-800 flex items-center justify-center"><span class="text-blue-400 font-bold">VIDEO</span></div>` | |
| : `<img src="${mediaUrl}" class="h-40 w-full object-cover" loading="lazy">`; | |
| card.innerHTML = ` | |
| ${mediaHtml} | |
| <div class="p-3"> | |
| <span class="text-[10px] font-mono text-blue-400">${item.date}</span> | |
| <h3 class="text-sm font-bold truncate">${item.title}</h3> | |
| </div> | |
| `; | |
| galleryEl.appendChild(card); | |
| }); | |
| } | |
| window.onload = () => { | |
| setInitialDates(); | |
| fetchGallery(); | |
| }; | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment