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,439 @@
/**
* AniWorld - Scan Manager Module
*
* Handles library scanning and progress overlay.
*
* Dependencies: constants.js, api-client.js, ui-utils.js
*/
var AniWorld = window.AniWorld || {};
AniWorld.ScanManager = (function() {
'use strict';
const API = AniWorld.Constants.API;
const DEFAULTS = AniWorld.Constants.DEFAULTS;
// State
let scanTotalItems = 0;
let lastScanData = null;
/**
* Initialize the scan manager
*/
function init() {
bindEvents();
// Check scan status on page load
checkActiveScanStatus();
}
/**
* Bind UI events
*/
function bindEvents() {
const rescanBtn = document.getElementById('rescan-btn');
if (rescanBtn) {
rescanBtn.addEventListener('click', rescanSeries);
}
// Click on rescan status indicator to reopen scan overlay
const rescanStatus = document.getElementById('rescan-status');
if (rescanStatus) {
rescanStatus.addEventListener('click', function(e) {
e.stopPropagation();
console.log('Rescan status clicked');
reopenScanOverlay();
});
}
}
/**
* Start a rescan of the series directory
*/
async function rescanSeries() {
try {
// Show the overlay immediately before making the API call
showScanProgressOverlay({
directory: 'Starting scan...',
total_items: 0
});
updateProcessStatus('rescan', true);
const response = await AniWorld.ApiClient.post(API.ANIME_RESCAN, {});
if (!response) {
removeScanProgressOverlay();
updateProcessStatus('rescan', false);
return;
}
const data = await response.json();
// Debug logging
console.log('Rescan response:', data);
// Note: The scan progress will be updated via WebSocket events
// The overlay will be closed when scan_completed is received
if (data.success !== true) {
removeScanProgressOverlay();
updateProcessStatus('rescan', false);
AniWorld.UI.showToast('Rescan error: ' + data.message, 'error');
}
} catch (error) {
console.error('Rescan error:', error);
removeScanProgressOverlay();
updateProcessStatus('rescan', false);
AniWorld.UI.showToast('Failed to start rescan', 'error');
}
}
/**
* Show the scan progress overlay
* @param {Object} data - Scan started event data
*/
function showScanProgressOverlay(data) {
// Remove existing overlay if present
removeScanProgressOverlay();
// Store total items for progress calculation
scanTotalItems = data?.total_items || 0;
// Store last scan data for reopening
lastScanData = data;
// Create overlay element
const overlay = document.createElement('div');
overlay.id = 'scan-progress-overlay';
overlay.className = 'scan-progress-overlay';
const totalDisplay = scanTotalItems > 0 ? scanTotalItems : '...';
overlay.innerHTML =
'<div class="scan-progress-container">' +
'<div class="scan-progress-header">' +
'<h3>' +
'<span class="scan-progress-spinner"></span>' +
'<i class="fas fa-check-circle scan-completed-icon"></i>' +
'<span class="scan-title-text">Scanning Library</span>' +
'</h3>' +
'</div>' +
'<div class="scan-progress-bar-container">' +
'<div class="scan-progress-bar" id="scan-progress-bar" style="width: 0%"></div>' +
'</div>' +
'<div class="scan-progress-text" id="scan-progress-text">' +
'<span id="scan-current-count">0</span> / <span id="scan-total-count">' + totalDisplay + '</span> directories' +
'</div>' +
'<div class="scan-progress-stats">' +
'<div class="scan-stat">' +
'<span class="scan-stat-value" id="scan-directories-count">0</span>' +
'<span class="scan-stat-label">Scanned</span>' +
'</div>' +
'<div class="scan-stat">' +
'<span class="scan-stat-value" id="scan-files-count">0</span>' +
'<span class="scan-stat-label">Series Found</span>' +
'</div>' +
'</div>' +
'<div class="scan-current-directory" id="scan-current-directory">' +
'<span class="scan-current-directory-label">Current:</span>' +
'<span id="scan-current-path">' + AniWorld.UI.escapeHtml(data?.directory || 'Initializing...') + '</span>' +
'</div>' +
'<div class="scan-elapsed-time hidden" id="scan-elapsed-time">' +
'<i class="fas fa-clock"></i>' +
'<span id="scan-elapsed-value">0.0s</span>' +
'</div>' +
'</div>';
document.body.appendChild(overlay);
// Add click-outside-to-close handler
overlay.addEventListener('click', function(e) {
// Only close if clicking the overlay background, not the container
if (e.target === overlay) {
removeScanProgressOverlay();
}
});
// Trigger animation by adding visible class after a brief delay
requestAnimationFrame(function() {
overlay.classList.add('visible');
});
}
/**
* Update the scan progress overlay
* @param {Object} data - Scan progress event data
*/
function updateScanProgressOverlay(data) {
const overlay = document.getElementById('scan-progress-overlay');
if (!overlay) return;
// Update total items if provided
if (data.total_items && data.total_items > 0) {
scanTotalItems = data.total_items;
const totalCount = document.getElementById('scan-total-count');
if (totalCount) {
totalCount.textContent = scanTotalItems;
}
}
// Update progress bar
const progressBar = document.getElementById('scan-progress-bar');
if (progressBar && scanTotalItems > 0 && data.directories_scanned !== undefined) {
const percentage = Math.min(100, (data.directories_scanned / scanTotalItems) * 100);
progressBar.style.width = percentage + '%';
}
// Update current/total count display
const currentCount = document.getElementById('scan-current-count');
if (currentCount && data.directories_scanned !== undefined) {
currentCount.textContent = data.directories_scanned;
}
// Update directories count
const dirCount = document.getElementById('scan-directories-count');
if (dirCount && data.directories_scanned !== undefined) {
dirCount.textContent = data.directories_scanned;
}
// Update files/series count
const filesCount = document.getElementById('scan-files-count');
if (filesCount && data.files_found !== undefined) {
filesCount.textContent = data.files_found;
}
// Update current directory (truncate if too long)
const currentPath = document.getElementById('scan-current-path');
if (currentPath && data.current_directory) {
const maxLength = 50;
let displayPath = data.current_directory;
if (displayPath.length > maxLength) {
displayPath = '...' + displayPath.slice(-maxLength + 3);
}
currentPath.textContent = displayPath;
currentPath.title = data.current_directory;
}
}
/**
* Hide the scan progress overlay with completion summary
* @param {Object} data - Scan completed event data
*/
function hideScanProgressOverlay(data) {
const overlay = document.getElementById('scan-progress-overlay');
if (!overlay) return;
const container = overlay.querySelector('.scan-progress-container');
if (container) {
container.classList.add('completed');
}
// Update title
const titleText = overlay.querySelector('.scan-title-text');
if (titleText) {
titleText.textContent = 'Scan Complete';
}
// Complete the progress bar
const progressBar = document.getElementById('scan-progress-bar');
if (progressBar) {
progressBar.style.width = '100%';
}
// Update final stats
if (data) {
const dirCount = document.getElementById('scan-directories-count');
if (dirCount && data.total_directories !== undefined) {
dirCount.textContent = data.total_directories;
}
const filesCount = document.getElementById('scan-files-count');
if (filesCount && data.total_files !== undefined) {
filesCount.textContent = data.total_files;
}
// Update progress text to show final count
const currentCount = document.getElementById('scan-current-count');
const totalCount = document.getElementById('scan-total-count');
if (currentCount && data.total_directories !== undefined) {
currentCount.textContent = data.total_directories;
}
if (totalCount && data.total_directories !== undefined) {
totalCount.textContent = data.total_directories;
}
// Show elapsed time
const elapsedTimeEl = document.getElementById('scan-elapsed-time');
const elapsedValueEl = document.getElementById('scan-elapsed-value');
if (elapsedTimeEl && elapsedValueEl && data.elapsed_seconds !== undefined) {
elapsedValueEl.textContent = data.elapsed_seconds.toFixed(1) + 's';
elapsedTimeEl.classList.remove('hidden');
}
// Update current directory to show completion message
const currentPath = document.getElementById('scan-current-path');
if (currentPath) {
currentPath.textContent = 'Scan finished successfully';
}
}
// Auto-dismiss after 3 seconds
setTimeout(function() {
removeScanProgressOverlay();
}, DEFAULTS.SCAN_AUTO_DISMISS);
}
/**
* Remove the scan progress overlay from the DOM
*/
function removeScanProgressOverlay() {
const overlay = document.getElementById('scan-progress-overlay');
if (overlay) {
overlay.classList.remove('visible');
// Wait for fade out animation before removing
setTimeout(function() {
if (overlay.parentElement) {
overlay.remove();
}
}, 300);
}
}
/**
* Reopen the scan progress overlay if a scan is in progress
*/
async function reopenScanOverlay() {
// Check if overlay already exists
const existingOverlay = document.getElementById('scan-progress-overlay');
if (existingOverlay) {
return;
}
// Check if scan is running via API
try {
const response = await AniWorld.ApiClient.get(API.ANIME_SCAN_STATUS);
if (!response || !response.ok) {
console.log('Could not fetch scan status');
return;
}
const data = await response.json();
console.log('Scan status for reopen:', data);
if (data.is_scanning) {
// A scan is in progress, show the overlay
showScanProgressOverlay({
directory: data.directory,
total_items: data.total_items
});
// Update with current progress
updateScanProgressOverlay({
directories_scanned: data.directories_scanned,
files_found: data.directories_scanned,
current_directory: data.current_directory,
total_items: data.total_items
});
}
} catch (error) {
console.error('Error checking scan status for reopen:', error);
}
}
/**
* Check if a scan is currently in progress
*/
async function checkActiveScanStatus() {
try {
const response = await AniWorld.ApiClient.get(API.ANIME_SCAN_STATUS);
if (!response || !response.ok) {
console.log('Could not fetch scan status, response:', response?.status);
return;
}
const data = await response.json();
console.log('Scan status check result:', data);
if (data.is_scanning) {
console.log('Scan is active, updating UI indicators');
// Update the process status indicator
updateProcessStatus('rescan', true);
// Show the overlay
showScanProgressOverlay({
directory: data.directory,
total_items: data.total_items
});
// Update with current progress
updateScanProgressOverlay({
directories_scanned: data.directories_scanned,
files_found: data.directories_scanned,
current_directory: data.current_directory,
total_items: data.total_items
});
} else {
console.log('No active scan detected');
updateProcessStatus('rescan', false);
}
} catch (error) {
console.error('Error checking scan status:', error);
}
}
/**
* Update process status indicator
* @param {string} processName - Process name (e.g., 'rescan', 'download')
* @param {boolean} isRunning - Whether the process is running
* @param {boolean} hasError - Whether there's an error
*/
function updateProcessStatus(processName, isRunning, hasError) {
hasError = hasError || false;
const statusElement = document.getElementById(processName + '-status');
if (!statusElement) {
console.warn('Process status element not found: ' + processName + '-status');
return;
}
const statusDot = statusElement.querySelector('.status-dot');
if (!statusDot) {
console.warn('Status dot not found in ' + processName + '-status element');
return;
}
// Remove all status classes
statusDot.classList.remove('idle', 'running', 'error');
statusElement.classList.remove('running', 'error', 'idle');
// Capitalize process name for display
const displayName = processName.charAt(0).toUpperCase() + processName.slice(1);
if (hasError) {
statusDot.classList.add('error');
statusElement.classList.add('error');
statusElement.title = displayName + ' error - click for details';
} else if (isRunning) {
statusDot.classList.add('running');
statusElement.classList.add('running');
statusElement.title = displayName + ' is running...';
} else {
statusDot.classList.add('idle');
statusElement.classList.add('idle');
statusElement.title = displayName + ' is idle';
}
console.log('Process status updated: ' + processName + ' = ' + (isRunning ? 'running' : (hasError ? 'error' : 'idle')));
}
// Public API
return {
init: init,
rescanSeries: rescanSeries,
showScanProgressOverlay: showScanProgressOverlay,
updateScanProgressOverlay: updateScanProgressOverlay,
hideScanProgressOverlay: hideScanProgressOverlay,
removeScanProgressOverlay: removeScanProgressOverlay,
reopenScanOverlay: reopenScanOverlay,
checkActiveScanStatus: checkActiveScanStatus,
updateProcessStatus: updateProcessStatus
};
})();