141 lines
3.5 KiB
JavaScript
141 lines
3.5 KiB
JavaScript
// All available spells data (loaded from API)
|
|
let allSpells = [];
|
|
|
|
const searchInput = document.getElementById('spell-search');
|
|
const resultsDiv = document.getElementById('spell-results');
|
|
const selectedDiv = document.getElementById('selected-spell');
|
|
const spellIdInput = document.getElementById('spell-id');
|
|
|
|
let currentResults = [];
|
|
|
|
// Load spells data from API
|
|
async function loadSpells() {
|
|
try {
|
|
const response = await fetch('/admin/api/spells');
|
|
if (response.ok) {
|
|
allSpells = await response.json();
|
|
} else {
|
|
console.error('Failed to load spells');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading spells:', error);
|
|
}
|
|
}
|
|
|
|
// Initialize data when DOM is loaded
|
|
document.addEventListener('DOMContentLoaded', loadSpells);
|
|
|
|
// Simple fuzzy search function
|
|
function fuzzyMatch(search, target) {
|
|
search = search.toLowerCase();
|
|
target = target.toLowerCase();
|
|
|
|
// Exact match gets highest score
|
|
if (target.includes(search)) {
|
|
return 1000 - target.indexOf(search);
|
|
}
|
|
|
|
// Character-by-character fuzzy matching
|
|
let score = 0;
|
|
let searchIndex = 0;
|
|
|
|
for (let i = 0; i < target.length && searchIndex < search.length; i++) {
|
|
if (target[i] === search[searchIndex]) {
|
|
score += 10;
|
|
searchIndex++;
|
|
}
|
|
}
|
|
|
|
// Only return score if we matched all search characters
|
|
return searchIndex === search.length ? score : 0;
|
|
}
|
|
|
|
function getSpellTypeName(type) {
|
|
switch (type) {
|
|
case 0:
|
|
return 'Heal';
|
|
case 1:
|
|
return 'Damage';
|
|
case 2:
|
|
return 'Sleep';
|
|
case 3:
|
|
return 'Uber Attack';
|
|
case 4:
|
|
return 'Uber Defense';
|
|
default:
|
|
return 'Unknown';
|
|
}
|
|
}
|
|
|
|
function searchSpells() {
|
|
const query = searchInput.value.trim();
|
|
|
|
if (query.length < 2) {
|
|
resultsDiv.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
// Search and score results
|
|
const scored = allSpells.map(spell => ({
|
|
...spell,
|
|
score: fuzzyMatch(query, spell.Name)
|
|
})).filter(spell => spell.score > 0)
|
|
.sort((a, b) => b.score - a.score)
|
|
.slice(0, 8); // Limit to 8 results
|
|
|
|
currentResults = scored;
|
|
|
|
if (scored.length === 0) {
|
|
resultsDiv.innerHTML = '<div style="padding: 0.5rem; color: #666;">No spells found</div>';
|
|
} else {
|
|
resultsDiv.innerHTML = scored.map((spell, index) =>
|
|
`<div class="spell-result" data-index="${index}" style="padding: 0.5rem; cursor: pointer; border-bottom: 1px solid #eee;">
|
|
<strong>${spell.Name}</strong> - ${getSpellTypeName(spell.Type)}
|
|
<span style="color: #666;">(${spell.MP} MP, ${spell.Power} Power)</span>
|
|
</div>`
|
|
).join('');
|
|
}
|
|
|
|
resultsDiv.style.display = 'block';
|
|
}
|
|
|
|
function selectSpell(spell) {
|
|
selectedDiv.innerHTML = `
|
|
<strong>${spell.Name}</strong> - ${getSpellTypeName(spell.Type)}<br>
|
|
<small>MP Cost: ${spell.MP} | Power: ${spell.Power}</small>
|
|
${spell.Lore ? `<br><em>${spell.Lore}</em>` : ''}
|
|
`;
|
|
spellIdInput.value = spell.ID;
|
|
searchInput.value = spell.Name;
|
|
resultsDiv.style.display = 'none';
|
|
}
|
|
|
|
// Event listeners
|
|
searchInput.addEventListener('input', searchSpells);
|
|
|
|
searchInput.addEventListener('focus', () => {
|
|
if (currentResults.length > 0) {
|
|
resultsDiv.style.display = 'block';
|
|
}
|
|
});
|
|
|
|
document.addEventListener('click', (e) => {
|
|
if (!searchInput.contains(e.target) && !resultsDiv.contains(e.target)) {
|
|
resultsDiv.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
resultsDiv.addEventListener('click', (e) => {
|
|
const resultEl = e.target.closest('.spell-result');
|
|
if (resultEl) {
|
|
const index = parseInt(resultEl.dataset.index);
|
|
selectSpell(currentResults[index]);
|
|
}
|
|
});
|
|
|
|
// Keyboard navigation
|
|
searchInput.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape') {
|
|
resultsDiv.style.display = 'none';
|
|
}
|
|
}); |