// 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 = `
${data.error}
`; 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); fetch('api/optimize_image.php', { method: 'POST', body: formData }) .then(response => { console.log('Response status:', response.status); console.log('Response headers:', response.headers); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } 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); alert('A network error occurred: ' + error.message); }); } }