We are launching a docker version (server version) today so we want to just organize the repo so its easier to navigate.
176 lines
6.4 KiB
JavaScript
176 lines
6.4 KiB
JavaScript
const SearchManager = {
|
|
availableExtensions: [],
|
|
searchTimeout: null,
|
|
|
|
init(inputSelector, resultsSelector, type = 'anime') {
|
|
const searchInput = document.querySelector(inputSelector);
|
|
const searchResults = document.querySelector(resultsSelector);
|
|
|
|
if (!searchInput || !searchResults) {
|
|
console.error('Search elements not found');
|
|
return;
|
|
}
|
|
|
|
this.loadExtensions(type);
|
|
|
|
searchInput.addEventListener('input', (e) => {
|
|
const query = e.target.value;
|
|
clearTimeout(this.searchTimeout);
|
|
|
|
if (query.length < 2) {
|
|
searchResults.classList.remove('active');
|
|
searchResults.innerHTML = '';
|
|
searchInput.style.borderRadius = '99px';
|
|
return;
|
|
}
|
|
|
|
this.searchTimeout = setTimeout(() => {
|
|
this.search(query, type, searchResults);
|
|
}, 300);
|
|
});
|
|
|
|
document.addEventListener('click', (e) => {
|
|
if (!e.target.closest('.search-wrapper')) {
|
|
searchResults.classList.remove('active');
|
|
searchInput.style.borderRadius = '99px';
|
|
}
|
|
});
|
|
},
|
|
|
|
async loadExtensions(type) {
|
|
try {
|
|
const endpoint = type === 'book' ? '/api/extensions/book' : '/api/extensions/anime';
|
|
const res = await fetch(endpoint);
|
|
const data = await res.json();
|
|
this.availableExtensions = data.extensions || [];
|
|
console.log(`${type} extensions loaded:`, this.availableExtensions);
|
|
} catch (err) {
|
|
console.error('Error loading extensions:', err);
|
|
}
|
|
},
|
|
|
|
async search(query, type, resultsContainer) {
|
|
try {
|
|
let apiUrl, extensionName = null, finalQuery = query;
|
|
|
|
const parts = query.split(':');
|
|
if (parts.length >= 2) {
|
|
const potentialExtension = parts[0].trim().toLowerCase();
|
|
const foundExtension = this.availableExtensions.find(
|
|
ext => ext.toLowerCase() === potentialExtension
|
|
);
|
|
|
|
if (foundExtension) {
|
|
extensionName = foundExtension;
|
|
finalQuery = parts.slice(1).join(':').trim();
|
|
|
|
if (finalQuery.length === 0) {
|
|
this.renderResults([], resultsContainer, type);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (extensionName) {
|
|
const endpoint = type === 'book' ? 'books' : '';
|
|
apiUrl = `/api/search/${endpoint ? endpoint + '/' : ''}${extensionName}?q=${encodeURIComponent(finalQuery)}`;
|
|
} else {
|
|
const endpoint = type === 'book' ? '/api/search/books' : '/api/search';
|
|
apiUrl = `${endpoint}?q=${encodeURIComponent(query)}`;
|
|
}
|
|
|
|
const res = await fetch(apiUrl);
|
|
const data = await res.json();
|
|
|
|
const results = (data.results || []).map(item => ({
|
|
...item,
|
|
isExtensionResult: !!extensionName,
|
|
extensionName
|
|
}));
|
|
|
|
this.renderResults(results, resultsContainer, type);
|
|
} catch (err) {
|
|
console.error("Search Error:", err);
|
|
this.renderResults([], resultsContainer, type);
|
|
}
|
|
},
|
|
|
|
renderResults(results, container, type) {
|
|
container.innerHTML = '';
|
|
|
|
if (!results || results.length === 0) {
|
|
container.innerHTML = '<div style="padding:1rem; color:#888; text-align:center">No results found</div>';
|
|
} else {
|
|
results.forEach(item => {
|
|
const resultElement = this.createResultElement(item, type);
|
|
container.appendChild(resultElement);
|
|
});
|
|
}
|
|
|
|
container.classList.add('active');
|
|
const searchInput = container.previousElementSibling || document.querySelector('.search-input');
|
|
if (searchInput) {
|
|
searchInput.style.borderRadius = '12px 12px 0 0';
|
|
}
|
|
},
|
|
|
|
createResultElement(item, type) {
|
|
const element = document.createElement('a');
|
|
element.className = 'search-item';
|
|
|
|
if (type === 'book') {
|
|
const title = item.title?.english || item.title?.romaji || "Unknown";
|
|
const img = item.coverImage?.medium || item.coverImage?.large || '';
|
|
const rating = Number.isInteger(item.averageScore) ? `${item.averageScore}%` : item.averageScore || 'N/A';
|
|
const year = item.seasonYear || item.startDate?.year || '????';
|
|
const format = item.format || 'MANGA';
|
|
|
|
element.href = item.isExtensionResult
|
|
? `/book/${item.extensionName}/${item.id}`
|
|
: `/book/${item.id}`;
|
|
|
|
element.innerHTML = `
|
|
<img src="${img}" class="search-poster" alt="${title}">
|
|
<div class="search-info">
|
|
<div class="search-title">${title}</div>
|
|
<div class="search-meta">
|
|
<span class="rating-pill">${rating}</span>
|
|
<span>• ${year}</span>
|
|
<span>• ${format}</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
} else {
|
|
|
|
const title = item.title?.english || item.title?.romaji || "Unknown Title";
|
|
const img = item.coverImage?.medium || item.coverImage?.large || '';
|
|
const rating = item.averageScore ? `${item.averageScore}%` : 'N/A';
|
|
const year = item.seasonYear || '';
|
|
const format = item.format || 'TV';
|
|
|
|
element.href = item.isExtensionResult
|
|
? `/anime/${item.extensionName}/${item.id}`
|
|
: `/anime/${item.id}`;
|
|
|
|
element.innerHTML = `
|
|
<img src="${img}" class="search-poster" alt="${title}">
|
|
<div class="search-info">
|
|
<div class="search-title">${title}</div>
|
|
<div class="search-meta">
|
|
<span class="rating-pill">${rating}</span>
|
|
<span>• ${year}</span>
|
|
<span>• ${format}</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
return element;
|
|
},
|
|
|
|
getTitle(item) {
|
|
return item.title?.english || item.title?.romaji || "Unknown Title";
|
|
}
|
|
};
|
|
|
|
window.SearchManager = SearchManager; |