document.addEventListener('DOMContentLoaded', () => { // ===== Theme Toggle ===== const themeCheckbox = document.getElementById('theme-toggle-checkbox'); const themeIconLight = document.getElementById('theme-icon-light'); const themeLabelSpan = document.querySelector('.theme-toggle-label span'); function updateTheme(theme) { document.documentElement.setAttribute('data-theme', theme); localStorage.setItem('theme', theme); if (themeCheckbox) { themeCheckbox.checked = theme === 'dark'; } if (themeIconLight) { themeIconLight.className = theme === 'dark' ? 'mdi mdi-weather-night' : 'mdi mdi-weather-sunny'; } if (themeLabelSpan) { themeLabelSpan.textContent = theme === 'dark' ? 'Dark Mode' : 'Light Mode'; } } // Init Theme const savedTheme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); updateTheme(savedTheme); if (themeCheckbox) { themeCheckbox.addEventListener('change', () => { const next = themeCheckbox.checked ? 'dark' : 'light'; updateTheme(next); }); } // ===== Mobile Sidebar Toggle ===== const sidebar = document.getElementById('sidebar'); const sidebarOverlay = document.getElementById('sidebar-overlay'); const mobileMenuBtn = document.getElementById('mobile-menu-btn'); function toggleSidebar() { sidebar?.classList.toggle('show'); sidebarOverlay?.classList.toggle('show'); } mobileMenuBtn?.addEventListener('click', toggleSidebar); sidebarOverlay?.addEventListener('click', toggleSidebar); // ===== Search Overlay ===== const searchOverlay = document.getElementById('search-overlay'); const searchToggleBtn = document.getElementById('search-toggle-btn'); const searchModalInput = document.getElementById('search-modal-input'); function openSearch() { searchOverlay?.classList.add('show'); setTimeout(() => searchModalInput?.focus(), 100); } function closeSearch() { searchOverlay?.classList.remove('show'); } searchToggleBtn?.addEventListener('click', openSearch); // Close search on overlay click searchOverlay?.addEventListener('click', (e) => { if (e.target === searchOverlay) { closeSearch(); } }); // Keyboard shortcut: S to open search, ESC to close document.addEventListener('keydown', (e) => { // Don't trigger if typing in an input if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { if (e.key === 'Escape') { closeSearch(); } return; } if (e.key === 's' || e.key === 'S') { e.preventDefault(); openSearch(); } if (e.key === 'Escape') { closeSearch(); closeFilterPanel(); } }); // ===== Filter Panel ===== const filterToggleBtn = document.getElementById('filter-toggle-btn'); const filterPanel = document.getElementById('filter-panel'); const filterCloseBtn = document.getElementById('filter-close-btn'); const filterPrivate = document.getElementById('filter-private'); const filterPublic = document.getElementById('filter-public'); const filterUntagged = document.getElementById('filter-untagged'); function toggleFilterPanel() { filterPanel?.classList.toggle('show'); } function closeFilterPanel() { filterPanel?.classList.remove('show'); } filterToggleBtn?.addEventListener('click', (e) => { e.stopPropagation(); toggleFilterPanel(); }); filterCloseBtn?.addEventListener('click', closeFilterPanel); // Close filter when clicking outside document.addEventListener('click', (e) => { if (filterPanel?.classList.contains('show')) { if (!filterPanel.contains(e.target) && e.target !== filterToggleBtn) { closeFilterPanel(); } } }); // Handle filter toggle switches - using data-url like the example document.querySelectorAll('.toggle-switch input[type="checkbox"]').forEach(checkbox => { checkbox.addEventListener('change', (e) => { const label = e.target.closest('label'); if (label && label.dataset.url) { window.location.href = label.dataset.url; } }); }); // Handle links per page options // Handle custom value form submission const filterInput = document.querySelector('.filter-input'); if (filterInput) { filterInput.closest('form')?.addEventListener('submit', (e) => { e.preventDefault(); const value = filterInput.value; if (value && value > 0) { window.location.href = shaarli.basePath + '/?nb=' + value; } }); } // ===== View Toggle (Grid/List/Compact) ===== const linksList = document.getElementById('links-list'); const viewGridBtn = document.getElementById('view-grid-btn'); const viewListBtn = document.getElementById('view-list-btn'); const viewCompactBtn = document.getElementById('view-compact-btn'); function setView(view) { if (!linksList) return; // Remove all view classes linksList.classList.remove('view-grid', 'view-list', 'view-compact'); // Remove active state from all buttons viewGridBtn?.classList.remove('active'); viewListBtn?.classList.remove('active'); viewCompactBtn?.classList.remove('active'); // Apply selected view if (view === 'list') { linksList.classList.add('view-list'); viewListBtn?.classList.add('active'); } else if (view === 'compact') { linksList.classList.add('view-compact'); viewCompactBtn?.classList.add('active'); } else { // Default to grid linksList.classList.add('view-grid'); viewGridBtn?.classList.add('active'); } localStorage.setItem('linksView', view); } // Init view from localStorage const savedView = localStorage.getItem('linksView') || 'grid'; setView(savedView); viewGridBtn?.addEventListener('click', () => setView('grid')); viewListBtn?.addEventListener('click', () => setView('list')); viewCompactBtn?.addEventListener('click', () => setView('compact')); // ===== Multi-Select Mode ===== const selectModeBtn = document.getElementById('select-mode-btn'); const bulkActionsBar = document.getElementById('bulk-actions-bar'); const bulkCount = document.getElementById('bulk-count'); const bulkSelectAll = document.getElementById('bulk-select-all'); const bulkCancel = document.getElementById('bulk-cancel'); const bulkDelete = document.getElementById('bulk-delete'); const bulkPublic = document.getElementById('bulk-public'); const bulkPrivate = document.getElementById('bulk-private'); let selectionMode = false; let selectedIds = new Set(); function updateBulkUI() { if (bulkCount) { bulkCount.textContent = selectedIds.size; } // Update link cards visual state document.querySelectorAll('.link-outer').forEach(card => { const id = card.dataset.id; if (selectedIds.has(id)) { card.classList.add('selected'); } else { card.classList.remove('selected'); } }); // Update checkboxes document.querySelectorAll('.link-checkbox').forEach(cb => { cb.checked = selectedIds.has(cb.dataset.id); }); } function enterSelectionMode() { selectionMode = true; document.body.classList.add('selection-mode'); bulkActionsBar?.classList.add('show'); selectModeBtn?.classList.add('active'); } function exitSelectionMode() { selectionMode = false; selectedIds.clear(); document.body.classList.remove('selection-mode'); bulkActionsBar?.classList.remove('show'); selectModeBtn?.classList.remove('active'); updateBulkUI(); } function toggleSelection(id) { if (selectedIds.has(id)) { selectedIds.delete(id); } else { selectedIds.add(id); } if (selectedIds.size > 0 && !selectionMode) { enterSelectionMode(); } else if (selectedIds.size === 0 && selectionMode) { exitSelectionMode(); } updateBulkUI(); } selectModeBtn?.addEventListener('click', () => { if (selectionMode) { exitSelectionMode(); } else { enterSelectionMode(); } }); bulkCancel?.addEventListener('click', exitSelectionMode); bulkSelectAll?.addEventListener('click', () => { document.querySelectorAll('.link-outer').forEach(card => { if (card.dataset.id) { selectedIds.add(card.dataset.id); } }); updateBulkUI(); }); // Handle checkbox clicks document.addEventListener('change', (e) => { if (e.target.classList.contains('link-checkbox')) { toggleSelection(e.target.dataset.id); } }); // Handle card clicks in selection mode document.addEventListener('click', (e) => { if (!selectionMode) return; const card = e.target.closest('.link-outer'); if (card && card.dataset.id) { // Don't toggle if clicking on actions or links if (e.target.closest('.link-actions') || e.target.closest('.link-hover-actions') || e.target.tagName === 'A') { return; } toggleSelection(card.dataset.id); } }); // Bulk actions bulkDelete?.addEventListener('click', () => { if (selectedIds.size === 0) return; if (!confirm(`Delete ${selectedIds.size} bookmark(s)?`)) return; // Submit form with selected IDs const form = document.createElement('form'); form.method = 'POST'; form.action = shaarli.basePath + '/admin/shaare/delete'; selectedIds.forEach(id => { const input = document.createElement('input'); input.type = 'hidden'; input.name = 'id[]'; input.value = id; form.appendChild(input); }); const tokenInput = document.createElement('input'); tokenInput.type = 'hidden'; tokenInput.name = 'token'; tokenInput.value = document.querySelector('input[name="token"]')?.value || ''; form.appendChild(tokenInput); document.body.appendChild(form); form.submit(); }); bulkPublic?.addEventListener('click', () => { if (selectedIds.size === 0) return; bulkVisibilityChange('public'); }); bulkPrivate?.addEventListener('click', () => { if (selectedIds.size === 0) return; bulkVisibilityChange('private'); }); function bulkVisibilityChange(visibility) { const form = document.createElement('form'); form.method = 'POST'; form.action = shaarli.basePath + '/admin/shaare/visibility'; selectedIds.forEach(id => { const input = document.createElement('input'); input.type = 'hidden'; input.name = 'id[]'; input.value = id; form.appendChild(input); }); const visInput = document.createElement('input'); visInput.type = 'hidden'; visInput.name = 'visibility'; visInput.value = visibility; form.appendChild(visInput); const tokenInput = document.createElement('input'); tokenInput.type = 'hidden'; tokenInput.name = 'token'; tokenInput.value = document.querySelector('input[name="token"]')?.value || ''; form.appendChild(tokenInput); document.body.appendChild(form); form.submit(); } // ===== Search Type Toggle ===== const searchTagsBtn = document.getElementById('search-tags-btn'); const searchAllBtn = document.getElementById('search-all-btn'); const searchForm = document.getElementById('search-form'); searchTagsBtn?.addEventListener('click', (e) => { e.preventDefault(); searchModalInput.name = 'searchtags'; searchAllBtn?.classList.remove('active'); searchTagsBtn?.classList.add('active'); searchForm?.submit(); }); searchAllBtn?.addEventListener('click', (e) => { e.preventDefault(); searchModalInput.name = 'searchterm'; searchTagsBtn?.classList.remove('active'); searchAllBtn?.classList.add('active'); searchForm?.submit(); }); // ===== Thumbnail Update ===== const thumbnailsPage = document.querySelector('.page-thumbnails'); if (thumbnailsPage) { const thumbnailPlaceholder = document.querySelector('.thumbnail-placeholder'); const thumbnailTitle = document.querySelector('.thumbnail-link-title'); const progressCurrent = document.querySelector('.progress-current'); const progressBarActual = document.querySelector('.progress-actual'); const idsInput = document.querySelector('input[name="ids"]'); if (idsInput && idsInput.value) { const thumbnailsIdList = idsInput.value.split(','); const total = thumbnailsIdList.length; let i = 0; const updateThumbnail = function (id) { console.log('Updating thumbnail #' + i + ' with id ' + id); fetch(shaarli.basePath + '/admin/shaare/' + id + '/update-thumbnail', { method: 'PATCH', headers: { 'Accept': 'application/json', } }) .then(response => response.json()) .then(data => { i++; if (thumbnailTitle) { thumbnailTitle.textContent = data.title || 'Processing...'; } if (thumbnailPlaceholder) { if (data.thumbnail) { thumbnailPlaceholder.innerHTML = ''; } else { thumbnailPlaceholder.innerHTML = ''; } } if (progressCurrent) { progressCurrent.textContent = i; } if (progressBarActual) { progressBarActual.style.width = ((i * 100) / total) + '%'; } if (i < total) { updateThumbnail(thumbnailsIdList[i]); } else { if (thumbnailTitle) { thumbnailTitle.textContent = 'Thumbnail update done!'; } } }) .catch(error => { console.error('Failed to update thumbnail:', error); if (thumbnailTitle) { thumbnailTitle.textContent = 'Error updating thumbnail #' + i; } // Continue with next thumbnail even if one fails i++; if (i < total) { updateThumbnail(thumbnailsIdList[i]); } }); }; // Start the update process updateThumbnail(thumbnailsIdList[0]); } } });