2025-09-25 12:42:10 -07:00
<!-- Description: Pokémon GO Level Calculator -->
<!-- Author: Justin Oros -->
<!-- Source: https://github.com/JustinOros -->
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Pokémon GO Level Calculator</ title >
2025-09-29 12:35:18 -07:00
< link rel = "icon" type = "image/x-icon" href = "favicon.ico" >
< link rel = "icon" type = "image/png" sizes = "32x32" href = "pogo-32.png" >
< link rel = "icon" type = "image/png" sizes = "192x192" href = "pogo-192.png" >
< link rel = "apple-touch-icon" sizes = "180x180" href = "pogo-180.png" >
< link rel = "icon" type = "image/png" sizes = "512x512" href = "pogo-512.png" >
2025-09-25 12:42:10 -07:00
< script src = "https://cdn.tailwindcss.com" ></ script >
< style >
2025-10-01 12:10:21 -07:00
body {
font-family : 'Inter' , sans-serif ;
position : relative ;
background : url ( 'pogo-wallpaper.jpg' ) no-repeat center center fixed ;
background-size : cover ;
2025-10-07 10:21:01 -07:00
transition : background-color 0.4 s , color 0.4 s ;
2025-10-01 12:10:21 -07:00
}
2025-09-25 12:42:10 -07:00
body :: before {
content : '' ;
position : fixed ;
top : 0 ; left : 0 ;
width : 100 % ; height : 100 % ;
2025-10-07 10:21:01 -07:00
background : rgba ( 255 , 255 , 255 , 0.8 );
2025-09-25 12:42:10 -07:00
background-image : radial-gradient ( circle at 1 px 1 px , rgba ( 0 , 0 , 0 , 0.05 ) 1 px , transparent 0 );
background-size : 20 px 20 px ;
2025-10-01 12:10:21 -07:00
opacity : 0.8 ;
2025-09-25 12:42:10 -07:00
z-index : -1 ;
2025-10-07 10:21:01 -07:00
transition : background 0.4 s , opacity 0.4 s ;
}
. dark body :: before {
background : rgba ( 0 , 0 , 0 , 0.7 );
background-image : radial-gradient ( circle at 1 px 1 px , rgba ( 255 , 255 , 255 , 0.08 ) 1 px , transparent 0 );
opacity : 0.9 ;
}
. dark-mode {
background-color : #0f172a ;
color : #f1f5f9 ;
}
. dark-mode . bg-white { background-color : #1e293b !important ; }
. dark-mode . text-slate-900 { color : #f1f5f9 !important ; }
. dark-mode . text-slate-500 ,
. dark-mode . text-slate-600 { color : #cbd5e1 !important ; }
. dark-mode . border-slate-200 { border-color : #334155 !important ; }
. dark-mode input { background-color : #334155 ; color : #f1f5f9 ; }
. dark-mode # progress-bar { background-color : #38bdf8 !important ; }
. dark-mode # theme-toggle { color : #facc15 ; }
# theme-toggle {
position : absolute ;
top : 0.75 rem ;
right : 0.75 rem ;
font-size : 1.5 rem ;
cursor : pointer ;
transition : transform 0.3 s ;
}
# theme-toggle : hover {
transform : scale ( 1.2 );
2025-09-25 12:42:10 -07:00
}
2025-09-30 13:11:46 -07:00
# edit-xp { display : inline-block ; transform : scaleX ( -1 ); }
2025-09-25 12:42:10 -07:00
</ style >
</ head >
2025-10-07 10:21:01 -07:00
< body class = "flex items-center justify-center min-h-screen p-4 transition-colors duration-500" >
2025-09-25 12:42:10 -07:00
2025-10-07 10:21:01 -07:00
< div id = "main-card" class = "relative w-full max-w-md bg-white rounded-2xl shadow-xl p-6 md:p-8 border border-slate-200 text-center" >
< div id = "theme-toggle" > ☀️</ div >
2025-09-29 12:27:21 -07:00
< img src = "pogo.png" alt = "Pokémon GO" class = "mx-auto mb-4 w-32 h-auto" >
2025-09-25 12:42:10 -07:00
< h1 class = "text-2xl md:text-3xl font-bold text-slate-900 mb-4" > Pokémon GO Level Calculator</ h1 >
< p class = "text-slate-500 mb-6" > Enter your total XP to see your level</ p >
2025-09-30 13:11:46 -07:00
2025-09-30 12:54:57 -07:00
< div class = "flex gap-2 mb-4 justify-center" >
< input type = "text" id = "xp-input" placeholder = "e.g., 123,456,789"
class = "flex-1 px-4 py-3 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 text-center text-lg" >
< button id = "calculate-btn"
class = "w-12 h-12 bg-green-500 hover:bg-green-600 text-white font-bold rounded-lg transition-transform transform hover:scale-105" >
GO!
</ button >
</ div >
2025-09-25 12:42:10 -07:00
2025-09-30 11:44:45 -07:00
< div id = "result" class = "mt-8 hidden" >
< div class = "relative w-full h-16 bg-slate-200 rounded-lg overflow-hidden" >
< div id = "progress-bar" class = "absolute top-0 left-0 h-full bg-green-500" ></ div >
< div id = "level-text" class = "absolute inset-0 flex items-center justify-center text-3xl font-bold text-black" ></ div >
</ div >
< div class = "flex justify-between text-xs text-slate-500 mt-1" >
< span id = "level-left" ></ span >
< span id = "level-right" ></ span >
</ div >
2025-09-30 12:06:56 -07:00
2025-09-30 12:31:21 -07:00
< div id = "extra-info" class = "mt-3 text-sm text-slate-600" >
2025-09-30 12:06:56 -07:00
< p >
2025-09-30 12:54:57 -07:00
If you average
2025-09-30 12:06:56 -07:00
< span id = "xp-per-day" > 15,000</ span > XP per day,
you will reach the next level in
2025-09-30 12:16:27 -07:00
< span id = "days-next" > 0</ span > and level 80 in
< span id = "days-80" > 0</ span > . < span id = "edit-xp" class = "ml-1 text-blue-500 cursor-pointer" > ✏️</ span >
2025-09-30 12:06:56 -07:00
</ p >
</ div >
2025-09-30 11:44:45 -07:00
</ div >
2025-09-25 12:42:10 -07:00
</ div >
2025-10-29 09:02:25 -07:00
< script >
let xpThresholds = [];
let avgXpPerDay = 15000 ;
let currentXp = 0 ;
let currentLevel = 1 ;
2025-09-25 12:42:10 -07:00
2025-10-29 09:02:25 -07:00
async function loadXPThresholds () {
try {
const response = await fetch ( 'levels.csv' );
const text = await response . text ();
2025-09-30 12:06:56 -07:00
2025-10-29 09:20:05 -07:00
xpThresholds = text
. trim ()
. split ( '\n' )
. map ( line => {
const parts = line . split ( ',' );
return parseInt ( parts [ 1 ]. replace ( /,/g , '' ), 10 );
});
2025-10-29 09:02:25 -07:00
2025-10-29 09:20:05 -07:00
console . log ( "Loaded XP thresholds (cumulative):" , xpThresholds );
2025-10-29 09:02:25 -07:00
} catch ( error ) {
alert ( "Failed to load levels.csv. Please ensure it's in the same folder as index.html." );
console . error ( error );
2025-09-30 11:44:45 -07:00
}
2025-10-29 09:02:25 -07:00
}
2025-09-25 12:42:10 -07:00
2025-10-29 09:02:25 -07:00
function calculateLevel ( xp ) {
let level = 1 ;
for ( let i = 0 ; i < xpThresholds . length ; i ++ ) {
if ( xp >= xpThresholds [ i ]) level = i + 1 ;
else break ;
2025-09-30 12:16:27 -07:00
}
2025-10-29 09:02:25 -07:00
return level ;
}
2025-09-30 12:16:27 -07:00
2025-10-29 09:02:25 -07:00
function formatDays ( days ) {
if ( days > 365 ) return ( days / 365 ). toFixed ( 1 ) + ' years' ;
return days . toLocaleString () + ' days' ;
}
2025-09-30 12:06:56 -07:00
2025-10-29 09:02:25 -07:00
function updateExtraInfo () {
2025-10-29 09:16:24 -07:00
const maxLevel = xpThresholds . length ;
const maxXp = xpThresholds [ maxLevel - 1 ];
if ( currentXp >= maxXp ) {
document . getElementById ( 'extra-info' ). innerHTML = `
<p class="text-green-600 font-semibold text-lg">
🎉 Congratulations! You're max level!
</p>` ;
return ;
}
2025-10-29 09:02:25 -07:00
let nextLevelXp = ( currentLevel < xpThresholds . length ) ? xpThresholds [ currentLevel ] : currentXp ;
let xpToNext = Math . max ( 0 , nextLevelXp - currentXp );
let xpTo40 = Math . max ( 0 , xpThresholds [ 39 ] - currentXp );
let xpTo80 = Math . max ( 0 , xpThresholds [ 79 ] - currentXp );
2025-09-30 12:06:56 -07:00
2025-10-29 09:02:25 -07:00
let daysNext = Math . ceil ( xpToNext / avgXpPerDay );
let days40 = Math . ceil ( xpTo40 / avgXpPerDay );
let days80 = Math . ceil ( xpTo80 / avgXpPerDay );
2025-09-30 13:06:11 -07:00
2025-10-29 09:02:25 -07:00
const nextText = currentLevel < 40
? `If you average ${ avgXpPerDay . toLocaleString () } XP per day, you will reach the next level in ${ formatDays ( daysNext ) } , level 40 in ${ formatDays ( days40 ) } , and level 80 in ${ formatDays ( days80 ) } .`
: `If you average ${ avgXpPerDay . toLocaleString () } XP per day, you will reach the next level in ${ formatDays ( daysNext ) } and level 80 in ${ formatDays ( days80 ) } .` ;
2025-09-30 13:11:46 -07:00
2025-10-29 09:16:24 -07:00
document . getElementById ( 'extra-info' ). innerHTML = `
<p> ${ nextText } <span id="edit-xp" class="ml-1 text-blue-500 cursor-pointer">✏️</span></p>` ;
2025-09-30 12:06:56 -07:00
2025-10-29 09:02:25 -07:00
document . getElementById ( 'edit-xp' ). addEventListener ( 'click' , () => {
let newVal = prompt ( "Enter your average XP per day:" , avgXpPerDay );
if ( newVal && ! isNaN ( newVal ) && parseInt ( newVal ) > 0 ) {
avgXpPerDay = parseInt ( newVal , 10 );
updateExtraInfo ();
2025-10-07 10:21:01 -07:00
}
});
2025-10-29 09:02:25 -07:00
}
document . getElementById ( 'calculate-btn' ). addEventListener ( 'click' , () => {
if ( xpThresholds . length === 0 ) {
alert ( 'Level data not loaded yet. Please wait a moment and try again.' );
return ;
}
let xp = document . getElementById ( 'xp-input' ). value . replace ( /,/g , '' ). trim ();
if ( ! xp || isNaN ( xp )) {
alert ( 'Please enter a valid number for XP.' );
return ;
}
currentXp = parseInt ( xp , 10 );
currentLevel = calculateLevel ( currentXp );
let nextLevelXp = ( currentLevel < xpThresholds . length ) ? xpThresholds [ currentLevel ] : currentXp ;
let currentLevelXp = xpThresholds [ currentLevel - 1 ];
let progress = ( currentXp - currentLevelXp ) / ( nextLevelXp - currentLevelXp ) * 100 ;
document . getElementById ( 'progress-bar' ). style . width = progress + '%' ;
document . getElementById ( 'level-text' ). textContent = `Level ${ currentLevel } ` ;
document . getElementById ( 'level-left' ). textContent = currentLevel ;
document . getElementById ( 'level-right' ). textContent = ( currentLevel < xpThresholds . length ) ? currentLevel + 1 : currentLevel ;
document . getElementById ( 'result' ). classList . remove ( 'hidden' );
updateExtraInfo ();
});
document . getElementById ( 'xp-input' ). addEventListener ( 'input' , ( e ) => {
e . target . value = e . target . value . replace ( /[^0-9,]/g , '' );
});
const themeToggle = document . getElementById ( 'theme-toggle' );
themeToggle . addEventListener ( 'click' , () => {
document . body . classList . toggle ( 'dark-mode' );
themeToggle . textContent = document . body . classList . contains ( 'dark-mode' ) ? '🌙' : '☀️' ;
});
window . addEventListener ( 'DOMContentLoaded' , loadXPThresholds );
</ script >
2025-09-25 12:42:10 -07:00
</ body >
</ html >