diff --git a/css/styles.css b/css/styles.css index 918573c..3d5790c 100644 --- a/css/styles.css +++ b/css/styles.css @@ -7,6 +7,24 @@ :root { --bgColor: #ffffff; + --normal: #999999; + --fighting: #C03028; + --flying: #A890F0; + --poison: #A040A0; + --ground: #E0C068; + --rock: #B8A038; + --bug: #A8B820; + --ghost: #705898; + --steel: #B8B8D0; + --fire: #F08030; + --water: #6890F0; + --grass: #78C850; + --electric: #F8D030; + --psychic: #F85888; + --ice: #98D8D8; + --dragon: #7038F8; + --dark: #41314e; + --fairy: #EE99AC; } @@ -33,7 +51,7 @@ body { } #landing { - width: 100vw; + width: 100%; height: 100vh; display: flex; justify-content: center; @@ -69,6 +87,7 @@ h1 { font-weight: 600; text-align: center; user-select: none; + white-space: nowrap; } #titleBanner { @@ -82,7 +101,7 @@ h1 { } .black { - background-color: #000; + background-color: #000; /* Reference[0] https://www.w3schools.com/howto/howto_css_cutout_text.asp */ color: #fff; mix-blend-mode: multiply; } @@ -120,7 +139,7 @@ h1 { background-color: #101010; overflow: auto; color: white; - padding: 50px; + padding: 10px; } p { @@ -173,6 +192,667 @@ h3 { border-bottom: none; } +/* FULLSCREEN STYLES */ +#fullscreen { + position: fixed; + height: 100vh; + width: 100%; + background: #1118; + z-index: 100; + backdrop-filter: blur(10px); + top: 0px; + left: 0px; + display: none; +} + +#fullscreen.visible { + display: flex; +} + +#pokemon-display { + height: calc(100% - 90px); + width: calc(100% - 90px); + background: white; + margin: auto; + align-self: center; + display: flex; + flex-direction: column; + border-radius: 26px; + padding: 10px; + overflow-y: scroll; + min-width: 260px; + position: relative; +} + +#fullscreen #close-button { + position: absolute; + top: 2px; + right: 0px; + height: 40px; + width: 40px; + border: 0; + background: #fff0; + font-size: 1.5em; + cursor: pointer; + color: white; + text-shadow: black 1px 1px 3px; +} + +#pokemon-display #type-container { + position: absolute; + top: 0; + right: 0; + margin-top: 8px; + margin-right: 10px; +} + +#pokemon-display .type-icon { + width: 40px; + height: 40px; + margin-left: 10px; +} + +#pokemon-display #image-container { + display: flex; + justify-content: center; + align-items: center; + max-height: 475px; + background: #ddd; + background-image: linear-gradient(to right, transparent 0px, #999 1px, transparent 2px), linear-gradient(to bottom, transparent 0px, #999 1px, transparent 2px); + background-size: 35px 35px; + animation: transform 2.5s infinite; + animation-timing-function: linear; + border-radius: 15px; + margin-bottom: 8px; +} + +#pokemon-display #dex-and-type-container { + display: flex; + flex-direction: column; + width: 100%; +} + +#pokemon-display img { + width: 100%; +} + +#description-carousel { + display: flex; + flex-direction: row; + background: #222; + border-radius: 15px; + padding-left: 28px; + padding-right: 28px; + margin: 0; + margin-bottom: 8px; + overflow: hidden; + height: fit-content; + position: relative; + overflow-y: scroll; +} + +#description-carousel .description-carousel-item { + color: white; + width: 100%; + flex: 0 0 100%; + height: fit-content; + margin-right: 33px; + transition: transform 0.3s ease; +} + +#description-carousel #carousel-left-button { + top: 0; + left: 0; + text-align: left; +} + +#description-carousel #carousel-right-button { + top: 0; + right: 0; + text-align: right; +} + +#description-carousel button { + position: absolute; + height: 100%; + border: none; + background: #0000; + color: white; + width: 30px; + cursor: pointer; +} + +#description-carousel button:hover { + font-size: 16px; +} + +#description-carousel .description-text { + left: 0; + color: #ccc; + font-style: italic; + font-weight: 250; +} + +#description-carousel .description-game { + left: 0; + font-size: 1.2em; + font-weight: 400; + padding-bottom: 10px; + border-bottom: 1px solid #ccc; +} + +@keyframes transform { + from { + background-position: 0px 0px; + } + to { + background-position: 35px 35px; + } +} + +#pokemon-display .pokemon-name { + font-size: 1.2em; + font-weight: 500; + margin: 0; + margin-left: 10px; + margin-bottom: 10px; + text-align: left; +} + +#pokemon-display .pokemon-id { + font-size: 1.6em; + font-weight: 150; + margin: 0; + margin-left: 10px; + position: relative; + overflow: visible; + height: 0; + width: 0; + text-shadow: #fff 0px 0px 5px; +} + +#statblock .stat-empty { + color: #bbb; +} + +#statblock #hp .stat-filled { + color: #90d060; +} + +#statblock #attack .stat-filled { + color: #ffd060; +} + +#statblock #defense .stat-filled { + color: #f09040; +} + +#statblock #special-attack .stat-filled { + color: #40a0f0; +} + +#statblock #special-defense .stat-filled { + color: #6b30ff; +} + +#statblock #speed .stat-filled { + color: #ff40ff; +} + +#statblock { + background-color: #222; + color: white; + display: flex; + flex-direction: column; + align-items: center; + padding: 10px; + height: fit-content; + border-radius: 15px; +} + +.stat-bar { + display: grid; + margin-bottom: 8px; +} + +#statblock .stat-name { + grid-column: 1; + grid-row: 1; +} + +#statblock .stat-value { + grid-column: 2; + grid-row: 1; + text-align: right; +} + +#statblock .stat-graphic { + grid-column: 1 / span 2; + grid-row: 2; +} + +#statblock p { + margin: 0; +} + +#image-and-stats { + width: 100%; + display: flex; + flex-direction: column; + margin-bottom: 8px; +} + +#type-chart-container { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + width: 100%; + grid-template-rows: 60px 1fr; + background-color: #222; + border-radius: 15px; + color: #ccc; + margin-bottom: 8px; + height: fit-content; +} + +#type-chart-container #type-chart-title { + grid-column: 1 / span 5; + margin: 0; + margin-top: 10px; + margin-left: 10px; + color: white; + font-weight: 400; + text-align: center; + padding-bottom: 5px; + border-bottom: 1px solid #ccc; + font-size: 1.2em; +} + +.type-chart-title { + display: flex; + flex-direction: column; + text-align: center; + height: 40px; + border-bottom: 1px solid #ccc; + justify-content: center; + padding: 2px; +} + +.type-chart-element { + display: flex; + flex-direction: column; + justify-content: center; + color: black; + text-align: center; + margin: 2px; + height: 18px; + border-radius: 9px; +} + +#noeffect-column, #doubleresist-column, #resist-column, #weak-column { + border-right: 1px solid #ccc; +} + +#type-chart-container .type-chart-column { + display: flex; + flex-direction: column; + font-size: 0.6em; + padding: 0; + list-style-type: none; +} + +/* POKEDEX STYLES */ + +#pokedex { + max-width: 1440px; + margin: auto; + margin-bottom: 50px; +} + +#generation-selector, #type-selector { + overflow-x: auto; +} + +#pokedex h2 { + text-align: center; +} + +#generation-selector { + display: flex; +} + +#generation-selector input { + display: none; +} + +#generation-selector label { + display: inline-block; + padding: 10px; + margin: 10px; + min-height: 40px; + min-width: 40px; + text-align: center; + text-shadow: black 0px 0px 3px; + line-height: 40px; + border-radius: 30px; + background-color: #202020; + color: white; + cursor: pointer; + transition: background-color 0.5s; +} + +#generation-selector label:hover { + background-color: #303030; +} + +#generation-selector input:checked + label { + background-color: var(--bgColor); +} + +#type-selector { + display: flex; +} + +#type-selector input { + display: none; +} + +#type-selector label { + display: inline-block; + padding: 10px; + margin: 10px; + height: 40px; + width: 40px; + text-align: center; + line-height: 40px; + border-radius: 30px; + background-color: #202020; + color: white; + cursor: pointer; + transition: background-color 0.5s; +} + +#type-selector label img { + width: 40px; + height: 40px; + font-size: 10px; +} + +#type-selector label:hover { + background-color: #303030; +} + +#type-selector input:checked + label { + background-color: var(--bgColor); +} + +#normal:checked + label { + background-color: var(--normal) !important; +} + +#fighting:checked + label { + background-color: var(--fighting) !important; +} + +#flying:checked + label { + background-color: var(--flying) !important; +} + +#poison:checked + label { + background-color: var(--poison) !important; +} + +#ground:checked + label { + background-color: var(--ground) !important; +} + +#rock:checked + label { + background-color: var(--rock) !important; +} + +#bug:checked + label { + background-color: var(--bug) !important; +} + +#ghost:checked + label { + background-color: var(--ghost) !important; +} + +#steel:checked + label { + background-color: var(--steel) !important; +} + +#fire:checked + label { + background-color: var(--fire) !important; +} + +#water:checked + label { + background-color: var(--water) !important; +} + +#grass:checked + label { + background-color: var(--grass) !important; +} + +#electric:checked + label { + background-color: var(--electric) !important; +} + +#psychic:checked + label { + background-color: var(--psychic) !important; +} + +#ice:checked + label { + background-color: var(--ice) !important; +} + +#dragon:checked + label { + background-color: var(--dragon) !important; +} + +#dark:checked + label { + background-color: var(--dark) !important; +} + +#fairy:checked + label { + background-color: var(--fairy) !important; +} + +.normal-primary-type { + background-color: var(--normal) !important; +} + +.normal-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--normal) , 0px 0px 0px 0px transparent !important; +} + +.fighting-primary-type { + background-color: var(--fighting) !important; +} + +.fighting-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--fighting) , 0px 0px 0px 0px transparent !important; +} + +.flying-primary-type { + background-color: var(--flying) !important; +} + +.flying-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--flying) , 0px 0px 0px 0px transparent !important; +} + +.poison-primary-type { + background-color: var(--poison) !important; +} + +.poison-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--poison) , 0px 0px 0px 0px transparent !important; +} + +.ground-primary-type { + background-color: var(--ground) !important; +} + +.ground-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--ground) , 0px 0px 0px 0px transparent !important; +} + +.rock-primary-type { + background-color: var(--rock) !important; +} + +.rock-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--rock) , 0px 0px 0px 0px transparent !important; +} + +.bug-primary-type { + background-color: var(--bug) !important; +} + +.bug-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--bug) , 0px 0px 0px 0px transparent !important; +} + +.ghost-primary-type { + background-color: var(--ghost) !important; +} + +.ghost-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--ghost) , 0px 0px 0px 0px transparent !important; +} + +.steel-primary-type { + background-color: var(--steel) !important; +} + +.steel-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--steel) , 0px 0px 0px 0px transparent !important; +} + +.fire-primary-type { + background-color: var(--fire) !important; +} + +.fire-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--fire) , 0px 0px 0px 0px transparent !important; +} + +.water-primary-type { + background-color: var(--water) !important; +} + +.water-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--water) , 0px 0px 0px 0px transparent !important; +} + +.grass-primary-type { + background-color: var(--grass) !important; +} + +.grass-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--grass) , 0px 0px 0px 0px transparent !important; +} + +.electric-primary-type { + background-color: var(--electric) !important; +} + +.electric-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--electric) , 0px 0px 0px 0px transparent !important; +} + +.psychic-primary-type { + background-color: var(--psychic) !important; +} + +.psychic-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--psychic) , 0px 0px 0px 0px transparent !important; +} + +.ice-primary-type { + background-color: var(--ice) !important; +} + +.ice-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--ice) , 0px 0px 0px 0px transparent !important; +} + +.dragon-primary-type { + background-color: var(--dragon) !important; +} + +.dragon-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--dragon) , 0px 0px 0px 0px transparent !important; +} + +.dark-primary-type { + background-color: var(--dark) !important; +} + +.dark-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--dark) , 0px 0px 0px 0px transparent !important; +} + +.fairy-primary-type { + background-color: var(--fairy) !important; +} + +.fairy-secondary-type { + box-shadow: inset 0px -150px 100px -100px var(--fairy) , 0px 0px 0px 0px transparent !important; +} + +#pokemon-container { + display: flex; + flex-direction: row; + overflow-x: auto; + overflow-y: hidden; + width: 100%; + height: 230px; +} + +.pokemon-card { + height: 200px; + min-width: 300px; + background-color: #444; + margin: 10px; + border-radius: 40px; + overflow: hidden; + cursor: pointer; +} + +.pokemon-card img { + position: relative; + top: -120px; + left: 100px; + height: 192px; + width: 192px; + margin: 10px; + image-rendering: pixelated; + color: black; +} + +.pokemon-card .pokemon-name { + font-weight: 500; + font-size: 1.15em; + position: relative; + margin: 0; + top: 15px; + left: 15px; + z-index: 1; + text-shadow: black 1px 1px 3px; +} + +.pokemon-card .pokemon-id { + font-weight: 100; + font-size: 3.5em; + position: relative; + margin: 0; + top: 15px; + left: 15px; + z-index: 0; + text-shadow: #0008 1px 1px 2px; +} + /* CAROUSEL STYLES */ #carousel { @@ -260,6 +940,49 @@ h3 { /* applies to screens wider than 499px */ + /* FULLSCREEN STYLES */ + #pokemon-display { + height: calc(100% - 120px); + width: calc(100% - 120px); + border-radius: 30px; + padding: 20px; + max-height: 600px; + max-width: 900px; + padding-bottom: 12px; + } + + #image-and-stats { + width: 100%; + display: grid; + grid-template-columns: 1fr 280px; + } + + #pokemon-display #type-container { + margin-top: 14px; + margin-right: 20px; + } + + #pokemon-display img { + grid-column: 1; + max-height: 440px; + max-width: 440px; + justify-self: center; + } + + #pokemon-display #image-container { + display: flex; + grid-column: 1; + width: calc(100% - 10px); + justify-content: center; + margin: 0; + margin-right: 10px; + } + + #statblock { + grid-column: 2; + width: 260px; + } + } @@ -279,11 +1002,10 @@ h3 { } .carousel-item { - min-width: 200px; height: 800px; - width: 150px; - min-width: 150px; - border-radius: 74px; + width: 146px; + min-width: 146px; + border-radius: 75px; } .carousel-item .carousel-id { @@ -305,12 +1027,105 @@ h3 { } .content-column { - max-width: 1300px; + max-width: 1440px; margin: auto; } + /* FULLSCREEN STYLES */ + + #pokemon-display { + border-radius: 35px; + padding: 20px; + max-height: 755px; + max-width: 1120px; + overflow: hidden; + } + + #fullscreen #close-button { + top: 20px; + right: 20px; + height: 60px; + width: 60px; + font-size: 2em; + } + + #pokemon-contents { + display: grid; + grid-template-columns: 475px 1fr; + grid-column-gap: 8px; + grid-template-rows: auto; + } + + #pokemon-display .pokemon-name { + font-size: 2.2em; + text-align: left; + margin-bottom: 5px; + transform: translate(0px, -8px); + } + + #pokemon-display .pokemon-id { + font-size: 2.5em; + } + + #pokemon-display #type-container { + margin-top: 15px; + margin-right: 25px; + } + + #pokemon-display .type-icon { + width: 60px; + height: 60px; + } + + #image-and-stats { + width: 475px; + display: flex; + flex-direction: column; + grid-column: 1; + grid-row: 1 / span 2; + } + + #pokemon-display img { + max-height: 475px; + max-width: 475px; + height: 475px; + width: 475px; + } + + #pokemon-display #image-container { + max-height: 475px; + max-width: 475px; + height: 475px; + width: 475px; + margin: 0; + margin-bottom: 8px; + } + + #statblock { + flex-direction: row; + flex-wrap: wrap; + width: 455px; + font-size: 0.85em !important; + justify-content: space-between; + grid-column: 1; + grid-row: 2; + } + + #description-carousel { + grid-column: 2; + height: 338px; + } + + #type-chart-container { + height: 337px; + } + /* BELOW LANDING STYLES */ + #below-landing { + padding: 50px; + } + .feature-showcase { display: grid; grid-template-columns: 1fr 1fr; @@ -344,4 +1159,18 @@ h3 { grid-row: 3 / 3; } + /* POKEDEX STYLES */ + + #generation-selector { + justify-content: center; + } + } + +h1 { + margin: auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} \ No newline at end of file diff --git a/index.html b/index.html index a86b839..c09f70c 100644 --- a/index.html +++ b/index.html @@ -17,8 +17,16 @@ - +
+
+

Pokedex

+
+
+
+

Select any creature to view its profile. You can use the top buttons to toggle creatures from different generations or use the bottom buttons to toggle creatures with a specific type.

+
+

Features

diff --git a/js/scripts.js b/js/scripts.js index 41b87f1..40c4e36 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -1,4 +1,6 @@ -console.log("hello"); +let controller = new AbortController(); // Reference[1] https://levelup.gitconnected.com/asynchronous-tasks-got-you-down-heres-how-to-cancel-them-480801e69ae5 +let signal = controller.signal; +let activePokemonCarousel = 0; // COLOUR PICKER BACKGROUND @@ -175,6 +177,696 @@ function transitionLake() { transitionBackground("assets/photos/hintersee-3601004.jpg", "white"); } +// POKEDEX + +async function initPokedex() { + await populateGenerations(); + await populateTypes(); + populatePokedex(signal); +} + +async function populateGenerations() { + const container = document.getElementById("generation-selector"); + const url = "https://pokeapi.co/api/v2/generation/"; + const response = await fetch(url); + const data = await response.json(); + + let generations = []; + let generationsURL = []; + + const genArray = data.results; + for (let i = 0; i < genArray.length; i++) { + const gen = genArray[i]; + const genName = gen.name; + const genId = genName.split("-")[1].toUpperCase(); + generations.push(genId); + generationsURL.push(gen.url); + } + + for (let i = 0; i < generations.length; i++) { + const gen = generations[i]; + const genButton = document.createElement("input"); + genButton.type = "checkbox"; + genButton.value = generationsURL[i]; + genButton.id = `gen${gen}`; + if (gen === "I") { + genButton.checked = true; + } else { + genButton.checked = false; + } + container.appendChild(genButton); + const genLabel = document.createElement("label"); + genLabel.htmlFor = `gen${gen}`; + genLabel.innerHTML = `${gen}`; + container.appendChild(genLabel); + } +} + +function getSelectedGenerations() { + const container = document.getElementById("generation-selector"); + const checkboxes = container.getElementsByTagName("input"); + let selected = []; + + for (let i = 0; i < checkboxes.length; i++) { + if (checkboxes[i].checked) { + selected.push(checkboxes[i].value); + } + } + + return(selected); +} + +async function populateTypes() { + const container = document.getElementById("type-selector"); + const url = "https://pokeapi.co/api/v2/type"; + const response = await fetch(url); + const data = await response.json(); + + let types = []; + + const typeArray = data.results; + for (let i = 0; i < typeArray.length; i++) { + const type = typeArray[i]; + const typeResponse = await fetch(type.url); + const typeData = await typeResponse.json(); + if (typeData.id < 1000) { + types.push(type.name); + } + } + + for (let i = 0; i < types.length; i++) { + const type = types[i]; + const typeButton = document.createElement("input"); + typeButton.type = "checkbox"; + typeButton.value = type; + typeButton.id = type; + if (type === "normal") { + typeButton.checked = true; + } else { + typeButton.checked = false; + } + container.appendChild(typeButton); + + const typeImg = document.createElement("img"); + typeImg.src = `https://raw.githubusercontent.com/duiker101/pokemon-type-svg-icons/5781623f147f1bf850f426cfe1874ba56a9b75ee/icons/${type}.svg`; + typeImg.alt = `${type}`; + + const typeLabel = document.createElement("label"); + typeLabel.htmlFor = type; + typeLabel.appendChild(typeImg); + container.appendChild(typeLabel); + } +} + +function getSelectedTypes() { + const container = document.getElementById("type-selector"); + const checkboxes = container.getElementsByTagName("input"); + let selected = []; + + for (let i = 0; i < checkboxes.length; i++) { + if (checkboxes[i].checked) { + selected.push(checkboxes[i].value); + } + } + + return selected; +} + +async function populatePokedex(signal) { + const container = document.getElementById("pokemon-container"); + container.innerHTML = ""; + const types = await getSelectedTypes(); + const generations = await getSelectedGenerations(); + for (let i = 0; i < generations.length; i++) { + if (signal.aborted) { + return; + } + const url = generations[i]; + const response = await fetch(url); + const data = await response.json(); + const pokemon = data.pokemon_species; + for (let j = 0; j < pokemon.length; j++) { + if (signal.aborted) { + return; + } + const poke = pokemon[j]; + const speciesURL = poke.url; + const speciesResponse = await fetch(speciesURL); + const speciesData = await speciesResponse.json(); + const varieties = speciesData.varieties; + for (let k = 0; k < varieties.length; k++) { + if (signal.aborted) { + return; + } + const variety = varieties[k]; + const varietyURL = variety.pokemon.url; + const varietyResponse = await fetch(varietyURL); + const varietyData = await varietyResponse.json(); + const varietyTypes = varietyData.types; + if (types.includes(varietyTypes[0].type.name)) { + if (signal.aborted) { + return; + } + addPokemon(varietyData); + } else { + try { + if (types.includes(varietyTypes[1].type.name)) { + if (signal.aborted) { + return; + } + addPokemon(varietyData); + } + } catch { + //no second type + } + } + } + } + } +} + +function addPokemon(pokemonData) { + const container = document.getElementById("pokemon-container"); + + // if (container.childElementCount > 100) { + // return; + // } + + const pokemon = document.createElement("div"); + pokemon.className = "pokemon-card"; + pokemon.classList.add(pokemonData.types[0].type.name + "-primary-type"); + if (pokemonData.types[1]) { + pokemon.classList.add(pokemonData.types[1].type.name + "-secondary-type"); + } + pokemon.id = pokemonData.name; + + const pokemonName = document.createElement("p"); + pokemonName.className = "pokemon-name"; + let formatedName = ""; + pokemonData.name.split("-").forEach((word) => { + formatedName += word.charAt(0).toUpperCase() + word.slice(1) + " "; + }); + if (formatedName.length > 20) { + formatedName = formatedName.slice(0, formatedName.length - 1); + formatedName = formatedName.slice(0, formatedName.lastIndexOf(" ")); + } + pokemonName.innerHTML = formatedName; + pokemon.appendChild(pokemonName); + + const pokemonId = document.createElement("p"); + pokemonId.className = "pokemon-id"; + pokemonId.innerHTML = `#${pokemonData.id}`; + pokemon.style.order = pokemonData.id; + pokemon.appendChild(pokemonId); + + const pokemonImg = document.createElement("img"); + pokemonImg.src = pokemonData.sprites.front_default; + pokemonImg.alt = pokemonData.name; + pokemon.appendChild(pokemonImg); + + pokemon.addEventListener("click", () => { + const id = pokemonData.id; + createPokemonDisplay(id); + }); + + container.appendChild(pokemon); +} + +const generationSelector = document.getElementById("generation-selector"); +generationSelector.addEventListener("change", (event) => { + restartPokedexUpdate(); +}); + +const typeSelector = document.getElementById("type-selector"); +typeSelector.addEventListener("change", (event) => { + restartPokedexUpdate(); +}); + +function restartPokedexUpdate() { + controller.abort(); + setTimeout(() => { + controller = new AbortController(); + signal = controller.signal; + populatePokedex(signal); + }, 250); +} + +async function createPokemonDisplay(id) { + activePokemonCarousel = 0; + const fullscreenContainer = document.getElementById("fullscreen"); + fullscreenContainer.className = "visible"; + + const pokemonDisplay = document.createElement("div"); + pokemonDisplay.id = "pokemon-display"; + + const closeButton = document.createElement("button"); + closeButton.id = "close-button"; + closeButton.innerHTML = "X"; + closeButton.addEventListener("click", () => { + closePokemonDisplay(); + }); + + fullscreenContainer.addEventListener("click", (event) => { + if (event.target.id === "fullscreen") { + closePokemonDisplay(); + } + }); + + const url = `https://pokeapi.co/api/v2/pokemon/${id}`; + const response = await fetch(url); + const data = await response.json(); + + const speciesurl = data.species.url; + const speciesResponse = await fetch(speciesurl); + const speciesData = await speciesResponse.json(); + + pokemonDisplay.className = data.types[0].type.name + "-primary-type" + + const pokemonName = document.createElement("p"); + pokemonName.className = "pokemon-name"; + + let formatedName = ""; + data.name.split("-").forEach((word) => { + formatedName += word.charAt(0).toUpperCase() + word.slice(1) + " "; + }); + formatedName = formatedName.slice(0, formatedName.length - 1); + + pokemonName.innerHTML = formatedName; + + const pokemonId = document.createElement("p"); + pokemonId.className = "pokemon-id"; + pokemonId.innerHTML = `#${data.id}`; + + const pokemonImg = document.createElement("img"); + pokemonImg.src = data.sprites.other["official-artwork"].front_default; + pokemonImg.alt = data.name; + const pokemonImgContainer = document.createElement("div"); + pokemonImgContainer.id = "image-container"; + pokemonImgContainer.appendChild(pokemonImg); + + const pokemonStatblock = document.createElement("div"); + pokemonStatblock.id = "statblock"; + + data.stats.forEach((stat) => { + const statBar = document.createElement("div"); + statBar.className = "stat-bar"; + statBar.id = stat.stat.name; + const statName = document.createElement("p"); + statName.className = "stat-name"; + let formatedStatName = ""; + stat.stat.name.split("-").forEach((word) => { + formatedStatName += word.charAt(0).toUpperCase() + word.slice(1) + " "; + }); + formatedStatName = formatedStatName.slice(0, formatedStatName.length - 1); + + statName.innerHTML = formatedStatName; + const statValue = document.createElement("p"); + statValue.className = "stat-value"; + statValue.innerHTML = stat.base_stat; + + const statGraphic = document.createElement("p"); + statGraphic.className = "stat-graphic"; + let stringFilled = document.createElement("span"); + let stringEmpty = document.createElement("span"); + + const barSize = 25; + + const baseStat = (stat.base_stat / 255) * barSize; + const emptyStat = barSize - baseStat; + + stringFilled.innerHTML = "❚".repeat(baseStat); + stringEmpty.innerHTML = "❚".repeat(emptyStat); + + stringFilled.className = "stat-filled"; + stringEmpty.className = "stat-empty"; + + statGraphic.appendChild(stringFilled); + statGraphic.appendChild(stringEmpty); + + statBar.appendChild(statGraphic); + statBar.appendChild(statName); + statBar.appendChild(statValue); + pokemonStatblock.appendChild(statBar); + }); + + const imageAndStats = document.createElement("div"); + imageAndStats.id = "image-and-stats"; + imageAndStats.appendChild(pokemonImgContainer); + imageAndStats.appendChild(pokemonStatblock); + + const descriptionCarousel = document.createElement("div"); + descriptionCarousel.id = "description-carousel"; + + let descriptionArray = []; + + speciesData.flavor_text_entries.forEach((entry) => { + if (entry.language.name === "en") { + const game = entry.version.name; + const pokemonDescription = entry.flavor_text; + descriptionArray[game] = pokemonDescription; + } + }); + + let descriptionArrayFiltered = [] + let gameArrayFiltered = [] + + for (const game in descriptionArray) { + let index = descriptionArrayFiltered.indexOf(descriptionArray[game]); + if (index === -1) { + descriptionArrayFiltered.push(descriptionArray[game]); + gameArrayFiltered.push(game); + } else { + gameArrayFiltered[index] += " | " + game; + } + } + + descriptionArrayFiltered.forEach((description, i) => { + const carouselItem = document.createElement("div"); + carouselItem.className = "description-carousel-item"; + + let descriptionFormatted = description; + descriptionFormatted = descriptionFormatted.replace("\u000c", " "); + descriptionFormatted = descriptionFormatted.replace("\n", " "); + descriptionFormatted = descriptionFormatted.replace("POKéMON", "Pokémon"); + + const descriptionText = document.createElement("p"); + descriptionText.className = "description-text"; + descriptionText.innerHTML = descriptionFormatted; + + const descriptionGame = document.createElement("p"); + descriptionGame.className = "description-game"; + descriptionGame.innerHTML = gameArrayFiltered[i].toUpperCase(); + + carouselItem.appendChild(descriptionGame); + carouselItem.appendChild(descriptionText); + descriptionCarousel.appendChild(carouselItem); + }); + + const leftButton = document.createElement("button"); + leftButton.id = "carousel-left-button"; + leftButton.innerHTML = "◀ "; + leftButton.style.display = "none"; + + const rightButton = document.createElement("button"); + rightButton.id = "carousel-right-button"; + rightButton.innerHTML = " ▶"; + + leftButton.addEventListener("click", () => { + pokemonCarousel("left"); + }); + + rightButton.addEventListener("click", () => { + pokemonCarousel("right"); + }); + + const types = data.types; + const primaryType = types[0].type.name; + const primaryIcon = `https://raw.githubusercontent.com/duiker101/pokemon-type-svg-icons/5781623f147f1bf850f426cfe1874ba56a9b75ee/icons/${primaryType}.svg` + let secondaryType = ""; + + if (types[1]) { + secondaryType = types[1].type.name; + secondaryIcon = `https://raw.githubusercontent.com/duiker101/pokemon-type-svg-icons/5781623f147f1bf850f426cfe1874ba56a9b75ee/icons/${secondaryType}.svg` + } else { + secondaryType = "Monotype"; + } + + const typeChartContainer = document.createElement("div"); + typeChartContainer.id = "type-chart-container"; + + const typeChartTitle = document.createElement("p"); + typeChartTitle.id = "type-chart-title"; + typeChartTitle.innerHTML = "Type Chart"; + + const noeffectColumn = document.createElement("ul"); + noeffectColumn.id = "noeffect-column"; + noeffectColumn.className = "type-chart-column"; + const noeffectTitle = document.createElement("li"); + noeffectTitle.innerHTML = "Immune"; + noeffectTitle.className = "type-chart-title"; + noeffectColumn.appendChild(noeffectTitle); + + const doubleresistColumn = document.createElement("ul"); + doubleresistColumn.id = "doubleresist-column"; + doubleresistColumn.className = "type-chart-column"; + const doubleresistTitle = document.createElement("li"); + doubleresistTitle.innerHTML = "Double Resist"; + doubleresistTitle.className = "type-chart-title"; + doubleresistColumn.appendChild(doubleresistTitle); + + const resistColumn = document.createElement("ul"); + resistColumn.id = "resist-column"; + resistColumn.className = "type-chart-column"; + const resistTitle = document.createElement("li"); + resistTitle.innerHTML = "Resist"; + resistTitle.className = "type-chart-title"; + resistColumn.appendChild(resistTitle); + + const weakColumn = document.createElement("ul"); + weakColumn.id = "weak-column"; + weakColumn.className = "type-chart-column"; + const weakTitle = document.createElement("li"); + weakTitle.innerHTML = "Weak"; + weakTitle.className = "type-chart-title"; + weakColumn.appendChild(weakTitle); + + const doubleweakColumn = document.createElement("ul"); + doubleweakColumn.id = "doubleweak-column"; + doubleweakColumn.className = "type-chart-column"; + const doubleweakTitle = document.createElement("li"); + doubleweakTitle.innerHTML = "Double Weak"; + doubleweakTitle.className = "type-chart-title"; + doubleweakColumn.appendChild(doubleweakTitle); + + typeChartContainer.appendChild(typeChartTitle); + typeChartContainer.appendChild(noeffectColumn); + typeChartContainer.appendChild(doubleresistColumn); + typeChartContainer.appendChild(resistColumn); + typeChartContainer.appendChild(weakColumn); + typeChartContainer.appendChild(doubleweakColumn); + + const primaryImg = document.createElement("img"); + primaryImg.src = primaryIcon; + primaryImg.alt = primaryType; + primaryImg.className = "type-icon"; + + const secondaryImg = document.createElement("img"); + secondaryImg.src = secondaryIcon; + secondaryImg.alt = secondaryType; + secondaryImg.className = "type-icon"; + + descriptionCarousel.appendChild(leftButton); + descriptionCarousel.appendChild(rightButton); + + window.addEventListener("resize", () => { + transitionPokemonCarousel(activePokemonCarousel); + }); + + const pokemonContents = document.createElement("div"); + pokemonContents.id = "pokemon-contents"; + + const typeContainer = document.createElement("div"); + typeContainer.id = "type-container"; + typeContainer.appendChild(primaryImg); + typeContainer.appendChild(secondaryImg); + + // const dexAndTypeContainer = document.createElement("div"); + // dexAndTypeContainer.id = "dex-and-type-container"; + // dexAndTypeContainer.appendChild(descriptionCarousel); + // dexAndTypeContainer.appendChild(typeChartContainer); + + pokemonDisplay.appendChild(pokemonName); + pokemonDisplay.appendChild(pokemonId); + pokemonDisplay.appendChild(typeContainer); + + pokemonContents.appendChild(imageAndStats); + //pokemonContents.appendChild(dexAndTypeContainer); + pokemonContents.appendChild(descriptionCarousel); + pokemonContents.appendChild(typeChartContainer); + + + pokemonDisplay.appendChild(pokemonContents); + + fullscreenContainer.appendChild(pokemonDisplay); + fullscreenContainer.appendChild(closeButton); + + const typeChart = await calculateTypeChart(primaryType, secondaryType) + displayTypeChart(typeChart); +} + +function closePokemonDisplay() { + const fullscreenContainer = document.getElementById("fullscreen"); + fullscreenContainer.className = ""; + const children = Array.from(fullscreenContainer.children); + children.forEach((child) => { + fullscreenContainer.removeChild(child); + }); + +} + +function pokemonCarousel(direction) { + const items = document.getElementsByClassName("description-carousel-item").length; + const leftButton = document.getElementById("carousel-left-button"); + const rightButton = document.getElementById("carousel-right-button"); + + if (items < 2) { + console.log("Not enough items for a carousel"); + return; + } + + if (direction === "left" && activePokemonCarousel === 0) { + console.log("Carousel already too far left"); + return; + } else if (direction === "right" && activePokemonCarousel === items - 1) { + console.log("Carousel already too far right"); + return; + } + + if (direction == "left") { + activePokemonCarousel--; + } else if (direction == "right") { + activePokemonCarousel++; + } + + if (activePokemonCarousel == 0) { + leftButton.style.display = "none"; + } else { + leftButton.style.display = "block"; + } + + if (activePokemonCarousel == items - 1) { + rightButton.style.display = "none"; + } else { + rightButton.style.display = "block"; + } + + transitionPokemonCarousel(activePokemonCarousel); +} + +function transitionPokemonCarousel(i) { + let offset = 0; + const items = document.getElementsByClassName("description-carousel-item"); + const width = items[0].offsetWidth; + offset = (i * (width + 33)); + for (const item of items) { + item.style.transform = `translateX(-${offset}px)`; + } +} + +async function calculateTypeChart(primaryType, secondaryType, stat) { + let typeArray = {"normal": 0, "fighting": 0, "flying": 0, "poison": 0, "ground": 0, "rock": 0, "bug": 0, "ghost": 0, "steel": 0, "fire": 0, "water": 0, "grass": 0, "electric": 0, "psychic": 0, "ice": 0, "dragon": 0, "dark": 0, "fairy": 0}; + + const primaryURL = `https://pokeapi.co/api/v2/type/${primaryType}`; + const primaryResponse = await fetch(primaryURL); + const primaryData = await primaryResponse.json(); + + primaryData.damage_relations.double_damage_from.forEach((type) => { + typeArray[type.name] += 1; + }); + primaryData.damage_relations.half_damage_from.forEach((type) => { + typeArray[type.name] -= 1; + }); + primaryData.damage_relations.no_damage_from.forEach((type) => { + typeArray[type.name] -= 999; + }); + + if (secondaryType !== "Monotype") { + const secondaryURL = `https://pokeapi.co/api/v2/type/${secondaryType}`; + const secondaryResponse = await fetch(secondaryURL); + const secondaryData = await secondaryResponse.json(); + + secondaryData.damage_relations.double_damage_from.forEach((type) => { + typeArray[type.name] += 1; + }); + secondaryData.damage_relations.half_damage_from.forEach((type) => { + typeArray[type.name] -= 1; + }); + secondaryData.damage_relations.no_damage_from.forEach((type) => { + typeArray[type.name] -= 999; + }); + } + + return typeArray; +} + +function displayTypeChart(typeArray) { + const resist = []; + const doubleresist = []; + const weak = []; + const doubleweak = []; + const noeffect = []; + for (const type in typeArray) { + if (typeArray[type] === -1) { + resist.push(type); + } else if (typeArray[type] === -2) { + doubleresist.push(type); + } else if (typeArray[type] === 1) { + weak.push(type); + } + else if (typeArray[type] === 2) { + doubleweak.push(type); + } else if (typeArray[type] < -100) { + noeffect.push(type); + } else { + //neutral + } + } + + const noeffectColumn = document.getElementById("noeffect-column"); + const doubleresistColumn = document.getElementById("doubleresist-column"); + const resistColumn = document.getElementById("resist-column"); + const weakColumn = document.getElementById("weak-column"); + const doubleweakColumn = document.getElementById("doubleweak-column"); + + for (const element of document.getElementsByClassName("type-chart-element")) { + element.remove(); + } + + try { + noeffect.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + noeffectColumn.appendChild(typeElement); + }); + + doubleresist.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + doubleresistColumn.appendChild(typeElement); + }); + + resist.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + resistColumn.appendChild(typeElement); + }); + + weak.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + weakColumn.appendChild(typeElement); + }); + + doubleweak.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + doubleweakColumn.appendChild(typeElement); + }); + } catch { + //type elements not built yet + } +} + // ACCORDIAN IMAGES function carouselExpand(i) { @@ -206,7 +898,7 @@ function initCarousel() { // INITIALIZATION // Set background colour to last saved value const bgColor = document.getElementById("bgColour"); -bgColor.value = localStorage.getItem("bgColour") || "#ffffff"; +bgColor.value = localStorage.getItem("bgColour") || "#ff0070"; const initR = parseInt(bgColour.value.substring(1, 3), 16); const initG = parseInt(bgColour.value.substring(3, 5), 16); const initB = parseInt(bgColour.value.substring(5, 7), 16); @@ -214,4 +906,6 @@ changeBackground(initR, initG, initB); document.documentElement.style.setProperty("--bgColor", bgColour.value); // Initialize carousel initCarousel(); -carouselExpand(1); \ No newline at end of file +carouselExpand(1); +// Initialize pokedex +initPokedex(); \ No newline at end of file