Your shiny Pokémon will appear here!
No Pokémon found matching your criteria. Try different filters!
`; return; } const scrollWrapper = document.createElement('div'); scrollWrapper.className = 'spg-scroll-wrapper'; const resultsContainer = document.createElement('div'); resultsContainer.className = 'spg-results-container'; pokemonList.forEach(pokemon => { const card = document.createElement('div'); card.className = 'spg-poke-card'; const name = pokemon.name; const spriteName = name.toLowerCase().replace(/[\s'.]/g, "").replace('♀', '-f').replace('♂', '-m'); const img = document.createElement('img'); img.src = POKE_SPRITE_URL.replace('{name}', spriteName); img.alt = `Shiny ${capitalize(name)}`; img.onerror = function() { this.onerror = null; this.src = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/${pokemon.id}.png`; this.style.imageRendering = 'auto'; this.onerror = function() { this.onerror = null; this.src = 'https://placehold.co/120x120/e9ecef/6c757d?text=?'; this.alt = 'Image not found'; } }; const nameEl = document.createElement('p'); nameEl.textContent = capitalize(name); card.appendChild(img); card.appendChild(nameEl); resultsContainer.appendChild(card); }); // Add scroll buttons const leftBtn = document.createElement('button'); leftBtn.className = 'spg-scroll-btn spg-scroll-btn-left'; leftBtn.innerHTML = ``; const rightBtn = document.createElement('button'); rightBtn.className = 'spg-scroll-btn spg-scroll-btn-right'; rightBtn.innerHTML = ``; scrollWrapper.appendChild(leftBtn); scrollWrapper.appendChild(resultsContainer); scrollWrapper.appendChild(rightBtn); outputDiv.appendChild(scrollWrapper); // Scroll button logic const updateScrollButtons = () => { const hasOverflow = resultsContainer.scrollWidth > resultsContainer.clientWidth; if (!hasOverflow) { leftBtn.classList.remove('visible'); rightBtn.classList.remove('visible'); return; } // Check left edge leftBtn.classList.toggle('visible', resultsContainer.scrollLeft > 0); // Check right edge (with a small tolerance) rightBtn.classList.toggle('visible', resultsContainer.scrollLeft < resultsContainer.scrollWidth - resultsContainer.clientWidth - 1); }; leftBtn.addEventListener('click', () => { resultsContainer.scrollLeft -= resultsContainer.clientWidth * 0.7; }); rightBtn.addEventListener('click', () => { resultsContainer.scrollLeft += resultsContainer.clientWidth * 0.7; }); resultsContainer.addEventListener('scroll', updateScrollButtons, { passive: true }); // Use a timeout to ensure layout is calculated before checking for overflow setTimeout(updateScrollButtons, 100); } async function getFullPokemonList() { if (allPokemonList.length > 0) return allPokemonList; const data = await cachedFetch(`${API_BASE}pokemon?limit=1302`); allPokemonList = data.results; return allPokemonList; } async function handleGenerate(mode) { showLoading(); try { let count, potentialPokemon; if (mode === 'random') { count = parseInt(randomCountSelect.value, 10); const allPokes = await getFullPokemonList(); potentialPokemon = [...allPokes]; } else { count = parseInt(filterCountSelect.value, 10); potentialPokemon = await getFilteredPokemon(); } if (potentialPokemon.length === 0) { renderResults([]); return; } const selectedPokemon = []; const fetchedDetails = new Map(); while (selectedPokemon.length < count && potentialPokemon.length > 0) { const index = Math.floor(Math.random() * potentialPokemon.length); const chosen = potentialPokemon.splice(index, 1)[0]; if (!fetchedDetails.has(chosen.url)) { const details = await cachedFetch(chosen.url); fetchedDetails.set(chosen.url, details); } const fullDetails = fetchedDetails.get(chosen.url); selectedPokemon.push({name: chosen.name, id: fullDetails.id}); } renderResults(selectedPokemon); } catch (error) { console.error("Generation failed:", error); outputDiv.innerHTML = `An error occurred. Please try again.
`; } finally { hideLoading(); } } async function getFilteredPokemon() { const gen = genSelect.value; const type = typeSelect.value; const evolution = evolutionSelect.value; let listsToIntersect = []; if (gen !== 'any') { const data = await cachedFetch(`${API_BASE}generation/${gen}`); listsToIntersect.push(data.pokemon_species.map(p => p.name)); } if (type !== 'any') { const data = await cachedFetch(`${API_BASE}type/${type}`); listsToIntersect.push(data.pokemon.map(p => p.pokemon.name)); } let filteredList; if (listsToIntersect.length > 0) { filteredList = listsToIntersect.reduce((a, b) => a.filter(c => b.includes(c))); } else { const allPokes = await getFullPokemonList(); filteredList = allPokes.map(p => p.name); } let pokemonObjects = filteredList.map(name => ({ name, url: `${API_BASE}pokemon/${name}` })); if (evolution !== 'any') { const evolutionFiltered = []; for (const pokemon of pokemonObjects) { try { const speciesData = await cachedFetch(`${API_BASE}pokemon-species/${pokemon.name.split('-')[0]}/`); const isBase = speciesData.evolves_from_species === null; if (evolution === 'base' && isBase) { evolutionFiltered.push(pokemon); continue; } if (evolution === 'final') { const evolutionChainData = await cachedFetch(speciesData.evolution_chain.url); let chain = evolutionChainData.chain; let isFinal = false; while(chain) { if (chain.species.name === pokemon.name && chain.evolves_to.length === 0) { isFinal = true; break; } if (chain.evolves_to.length > 0) { let foundInNext = false; for (const next of chain.evolves_to) { if (next.species.name === pokemon.name && next.evolves_to.length === 0) { isFinal = true; foundInNext = true; break; } } if (foundInNext) break; chain = chain.evolves_to[0]; } else { chain = null; } } if(isFinal) evolutionFiltered.push(pokemon); } } catch (e) { /* Ignore failures */ } } return evolutionFiltered; } return pokemonObjects; } // --- Event Listeners --- randomGenerateBtn.addEventListener('click', () => handleGenerate('random')); filteredGenerateBtn.addEventListener('click', () => handleGenerate('filtered')); // --- Initializer Call --- setupTabs(); populateFilters(); } })();