541 lines
20 KiB
JavaScript
541 lines
20 KiB
JavaScript
|
let currentYear;
|
|||
|
|
|||
|
//Adding new element in the array creates new tab
|
|||
|
const years = ['pre-2009', '2009', '2010', '2011', '2012', '2013', '2014', '2015',
|
|||
|
'2016','2017','2018','2019','2020','2021','2022','2023', '2024', 're-watch', 'manga'];
|
|||
|
|
|||
|
function loadYearTabs() {
|
|||
|
const yearTabs = document.getElementById('year-tabs');
|
|||
|
|
|||
|
// Populate the tabs
|
|||
|
years.forEach(year => {
|
|||
|
const li = document.createElement('li');
|
|||
|
li.textContent = year;
|
|||
|
li.classList.add('tab');
|
|||
|
li.dataset.year = year; // Set data-year attribute for each tab
|
|||
|
|
|||
|
// Add a click event listener to each tab
|
|||
|
li.addEventListener('click', () => {
|
|||
|
loadYearContent(year);
|
|||
|
});
|
|||
|
|
|||
|
yearTabs.appendChild(li);
|
|||
|
// Now check if all entries are completed for this year
|
|||
|
fetch(`php/check_completed_year.php?year=${encodeURIComponent(year)}`)
|
|||
|
.then(response => response.json())
|
|||
|
.then(data => {
|
|||
|
if (data.all_completed) {
|
|||
|
// Strike through the tab if all are completed
|
|||
|
li.style.textDecoration = 'line-through';
|
|||
|
}
|
|||
|
})
|
|||
|
.catch(error => console.error('Error checking completed status:', error));
|
|||
|
});
|
|||
|
|
|||
|
// Load the last active tab or default to 'pre-2009'
|
|||
|
const activeYear = localStorage.getItem('activeYear') || 'pre-2009';
|
|||
|
loadYearContent(activeYear);
|
|||
|
}
|
|||
|
|
|||
|
function loadYearContent(year) {
|
|||
|
currentYear = year;
|
|||
|
// Save the current year in localStorage for persistence
|
|||
|
localStorage.setItem('activeYear', year);
|
|||
|
|
|||
|
// Highlight the active tab
|
|||
|
const tabs = document.querySelectorAll('#year-tabs .tab');
|
|||
|
tabs.forEach(tab => {
|
|||
|
// Add or remove the active class based on the tab's data-year
|
|||
|
if (tab.dataset.year === year) {
|
|||
|
tab.classList.add('active');
|
|||
|
} else {
|
|||
|
tab.classList.remove('active');
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// Fetch and display content for the selected year
|
|||
|
fetch(`php/fetch_data.php?year=${encodeURIComponent(year)}`)
|
|||
|
.then(response => response.text())
|
|||
|
.then(html => {
|
|||
|
document.getElementById('content').innerHTML = html;
|
|||
|
setupFormSubmission();
|
|||
|
setupEditButtons();
|
|||
|
setupDeleteButtons();
|
|||
|
setupSuggestButton();
|
|||
|
setupSetCompleteButtons();
|
|||
|
setupSetCurrentlyWatchingButtons();
|
|||
|
})
|
|||
|
.catch(error => console.error('Error loading content:', error));
|
|||
|
}
|
|||
|
|
|||
|
function setupFormSubmission() {
|
|||
|
const form = document.getElementById('add-form');
|
|||
|
|
|||
|
function handleFormSubmit(event) {
|
|||
|
event.preventDefault();
|
|||
|
const formData = new FormData(form);
|
|||
|
fetch('php/add_record.php', {
|
|||
|
method: 'POST',
|
|||
|
body: formData
|
|||
|
})
|
|||
|
.then(response => {
|
|||
|
if (!response.ok) {
|
|||
|
// Если сервер вернул ошибку (например, 403)
|
|||
|
return response.json().then(errorData => {
|
|||
|
if (response.status === 403) {
|
|||
|
alert(errorData.message || 'Access denied: You are not authorized to perform this action.');
|
|||
|
}
|
|||
|
throw new Error(errorData.message || 'An error occurred.');
|
|||
|
});
|
|||
|
}
|
|||
|
return response.text();
|
|||
|
})
|
|||
|
.then(() => {
|
|||
|
// Если запись добавлена успешно
|
|||
|
loadYearContent(currentYear);
|
|||
|
})
|
|||
|
.catch(error => {
|
|||
|
console.error('Error:', error);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
form.addEventListener('submit', handleFormSubmit);
|
|||
|
|
|||
|
form.addEventListener('keydown', event => {
|
|||
|
if (event.ctrlKey && event.key === 'Enter') {
|
|||
|
event.preventDefault();
|
|||
|
handleFormSubmit(event);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
function setupEditButtons() {
|
|||
|
const editButtons = document.querySelectorAll('.edit-button');
|
|||
|
editButtons.forEach(button => {
|
|||
|
button.addEventListener('click', () => {
|
|||
|
const recordId = button.dataset.id;
|
|||
|
openEditModal(recordId);
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function setupDeleteButtons() {
|
|||
|
const deleteButtons = document.querySelectorAll('.delete-button');
|
|||
|
deleteButtons.forEach(button => {
|
|||
|
button.addEventListener('click', () => {
|
|||
|
const recordId = button.dataset.id;
|
|||
|
if (confirm('Are you sure you want to delete this record?')) {
|
|||
|
fetch(`php/delete_record.php?id=${recordId}`, {
|
|||
|
method: 'DELETE' // Используем метод DELETE для удаления
|
|||
|
})
|
|||
|
.then(response => {
|
|||
|
if (!response.ok) {
|
|||
|
// Если сервер вернул ошибку (например, 403)
|
|||
|
return response.json().then(errorData => {
|
|||
|
if (response.status === 403) {
|
|||
|
alert(errorData.message || 'Access denied: You are not authorized to delete this record.');
|
|||
|
}
|
|||
|
throw new Error(errorData.message || 'An error occurred while deleting the record.');
|
|||
|
});
|
|||
|
}
|
|||
|
return response.text();
|
|||
|
})
|
|||
|
.then(() => {
|
|||
|
// Если удаление прошло успешно
|
|||
|
loadYearContent(currentYear);
|
|||
|
})
|
|||
|
.catch(error => {
|
|||
|
console.error('Error:', error);
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
function openEditModal(id) {
|
|||
|
// Fetch record data
|
|||
|
fetch(`php/fetch_record.php?id=${id}`)
|
|||
|
.then(response => response.json())
|
|||
|
.then(data => {
|
|||
|
const modal = document.getElementById('edit-modal');
|
|||
|
const form = document.getElementById('edit-form');
|
|||
|
form.innerHTML = generateEditForm(data);
|
|||
|
modal.style.display = 'block';
|
|||
|
setupEditFormSubmission();
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function setupModal() {
|
|||
|
const modal = document.getElementById('edit-modal');
|
|||
|
const closeModal = document.getElementById('close-modal');
|
|||
|
closeModal.addEventListener('click', () => modal.style.display = 'none');
|
|||
|
|
|||
|
window.addEventListener('keydown', event => {
|
|||
|
if (event.key === 'Escape') {
|
|||
|
modal.style.display = 'none';
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// Close modal when clicking outside the modal content
|
|||
|
modal.addEventListener('click', event => {
|
|||
|
if (event.target === modal) {
|
|||
|
modal.style.display = 'none';
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function mapLabelToYear(label) {
|
|||
|
if (label === 'pre-2009') return -1;
|
|||
|
if (label === 're-watch') return -2;
|
|||
|
return parseInt(label, 10);
|
|||
|
}
|
|||
|
|
|||
|
function setupEditFormSubmission() {
|
|||
|
const form = document.getElementById('edit-form');
|
|||
|
|
|||
|
function handleEditFormSubmit(event) {
|
|||
|
event.preventDefault();
|
|||
|
|
|||
|
const yearLabelSelect = form.querySelector('#year_label');
|
|||
|
const numericYearInput = form.querySelector('#year_numeric');
|
|||
|
const selectedLabel = yearLabelSelect.value;
|
|||
|
|
|||
|
// Use the mapLabelToYear function
|
|||
|
numericYearInput.value = mapLabelToYear(selectedLabel);
|
|||
|
|
|||
|
const formData = new FormData(form);
|
|||
|
fetch('php/edit_record.php', {
|
|||
|
method: 'POST',
|
|||
|
body: formData
|
|||
|
})
|
|||
|
.then(response => {
|
|||
|
if (!response.ok) {
|
|||
|
return response.json().then(errorData => {
|
|||
|
if (response.status === 403) {
|
|||
|
alert(errorData.message || 'Access denied: You are not authorized to edit this record.');
|
|||
|
}
|
|||
|
throw new Error(errorData.message || 'An error occurred while editing the record.');
|
|||
|
});
|
|||
|
}
|
|||
|
return response.text();
|
|||
|
})
|
|||
|
.then(() => {
|
|||
|
const modal = document.getElementById('edit-modal');
|
|||
|
modal.style.display = 'none';
|
|||
|
loadYearContent(currentYear);
|
|||
|
})
|
|||
|
.catch(error => {
|
|||
|
console.error('Error:', error);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
form.addEventListener('submit', handleEditFormSubmit);
|
|||
|
|
|||
|
form.addEventListener('keydown', event => {
|
|||
|
if (event.ctrlKey && event.key === 'Enter') {
|
|||
|
event.preventDefault();
|
|||
|
handleEditFormSubmit(event);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function generateEditForm(data) {
|
|||
|
|
|||
|
function mapYearToLabel(yearValue) {
|
|||
|
if (yearValue == -1) return 'pre-2009';
|
|||
|
if (yearValue == -2) return 're-watch';
|
|||
|
if (yearValue == -3) return 'manga';
|
|||
|
return yearValue.toString();
|
|||
|
}
|
|||
|
|
|||
|
const selectedYearLabel = mapYearToLabel(data.year);
|
|||
|
|
|||
|
return `
|
|||
|
<h3>Edit Record</h3>
|
|||
|
<input type="hidden" name="id" value="${data.id}">
|
|||
|
<input type="hidden" name="year" id="year_numeric" value="${data.year}">
|
|||
|
|
|||
|
<div class="form-group">
|
|||
|
<label for="name">Name:</label>
|
|||
|
<input type="text" name="name" id="name" class="form-control" value="${data.name || ''}" required>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- Wrap season, year, and type fields in a single container -->
|
|||
|
<div class="form-row">
|
|||
|
<div class="form-group">
|
|||
|
<label for="season">Season:</label>
|
|||
|
<select name="season" id="season" class="form-control">
|
|||
|
${['winter', 'spring', 'summer', 'fall', 'omake'].map(season => `
|
|||
|
<option value="${season}" ${data.season === season ? 'selected' : ''}>
|
|||
|
${season.charAt(0).toUpperCase() + season.slice(1)}
|
|||
|
</option>
|
|||
|
`).join('')}
|
|||
|
</select>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-group">
|
|||
|
<label for="year_label">Year:</label>
|
|||
|
<select name="year_label" id="year_label" class="form-control">
|
|||
|
${years.map(y => `
|
|||
|
<option value="${y}" ${y === selectedYearLabel ? 'selected' : ''}>${y}</option>
|
|||
|
`).join('')}
|
|||
|
</select>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-group">
|
|||
|
<label for="type">Type:</label>
|
|||
|
<select name="type" id="type" class="form-control">
|
|||
|
${['tv', 'special', 'short', 'ova', 'movie', 'other'].map(type => `
|
|||
|
<option value="${type}" ${data.type === type ? 'selected' : ''}>${type}</option>
|
|||
|
`).join('')}
|
|||
|
</select>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-group">
|
|||
|
<label for="comment">Comment:</label>
|
|||
|
<input type="text" name="comment" id="comment" class="form-control" value="${data.comment || ''}">
|
|||
|
</div>
|
|||
|
<div class="form-group">
|
|||
|
<label for="date_completed">Date Completed:</label>
|
|||
|
<input type="date" name="date_completed" id="date_completed" class="form-control" value="${data.date_completed || ''}">
|
|||
|
</div>
|
|||
|
<div class="form-group">
|
|||
|
<label for="url">URL:</label>
|
|||
|
<input type="url" name="url" id="url" class="form-control" value="${data.url || ''}">
|
|||
|
</div>
|
|||
|
<div class="form-group form-check">
|
|||
|
<input type="checkbox" name="is_completed" id="is_completed" class="form-check-input" ${parseInt(data.is_completed) ? 'checked' : ''}>
|
|||
|
<label for="is_completed" class="form-check-label">Is Completed</label>
|
|||
|
</div>
|
|||
|
<div class="form-group form-check">
|
|||
|
<input type="checkbox" name="currently_watching" id="currently_watching" class="form-check-input" ${parseInt(data.currently_watching) ? 'checked' : ''}>
|
|||
|
<label for="currently_watching" class="form-check-label">Currently Watching</label>
|
|||
|
</div>
|
|||
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
|||
|
`;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
function setupSuggestButton() {
|
|||
|
const suggestButton = document.getElementById('suggest-button');
|
|||
|
if (suggestButton) {
|
|||
|
suggestButton.addEventListener('click', () => {
|
|||
|
makeSuggestion();
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
function makeSuggestion() {
|
|||
|
// Clear any previous suggestions
|
|||
|
const previousSuggestion = document.getElementById('suggestion-display');
|
|||
|
if (previousSuggestion) {
|
|||
|
previousSuggestion.remove();
|
|||
|
}
|
|||
|
|
|||
|
// Get all table rows that are not completed
|
|||
|
const rows = Array.from(document.querySelectorAll('table tbody tr')).filter(row => {
|
|||
|
return !row.classList.contains('completed');
|
|||
|
});
|
|||
|
|
|||
|
// Exclude header rows and ensure there are at least two entries
|
|||
|
if (rows.length < 1) {
|
|||
|
alert('Not enough uncompleted entries to make a suggestion.');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Randomly select two different rows
|
|||
|
const indices = [];
|
|||
|
while (indices.length < 1) {
|
|||
|
const index = Math.floor(Math.random() * rows.length);
|
|||
|
if (!indices.includes(index)) {
|
|||
|
indices.push(index);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const selectedRows = [rows[indices[0]]];
|
|||
|
|
|||
|
// Get the names of the selected records
|
|||
|
const names = selectedRows.map(row => row.getAttribute('data-name'));
|
|||
|
|
|||
|
// Display the names at the top of the content
|
|||
|
const content = document.getElementById('content');
|
|||
|
const suggestionDisplayDiv = document.createElement('div');
|
|||
|
suggestionDisplayDiv.id = 'suggestion-display';
|
|||
|
suggestionDisplayDiv.innerHTML = `<p>${names.join('<br> ')}</p>`;
|
|||
|
content.insertBefore(suggestionDisplayDiv, content.querySelector('button')); // Insert before the add form
|
|||
|
}
|
|||
|
|
|||
|
function setupStickyTabs() {
|
|||
|
const tabsContainer = document.querySelector('.tabs-container');
|
|||
|
|
|||
|
const observer = new IntersectionObserver(
|
|||
|
([e]) => e.target.classList.toggle('is-sticky', e.intersectionRatio < 1),
|
|||
|
{ threshold: [1] }
|
|||
|
);
|
|||
|
|
|||
|
observer.observe(tabsContainer);
|
|||
|
}
|
|||
|
|
|||
|
function setupSetCompleteButtons() {
|
|||
|
const setCompleteButtons = document.querySelectorAll('.set-complete-button');
|
|||
|
setCompleteButtons.forEach(button => {
|
|||
|
button.addEventListener('click', () => {
|
|||
|
const recordId = button.dataset.id;
|
|||
|
const formData = new FormData();
|
|||
|
formData.append('id', recordId);
|
|||
|
fetch('php/set_complete.php', {
|
|||
|
method: 'POST',
|
|||
|
body: formData
|
|||
|
})
|
|||
|
.then(response => {
|
|||
|
if (!response.ok) {
|
|||
|
return response.json().then(errorData => {
|
|||
|
if (response.status === 403) {
|
|||
|
alert(errorData.message || 'Access denied: You are not authorized to perform this action.');
|
|||
|
}
|
|||
|
throw new Error(errorData.message || 'An error occurred while setting the record as complete.');
|
|||
|
});
|
|||
|
}
|
|||
|
return response.json();
|
|||
|
})
|
|||
|
.then(() => {
|
|||
|
// Reload the table to reflect the changes
|
|||
|
loadYearContent(currentYear);
|
|||
|
})
|
|||
|
.catch(error => {
|
|||
|
console.error('Error:', error);
|
|||
|
});
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function setupSetCurrentlyWatchingButtons() {
|
|||
|
const setCurrentlyWatchingButtons = document.querySelectorAll('.set-currently-watching-button');
|
|||
|
setCurrentlyWatchingButtons.forEach(button => {
|
|||
|
button.addEventListener('click', () => {
|
|||
|
const recordId = button.dataset.id;
|
|||
|
const formData = new FormData();
|
|||
|
formData.append('id', recordId);
|
|||
|
fetch('php/set_currently_watching.php', {
|
|||
|
method: 'POST',
|
|||
|
body: formData
|
|||
|
})
|
|||
|
.then(response => {
|
|||
|
if (!response.ok) {
|
|||
|
return response.json().then(errorData => {
|
|||
|
if (response.status === 403) {
|
|||
|
alert(errorData.message || 'Access denied: You are not authorized to perform this action.');
|
|||
|
}
|
|||
|
throw new Error(errorData.message || 'An error occurred while setting the record as currently watching.');
|
|||
|
});
|
|||
|
}
|
|||
|
return response.json();
|
|||
|
})
|
|||
|
.then(() => {
|
|||
|
// Reload the table to reflect the changes
|
|||
|
loadYearContent(currentYear);
|
|||
|
})
|
|||
|
.catch(error => {
|
|||
|
console.error('Error:', error);
|
|||
|
});
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
document.addEventListener('DOMContentLoaded', () => {
|
|||
|
console.log('DOM fully loaded and parsed');
|
|||
|
loadYearTabs();
|
|||
|
setupModal();
|
|||
|
var imageCache = {};
|
|||
|
var currentHoverTarget = null;
|
|||
|
|
|||
|
function showImageTooltip(event, imageUrl) {
|
|||
|
var tooltip = document.getElementById('image-tooltip');
|
|||
|
if (!tooltip) {
|
|||
|
tooltip = document.createElement('div');
|
|||
|
tooltip.id = 'image-tooltip';
|
|||
|
tooltip.style.position = 'absolute';
|
|||
|
tooltip.style.border = '1px solid #ccc';
|
|||
|
tooltip.style.background = '#fff';
|
|||
|
tooltip.style.padding = '5px';
|
|||
|
tooltip.style.zIndex = 1000;
|
|||
|
tooltip.style.maxWidth = '200px';
|
|||
|
tooltip.style.maxHeight = '300px';
|
|||
|
tooltip.style.overflow = 'hidden';
|
|||
|
tooltip.style.display = 'none';
|
|||
|
document.body.appendChild(tooltip);
|
|||
|
}
|
|||
|
|
|||
|
tooltip.innerHTML = '<img src="' + imageUrl + '" style="max-width: 100%; max-height: 100%;">';
|
|||
|
tooltip.style.left = (event.pageX + 15) + 'px';
|
|||
|
tooltip.style.top = (event.pageY + 15) + 'px';
|
|||
|
tooltip.style.display = 'block';
|
|||
|
}
|
|||
|
|
|||
|
function hideImageTooltip() {
|
|||
|
var tooltip = document.getElementById('image-tooltip');
|
|||
|
if (tooltip) {
|
|||
|
tooltip.style.display = 'none';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Добавляем обработчики событий на родительский элемент
|
|||
|
var contentDiv = document.getElementById('content');
|
|||
|
|
|||
|
contentDiv.addEventListener('mouseover', function (event) {
|
|||
|
var target = event.target;
|
|||
|
|
|||
|
// Проверяем, является ли целевой элемент ссылкой с атрибутом data-url
|
|||
|
if (target.tagName.toLowerCase() === 'a' && target.hasAttribute('data-url')) {
|
|||
|
var url = target.getAttribute('data-url');
|
|||
|
currentHoverTarget = target; // Set the current hover target
|
|||
|
|
|||
|
if (imageCache[url]) {
|
|||
|
showImageTooltip(event, imageCache[url]);
|
|||
|
} else {
|
|||
|
var xhr = new XMLHttpRequest();
|
|||
|
xhr.open('GET', './php/get_image.php?url=' + encodeURIComponent(url), true);
|
|||
|
xhr.onreadystatechange = function () {
|
|||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
|||
|
var response = JSON.parse(xhr.responseText);
|
|||
|
if (response.image_url) {
|
|||
|
imageCache[url] = response.image_url;
|
|||
|
if (currentHoverTarget === target) {
|
|||
|
showImageTooltip(event, response.image_url);
|
|||
|
}
|
|||
|
} else if (response.error) {
|
|||
|
console.error(response.error);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
xhr.send();
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
contentDiv.addEventListener('mouseout', function (event) {
|
|||
|
var target = event.target;
|
|||
|
|
|||
|
if (target.tagName.toLowerCase() === 'a' && target.hasAttribute('data-url')) {
|
|||
|
hideImageTooltip();
|
|||
|
if (currentHoverTarget === target) {
|
|||
|
currentHoverTarget = null; // Clear the current hover target
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
contentDiv.addEventListener('mousemove', function (event) {
|
|||
|
var tooltip = document.getElementById('image-tooltip');
|
|||
|
if (tooltip && tooltip.style.display === 'block') {
|
|||
|
tooltip.style.left = (event.pageX + 15) + 'px';
|
|||
|
tooltip.style.top = (event.pageY + 15) + 'px';
|
|||
|
}
|
|||
|
});
|
|||
|
});
|