// JavaScript for Bookmarkly
document.addEventListener('DOMContentLoaded', function() {
// Handle Edit Category Modal
const editCategoryModal = document.getElementById('editCategoryModal');
if (editCategoryModal) {
editCategoryModal.addEventListener('show.bs.modal', function (event) {
const button = event.relatedTarget;
const id = button.getAttribute('data-bs-id');
const name = button.getAttribute('data-bs-name');
const idInput = editCategoryModal.querySelector('#edit-category-id');
const nameInput = editCategoryModal.querySelector('#edit-category-name');
idInput.value = id;
nameInput.value = name;
});
}
// --- Bookmark Modals ---
const editBookmarkModal = document.getElementById('editBookmarkModal');
if (editBookmarkModal) {
editBookmarkModal.addEventListener('show.bs.modal', function (event) {
const button = event.relatedTarget;
const id = button.getAttribute('data-bs-id');
const title = button.getAttribute('data-bs-title');
const url = button.getAttribute('data-bs-url');
const category = button.getAttribute('data-bs-category');
const icon = button.getAttribute('data-bs-icon');
const iconSrc = button.getAttribute('data-bs-icon-src');
const favorite = button.getAttribute('data-bs-favorite');
const modalTitle = editBookmarkModal.querySelector('.modal-title');
const idInput = editBookmarkModal.querySelector('#edit-bookmark-id');
const titleInput = editBookmarkModal.querySelector('#edit-bookmark-title');
const urlInput = editBookmarkModal.querySelector('#edit-bookmark-url');
const categoryInput = editBookmarkModal.querySelector('#edit-bookmark-category');
const iconUrlInput = editBookmarkModal.querySelector('#edit-bookmark-icon-url');
const favoriteInput = editBookmarkModal.querySelector('#edit-bookmark-favorite');
const currentIconPreview = document.getElementById('current-icon-preview');
const currentIconContainer = document.getElementById('current-icon-container');
const removeCustomIconBtn = document.getElementById('remove-custom-icon');
const removeIconInput = document.getElementById('remove-icon-input');
modalTitle.textContent = 'Edit ' + title;
idInput.value = id;
titleInput.value = title;
urlInput.value = url;
categoryInput.value = category;
favoriteInput.checked = (favorite == '1');
// Handle icon display logic
currentIconContainer.style.display = 'none'; // Hide by default
// Handle text input for icon URL/domain
if (icon && icon.startsWith('data/images/custom/')) {
iconUrlInput.value = ''; // Clear if it's a custom icon
} else {
// For domains or full URLs, just show the value in the input.
iconUrlInput.value = icon;
}
// Handle current icon preview and remove button
if (icon && icon.startsWith('data/images/custom/')) {
currentIconPreview.src = iconSrc;
currentIconContainer.style.display = 'block';
removeIconInput.value = '0';
} else {
currentIconContainer.style.display = 'none';
}
removeCustomIconBtn.onclick = function() {
removeIconInput.value = '1';
currentIconContainer.style.display = 'none';
document.getElementById('edit-bookmark-icon-upload').value = '';
};
});
// Reset remove icon flag when modal is hidden
editBookmarkModal.addEventListener('hidden.bs.modal', function () {
document.getElementById('remove-icon-input').value = '0';
document.getElementById('edit-bookmark-icon-upload').value = ''; // Clear file input
});
}
// --- URL Formatting ---
function formatUrl(inputElement) {
if (!inputElement || !inputElement.value) return;
let url = inputElement.value.trim();
if (!url) return;
// Check if URL already has a protocol
if (!/^https?:\/\//i.test(url)) {
// Add https:// by default
url = 'https://' + url;
inputElement.value = url;
}
}
// --- Favicon Fetching ---
function fetchFavicon(url, targetInputId) {
if (!url) return;
try {
// We only want to store the domain, not the full generated URL
const domain = new URL(url).hostname;
const targetInput = document.getElementById(targetInputId);
if (targetInput) {
targetInput.value = domain;
}
} catch (e) {
console.error('Invalid URL for favicon fetching:', e);
const targetInput = document.getElementById(targetInputId);
if (targetInput) {
targetInput.value = '';
}
}
}
// Event listener for fetching favicon in ADD modal
const fetchButtonAdd = document.getElementById('fetch-favicon-add');
const urlInputAdd = document.getElementById('bookmark-url');
if (fetchButtonAdd && urlInputAdd) {
// Auto-format URL with http/https
urlInputAdd.addEventListener('blur', function() {
formatUrl(urlInputAdd);
});
// Auto-fetch on change
urlInputAdd.addEventListener('change', function() {
fetchFavicon(urlInputAdd.value, 'bookmark-icon-url');
});
// Manual fetch on button click
fetchButtonAdd.addEventListener('click', function() {
fetchFavicon(urlInputAdd.value, 'bookmark-icon-url');
});
}
// Event listener for fetching favicon in EDIT modal
const fetchButtonEdit = document.getElementById('fetch-favicon-edit');
const urlInputEdit = document.getElementById('edit-bookmark-url');
if (fetchButtonEdit && urlInputEdit) {
// Auto-format URL with http/https
urlInputEdit.addEventListener('blur', function() {
formatUrl(urlInputEdit);
});
urlInputEdit.addEventListener('change', function() {
const iconContainer = document.getElementById('current-icon-container');
// Only fetch favicon if there is no custom icon currently set
if (iconContainer && iconContainer.style.display === 'none') {
fetchFavicon(urlInputEdit.value, 'edit-bookmark-icon-url');
}
});
fetchButtonEdit.addEventListener('click', function() {
fetchFavicon(urlInputEdit.value, 'edit-bookmark-icon-url');
});
}
// --- Pexels API ---
const pexelsSearchButton = document.getElementById('pexels_search_button');
const pexelsSearchQuery = document.getElementById('pexels_search_query');
const pexelsResultsContainer = document.getElementById('pexels_results');
const bgUrlInput = document.getElementById('background_image_url');
if (pexelsSearchButton && pexelsSearchQuery && pexelsResultsContainer && bgUrlInput) {
const performSearch = () => {
const query = pexelsSearchQuery.value;
if (!query) {
alert('Please enter a search term.');
return;
}
pexelsResultsContainer.innerHTML = '
Loading...
';
fetch(`api/pexels.php?query=${encodeURIComponent(query)}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
pexelsResultsContainer.innerHTML = '';
if (data.error) {
pexelsResultsContainer.innerHTML = ``;
return;
}
if (data.photos && data.photos.length > 0) {
data.photos.forEach(photo => {
const imgContainer = document.createElement('div');
imgContainer.className = 'col';
const img = document.createElement('img');
img.src = photo.src.medium;
img.className = 'img-thumbnail';
img.style.cursor = 'pointer';
img.alt = photo.alt;
img.addEventListener('click', () => {
bgUrlInput.value = photo.src.original;
// Optionally, show a preview
document.body.style.backgroundImage = `url(${photo.src.original})`;
pexelsResultsContainer.innerHTML = 'Background selected! Save theme settings to apply.
';
});
imgContainer.appendChild(img);
pexelsResultsContainer.appendChild(imgContainer);
});
} else {
pexelsResultsContainer.innerHTML = 'No images found for your query.
';
}
})
.catch(error => {
console.error('Error fetching from Pexels API:', error);
pexelsResultsContainer.innerHTML = 'An error occurred while searching.
';
});
};
pexelsSearchButton.addEventListener('click', performSearch);
pexelsSearchQuery.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
performSearch();
}
});
}
// Live preview for background settings
const brightnessSlider = document.getElementById('background_brightness');
const saturationSlider = document.getElementById('background_saturation');
const transparencySlider = document.getElementById('theme_transparency');
const themeSelector = document.getElementById('theme');
const transparencyContainer = document.getElementById('transparency-slider-container');
const updateTextColor = () => {
if (themeSelector && themeSelector.value !== 'dark' && brightnessSlider && brightnessSlider.value < 70) {
document.body.classList.add('dark-background-active');
} else {
document.body.classList.remove('dark-background-active');
}
};
const toggleTransparencySlider = () => {
if (themeSelector && transparencyContainer) {
transparencyContainer.style.display = themeSelector.value === 'transparent' ? 'block' : 'none';
}
};
// Initial check
toggleTransparencySlider();
if(themeSelector) {
themeSelector.addEventListener('change', () => {
toggleTransparencySlider();
updateTextColor();
});
}
if (brightnessSlider) {
brightnessSlider.addEventListener('input', (event) => {
document.documentElement.style.setProperty('--bg-brightness', `${event.target.value}%`);
updateTextColor();
});
}
if (saturationSlider) {
saturationSlider.addEventListener('input', (event) => {
document.documentElement.style.setProperty('--bg-saturation', `${event.target.value}%`);
});
}
if (transparencySlider) {
transparencySlider.addEventListener('input', (event) => {
document.documentElement.style.setProperty('--theme-transparency', event.target.value / 100);
});
}
// Initialize SortableJS for drag-and-drop
initSortable();
// Add form submit handlers to format URLs before submission
// Use event delegation to handle dynamically created forms
document.addEventListener('submit', function(event) {
const form = event.target;
if (form.tagName === 'FORM' && form.action.includes('bookmarks.php')) {
const actionInput = form.querySelector('input[name="action"]');
if (actionInput && (actionInput.value === 'add_bookmark' || actionInput.value === 'edit_bookmark')) {
const urlInput = form.querySelector('#bookmark-url') || form.querySelector('#edit-bookmark-url');
if (urlInput) {
formatUrl(urlInput);
}
}
}
});
});
function initSortable() {
const categoryList = document.getElementById('category-list');
if (categoryList) {
new Sortable(categoryList, {
handle: '.sort-handle',
animation: 150,
onEnd: function (evt) {
const order = Array.from(evt.target.children).map(el => el.getAttribute('data-id'));
updateSortOrder('category', order);
}
});
}
const bookmarkLists = document.querySelectorAll('.bookmark-list');
bookmarkLists.forEach(list => {
new Sortable(list, {
handle: '.sort-handle',
animation: 150,
onEnd: function (evt) {
const order = Array.from(evt.target.children).map(el => el.getAttribute('data-id'));
updateSortOrder('bookmark', order);
}
});
});
}
function updateSortOrder(type, order) {
fetch('api/update_order.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ type: type, order: order })
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error('Failed to update sort order:', data.error);
// Optionally, show an error message to the user
}
})
.catch(error => console.error('Error updating sort order:', error));
}
function optimizeImage(path) {
if (confirm('Are you sure you want to optimize this image? The original file will be overwritten.')) {
const formData = new FormData();
formData.append('path', path);
console.log('Optimizing image:', path);
console.log('Current URL:', window.location.href);
console.log('Request URL:', new URL('api/optimize_image.php', window.location.href).href);
fetch('api/optimize_image.php', {
method: 'POST',
body: formData,
credentials: 'same-origin',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => {
console.log('Response status:', response.status);
console.log('Response statusText:', response.statusText);
console.log('Response headers:', [...response.headers.entries()]);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status} - ${response.statusText}`);
}
return response.text(); // First get as text to see raw response
})
.then(text => {
console.log('Raw response:', text);
try {
const data = JSON.parse(text);
if (data.success) {
alert('Image optimized successfully. The page will now reload.');
window.location.reload();
} else {
alert('An error occurred: ' + data.message);
}
} catch (parseError) {
console.error('JSON parse error:', parseError);
console.error('Response text:', text);
alert('Invalid response from server. Check console for details.');
}
})
.catch(error => {
console.error('Fetch error:', error);
console.error('Error stack:', error.stack);
alert('A network error occurred: ' + error.message);
});
}
}