refactor: split CSS and JS into modular files (SRP)

This commit is contained in:
2025-12-26 13:55:02 +01:00
parent 94cf36bff3
commit 2e5731b5d6
47 changed files with 8882 additions and 2298 deletions

View File

@@ -0,0 +1,156 @@
/**
* AniWorld - Search Module
*
* Handles anime search functionality and result display.
*
* Dependencies: constants.js, api-client.js, ui-utils.js, series-manager.js
*/
var AniWorld = window.AniWorld || {};
AniWorld.Search = (function() {
'use strict';
const API = AniWorld.Constants.API;
/**
* Initialize the search module
*/
function init() {
bindEvents();
}
/**
* Bind UI events
*/
function bindEvents() {
const searchInput = document.getElementById('search-input');
const searchBtn = document.getElementById('search-btn');
const clearSearch = document.getElementById('clear-search');
if (searchBtn) {
searchBtn.addEventListener('click', performSearch);
}
if (searchInput) {
searchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
performSearch();
}
});
}
if (clearSearch) {
clearSearch.addEventListener('click', function() {
if (searchInput) searchInput.value = '';
hideSearchResults();
});
}
}
/**
* Perform anime search
*/
async function performSearch() {
const searchInput = document.getElementById('search-input');
const query = searchInput ? searchInput.value.trim() : '';
if (!query) {
AniWorld.UI.showToast('Please enter a search term', 'warning');
return;
}
try {
AniWorld.UI.showLoading();
const response = await AniWorld.ApiClient.post(API.ANIME_SEARCH, { query: query });
if (!response) return;
const data = await response.json();
// Check if response is a direct array (new format) or wrapped object (legacy)
if (Array.isArray(data)) {
displaySearchResults(data);
} else if (data.status === 'success') {
displaySearchResults(data.results);
} else {
AniWorld.UI.showToast('Search error: ' + (data.message || 'Unknown error'), 'error');
}
} catch (error) {
console.error('Search error:', error);
AniWorld.UI.showToast('Search failed', 'error');
} finally {
AniWorld.UI.hideLoading();
}
}
/**
* Display search results
* @param {Array} results - Search results array
*/
function displaySearchResults(results) {
const resultsContainer = document.getElementById('search-results');
const resultsList = document.getElementById('search-results-list');
if (results.length === 0) {
resultsContainer.classList.add('hidden');
AniWorld.UI.showToast('No search results found', 'warning');
return;
}
resultsList.innerHTML = results.map(function(result) {
const displayName = AniWorld.UI.getDisplayName(result);
return '<div class="search-result-item">' +
'<span class="search-result-name">' + AniWorld.UI.escapeHtml(displayName) + '</span>' +
'<button class="btn btn-small btn-primary" ' +
'onclick="AniWorld.Search.addSeries(\'' + AniWorld.UI.escapeHtml(result.link) + '\', \'' +
AniWorld.UI.escapeHtml(displayName) + '\')">' +
'<i class="fas fa-plus"></i> Add' +
'</button>' +
'</div>';
}).join('');
resultsContainer.classList.remove('hidden');
}
/**
* Hide search results
*/
function hideSearchResults() {
document.getElementById('search-results').classList.add('hidden');
}
/**
* Add a series from search results
* @param {string} link - Series link
* @param {string} name - Series name
*/
async function addSeries(link, name) {
try {
const response = await AniWorld.ApiClient.post(API.ANIME_ADD, { link: link, name: name });
if (!response) return;
const data = await response.json();
if (data.status === 'success') {
AniWorld.UI.showToast(data.message, 'success');
AniWorld.SeriesManager.loadSeries();
hideSearchResults();
document.getElementById('search-input').value = '';
} else {
AniWorld.UI.showToast('Error adding series: ' + data.message, 'error');
}
} catch (error) {
console.error('Error adding series:', error);
AniWorld.UI.showToast('Failed to add series', 'error');
}
}
// Public API
return {
init: init,
performSearch: performSearch,
hideSearchResults: hideSearchResults,
addSeries: addSeries
};
})();