| |
| |
| |
| |
|
|
| document.addEventListener('DOMContentLoaded', () => { |
| initTheme(); |
| initNavigation(); |
| initMobileMenu(); |
| initSetupNotice(); |
| initPdfEditor(); |
| initImageScraper(); |
| initGlobalShortcuts(); |
| initPdfTools(); |
| initImageTools(); |
| }); |
|
|
| |
| |
| |
| function initTheme() { |
| const themeToggle = document.getElementById('themeToggle'); |
| const savedTheme = localStorage.getItem('theme') || 'light'; |
| |
| document.documentElement.setAttribute('data-theme', savedTheme); |
| |
| themeToggle.addEventListener('click', () => { |
| const currentTheme = document.documentElement.getAttribute('data-theme'); |
| const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; |
| |
| document.documentElement.setAttribute('data-theme', newTheme); |
| localStorage.setItem('theme', newTheme); |
| |
| showToast( |
| newTheme === 'dark' ? 'Dark Mode' : 'Light Mode', |
| 'Theme updated', |
| 'success', |
| 2000 |
| ); |
| }); |
| } |
|
|
| |
| |
| |
| function initSetupNotice() { |
| const notice = document.getElementById('setupNotice'); |
| const closeBtn = document.getElementById('closeSetupNotice'); |
| |
| |
| if (localStorage.getItem('setupNoticeDismissed')) { |
| notice.classList.add('hidden'); |
| } |
| |
| |
| closeBtn.addEventListener('click', () => { |
| notice.classList.add('hidden'); |
| localStorage.setItem('setupNoticeDismissed', 'true'); |
| }); |
| |
| |
| document.querySelectorAll('.copy-btn').forEach(btn => { |
| btn.addEventListener('click', async () => { |
| const text = btn.dataset.copy; |
| |
| try { |
| await navigator.clipboard.writeText(text); |
| btn.classList.add('copied'); |
| showToast('Copied!', text, 'success', 2000); |
| |
| setTimeout(() => { |
| btn.classList.remove('copied'); |
| }, 2000); |
| } catch (err) { |
| showToast('Copy failed', 'Please copy manually', 'error'); |
| } |
| }); |
| }); |
| } |
|
|
| |
| |
| |
| function initNavigation() { |
| const navItems = document.querySelectorAll('.nav-item[data-page]'); |
| const pages = document.querySelectorAll('.page'); |
|
|
| navItems.forEach(item => { |
| item.addEventListener('click', () => { |
| const targetPage = item.dataset.page; |
| switchToPage(targetPage); |
| |
| |
| closeMobileMenu(); |
| }); |
| }); |
| } |
|
|
| function switchToPage(pageName) { |
| const navItems = document.querySelectorAll('.nav-item[data-page]'); |
| const pages = document.querySelectorAll('.page'); |
| |
| navItems.forEach(item => { |
| item.classList.toggle('active', item.dataset.page === pageName); |
| }); |
| |
| pages.forEach(page => { |
| page.classList.toggle('active', page.id === `page-${pageName}`); |
| }); |
| } |
|
|
| |
| |
| |
| function initMobileMenu() { |
| const menuBtn = document.getElementById('mobileMenuBtn'); |
| const sidebar = document.getElementById('sidebar'); |
| const overlay = document.getElementById('sidebarOverlay'); |
| |
| menuBtn.addEventListener('click', () => { |
| sidebar.classList.toggle('open'); |
| overlay.classList.toggle('active'); |
| }); |
| |
| overlay.addEventListener('click', closeMobileMenu); |
| } |
|
|
| function closeMobileMenu() { |
| const sidebar = document.getElementById('sidebar'); |
| const overlay = document.getElementById('sidebarOverlay'); |
| sidebar.classList.remove('open'); |
| overlay.classList.remove('active'); |
| } |
|
|
| |
| |
| |
| function initGlobalShortcuts() { |
| registerShortcut('1', () => switchToPage('pdf'), 'PDF Editor'); |
| registerShortcut('2', () => switchToPage('watermark'), 'Watermark Remover'); |
| registerShortcut('3', () => switchToPage('img2pdf'), 'Images to PDF'); |
| registerShortcut('4', () => switchToPage('merge'), 'Merge PDFs'); |
| registerShortcut('5', () => switchToPage('split'), 'Split PDF'); |
| registerShortcut('6', () => switchToPage('images'), 'Image Scraper'); |
| |
| registerShortcut('ctrl+k', () => { |
| document.getElementById('themeToggle').click(); |
| }, 'Toggle Theme'); |
| |
| registerShortcut('?', () => { |
| showKeyboardShortcutsModal(); |
| }, 'Show Shortcuts'); |
| |
| |
| document.getElementById('navRecentFiles').addEventListener('click', showRecentFilesModal); |
| document.getElementById('navKeyboardShortcuts').addEventListener('click', showKeyboardShortcutsModal); |
| |
| |
| initWatermarkRemoval(); |
| } |
|
|
| |
| |
| |
| function showRecentFilesModal() { |
| const recentFiles = JSON.parse(localStorage.getItem('recentFiles') || '[]'); |
| |
| let content = ''; |
| if (recentFiles.length === 0) { |
| content = ` |
| <div class="empty-state" style="padding: 40px 20px;"> |
| <div class="empty-state-illustration" style="width: 80px; height: 80px;"> |
| <svg width="32" height="32" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/> |
| </svg> |
| </div> |
| <div class="empty-state-title" style="font-size: 16px;">No Recent Files</div> |
| <div class="empty-state-message" style="font-size: 13px;">Files you work with will appear here</div> |
| </div> |
| `; |
| } else { |
| content = ` |
| <div style="padding: 16px;"> |
| <div style="font-size: 14px; font-weight: 600; margin-bottom: 12px; color: var(--text);">Recent Files</div> |
| <div style="display: flex; flex-direction: column; gap: 8px;"> |
| ${recentFiles.map(file => ` |
| <div style="display: flex; align-items: center; gap: 12px; padding: 12px; background: var(--border-light); border-radius: 8px;"> |
| <svg width="20" height="20" fill="none" stroke="var(--primary)" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/> |
| </svg> |
| <div style="flex: 1; min-width: 0;"> |
| <div style="font-size: 13px; font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${file.name}</div> |
| <div style="font-size: 11px; color: var(--text-muted);">${file.date}</div> |
| </div> |
| </div> |
| `).join('')} |
| </div> |
| <button class="btn btn-secondary" style="width: 100%; margin-top: 12px;" onclick="clearRecentFiles()">Clear History</button> |
| </div> |
| `; |
| } |
| |
| showModal('Recent Files', content); |
| } |
|
|
| function addToRecentFiles(name, type) { |
| const recentFiles = JSON.parse(localStorage.getItem('recentFiles') || '[]'); |
| const newFile = { |
| name: name, |
| type: type, |
| date: new Date().toLocaleDateString() |
| }; |
| |
| |
| const filtered = recentFiles.filter(f => f.name !== name); |
| filtered.unshift(newFile); |
| |
| |
| localStorage.setItem('recentFiles', JSON.stringify(filtered.slice(0, 10))); |
| } |
|
|
| function clearRecentFiles() { |
| localStorage.removeItem('recentFiles'); |
| closeModal(); |
| showToast('Cleared', 'Recent files history cleared', 'success', 2000); |
| } |
|
|
| |
| |
| |
| function showKeyboardShortcutsModal() { |
| const shortcuts = [ |
| { key: '1', desc: 'PDF Editor' }, |
| { key: '2', desc: 'Watermark Remover' }, |
| { key: '3', desc: 'Images to PDF' }, |
| { key: '4', desc: 'Merge PDFs' }, |
| { key: '5', desc: 'Split PDF' }, |
| { key: '6', desc: 'Image Scraper' }, |
| { key: 'Ctrl + K', desc: 'Toggle theme' }, |
| { key: '?', desc: 'Show this help' }, |
| ]; |
| |
| const content = ` |
| <div style="padding: 16px;"> |
| <div style="display: flex; flex-direction: column; gap: 8px;"> |
| ${shortcuts.map(s => ` |
| <div style="display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; background: var(--border-light); border-radius: 8px;"> |
| <span style="font-size: 13px; color: var(--text-secondary);">${s.desc}</span> |
| <kbd class="kbd" style="margin-left: 0;">${s.key}</kbd> |
| </div> |
| `).join('')} |
| </div> |
| </div> |
| `; |
| |
| showModal('Keyboard Shortcuts', content); |
| } |
|
|
| |
| |
| |
| function showModal(title, content) { |
| |
| closeModal(); |
| |
| const modal = document.createElement('div'); |
| modal.className = 'modal-overlay active'; |
| modal.id = 'genericModal'; |
| modal.innerHTML = ` |
| <div class="modal-content" style="max-width: 400px; width: 100%;"> |
| <button class="modal-close" onclick="closeModal()"> |
| <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> |
| </svg> |
| </button> |
| <div style="padding: 20px 24px; border-bottom: 1px solid var(--border);"> |
| <h3 style="font-size: 16px; font-weight: 600; color: var(--text);">${title}</h3> |
| </div> |
| ${content} |
| </div> |
| `; |
| |
| modal.addEventListener('click', (e) => { |
| if (e.target === modal) closeModal(); |
| }); |
| |
| document.body.appendChild(modal); |
| } |
|
|
| function closeModal() { |
| const modal = document.getElementById('genericModal'); |
| if (modal) modal.remove(); |
| } |
|
|
| |
| document.addEventListener('keydown', (e) => { |
| if (e.key === 'Escape') closeModal(); |
| }); |
|
|