/* HTML generation */
const TYPES = {
Grass: '🍃',
Fire: '🔥',
Water: '💧',
Lightning: '⚡',
Fighting: '✊',
Psychic: '👁️',
Colorless: '⭐',
Darkness: '🌑',
Metal: '⚙️',
Dragon: '🐲',
Fairy: '🧚',
};
const energyHTML = (type, types = TYPES) => {
return `${types[type]}`;
};
const attackDescriptionHTML = (text) => {
if (!text) {
return '';
}
let fontSize;
if (text.length > 185) {
fontSize = 0.7;
} else if (text.length > 140) {
fontSize = 0.8;
} else if (text.length > 90) {
fontSize = 0.9;
}
return `${text}`;
};
const attackRowsHTML = (attacks) => {
return attacks
.map((attack) => {
const { cost, damage, name, text } = attack;
return `
${cost.map((energy) => energyHTML(energy)).join('')}
${name}
${attackDescriptionHTML(text)}
${damage ? damage : ''}
`;
})
.join('');
};
const cardHTML = (details) => {
const { hp, energy_type, species, length, weight, attacks, weakness, resistance, retreat, description, rarity } =
details;
const poke_name = details.name; // `name` would be reserved JS word
return `
Basic Pokémon
${species} Pokémon. Length: ${length.feet}'${length.inches}", Weight: ${weight}
${attackRowsHTML(attacks)}
weakness
${weakness ? energyHTML(weakness) : ''}
resistance
${resistance ? energyHTML(resistance) : ''}
${resistance ? '-30' : ''}
retreat cost
${energyHTML('Colorless').repeat(retreat)}
${description}
`;
};
/* Utility */
const getBasePath = () => {
return document.location.origin + document.location.pathname;
};
const generateDetails = async () => {
const details = await fetch(`${getBasePath()}/details`);
return await details.json();
};
const createTask = async (prompt) => {
const taskResponse = await fetch(`${getBasePath()}task/create?prompt=${prompt}`);
const task = await taskResponse.json();
return task;
};
const queueTask = (task_id) => {
fetch(`${getBasePath()}task/queue?task_id=${task_id}`);
};
const pollTask = async (task) => {
const taskResponse = await fetch(`${getBasePath()}task/poll?task_id=${task.task_id}`);
return await taskResponse.json();
};
const longPollTask = async (task, interval = 5_000, max) => {
if (task.status === 'complete' || (max && task.poll_count > max)) {
return task;
}
const taskResponse = await fetch(`${getBasePath()}task/poll?task_id=${task.task_id}`);
task = await taskResponse.json();
if (task.status === 'complete' || task.poll_count > max) {
return task;
}
await new Promise((resolve) => setTimeout(resolve, interval));
return await longPollTask(task, interval, max);
};
/* DOM */
const generateButton = document.querySelector('button.generate');
const rotateCard = () => {
const RANGE = 0.1;
const INTERVAL = 13; // ~75 per second
let previousTime = 0;
// Throttle closure
return (card, containerMouseEvent) => {
const currentTime = performance.now();
if (currentTime - previousTime > INTERVAL) {
previousTime = currentTime;
const rect = card.getBoundingClientRect();
const rotateX = (containerMouseEvent.clientY - rect.y - rect.height / 2) * RANGE;
const rotateY = -(containerMouseEvent.clientX - rect.x - rect.width / 2) * RANGE;
card.style.setProperty('--rotate-x', rotateX + 'deg');
card.style.setProperty('--rotate-y', rotateY + 'deg');
}
};
};
const cardRotationInitiator = (renderSection) => {
let currentCard;
return () => {
let handleMouseMove;
if (currentCard) {
handleMouseMove = rotateCard().bind(null, currentCard);
renderSection.removeEventListener('mousemove', handleMouseMove, true);
}
const newCard = document.querySelector('.pokecard');
currentCard = newCard;
handleMouseMove = rotateCard().bind(null, newCard);
renderSection.addEventListener('mousemove', handleMouseMove, true);
};
};
generateButton.addEventListener('click', async () => {
const details = await generateDetails();
console.log({ details });
const renderSection = document.querySelector('section.render');
const durationSeconds = document.querySelector('.duration > .seconds');
const initialiseCardRotation = cardRotationInitiator(renderSection);
let duration = 0.0;
try {
const task = await createTask(details.energy_type);
queueTask(task.task_id);
const incrementSeconds = setInterval(() => {
duration += 0.1;
durationSeconds.textContent = duration.toFixed(1).toString();
}, 100);
const completedTask = await longPollTask(task);
clearInterval(incrementSeconds);
renderSection.innerHTML = cardHTML(details);
const picture = document.querySelector('img.picture');
picture.src = completedTask.value;
initialiseCardRotation();
} catch (err) {
console.error(err);
}
});