const shortcuts = { MAX_SHORTCUTS: 12, validateAndFormatUrl: (url) => { if (!/^https?:\/\//i.test(url)) { url = 'https://' + url; } try { new URL(url); return url; } catch (e) { return false; } }, add: (url, name, isPasswordProtected = false) => { const currentShortcuts = Storage.get('shortcuts') || []; if (currentShortcuts.length >= shortcuts.MAX_SHORTCUTS) { notifications.show('Maximum shortcuts limit reached!', 'error'); return; } const formattedUrl = shortcuts.validateAndFormatUrl(url); if (!formattedUrl) { notifications.show('Invalid URL format!', 'error'); return; } currentShortcuts.push({ url: formattedUrl, name, isPasswordProtected: isPasswordProtected || false }); Storage.set('shortcuts', currentShortcuts); shortcuts.render(); CacheUpdater.update(); }, remove: (index) => { const currentShortcuts = Storage.get('shortcuts') || []; currentShortcuts.splice(index, 1); Storage.set('shortcuts', currentShortcuts); shortcuts.render(); notifications.show('Shortcut removed!', 'success'); CacheUpdater.update(); }, showConfirmDialog: (title, message, onConfirm) => { const dialog = document.getElementById('confirmation-dialog'); const titleEl = document.getElementById('confirmation-title'); const messageEl = document.getElementById('confirmation-message'); const confirmBtn = document.getElementById('confirm-action'); const cancelBtn = document.getElementById('cancel-action'); titleEl.textContent = title; messageEl.textContent = message; dialog.classList.remove('hidden'); setTimeout(() => dialog.classList.add('active'), 10); const closeDialog = () => { dialog.classList.remove('active'); setTimeout(() => dialog.classList.add('hidden'), 300); }; const handleConfirm = () => { onConfirm(); closeDialog(); confirmBtn.removeEventListener('click', handleConfirm); cancelBtn.removeEventListener('click', handleCancel); }; const handleCancel = () => { closeDialog(); confirmBtn.removeEventListener('click', handleConfirm); cancelBtn.removeEventListener('click', handleCancel); }; confirmBtn.addEventListener('click', handleConfirm); cancelBtn.addEventListener('click', handleCancel); }, showPasswordDialog: (shortcut, callback) => { const dialog = document.getElementById('password-dialog'); const passwordInput = document.getElementById('shortcut-password'); const submitBtn = document.getElementById('submit-password'); const cancelBtn = document.getElementById('cancel-password'); const closeBtn = document.getElementById('close-password-dialog'); const errorMsg = document.getElementById('password-error'); const contextMenu = document.getElementById('context-menu'); if (contextMenu) { contextMenu.classList.add('hidden'); } if (errorMsg) { errorMsg.classList.add('hidden'); } if (passwordInput) { passwordInput.value = ''; } if (dialog) { dialog.classList.remove('hidden'); setTimeout(() => { dialog.classList.add('active'); if (passwordInput) { passwordInput.focus(); } }, 10); } const closeDialog = () => { dialog.classList.remove('active'); setTimeout(() => dialog.classList.add('hidden'), 300); }; const handleSubmit = () => { const password = passwordInput.value; const masterPassword = Storage.get('masterPassword'); if (!masterPassword) { errorMsg.textContent = "No master password set. Please set one in settings."; errorMsg.classList.remove('hidden'); return; } if (password === masterPassword) { closeDialog(); callback(); submitBtn.removeEventListener('click', handleSubmit); cancelBtn.removeEventListener('click', handleCancel); closeBtn.removeEventListener('click', handleCancel); passwordInput.removeEventListener('keydown', handleKeydown); } else { errorMsg.textContent = "Incorrect password. Please try again."; errorMsg.classList.remove('hidden'); passwordInput.value = ''; passwordInput.focus(); } }; const handleCancel = () => { closeDialog(); submitBtn.removeEventListener('click', handleSubmit); cancelBtn.removeEventListener('click', handleCancel); closeBtn.removeEventListener('click', handleCancel); passwordInput.removeEventListener('keydown', handleKeydown); }; const handleKeydown = (e) => { if (e.key === 'Enter') { handleSubmit(); } else if (e.key === 'Escape') { handleCancel(); } }; submitBtn.addEventListener('click', handleSubmit); cancelBtn.addEventListener('click', handleCancel); closeBtn.addEventListener('click', handleCancel); passwordInput.addEventListener('keydown', handleKeydown); }, edit: (index, newUrl, newName, isPasswordProtected) => { const currentShortcuts = Storage.get('shortcuts') || []; currentShortcuts[index] = { url: newUrl, name: newName, isPasswordProtected: isPasswordProtected || false }; Storage.set('shortcuts', currentShortcuts); shortcuts.render(); notifications.show('Shortcut updated!', 'success'); CacheUpdater.update(); }, showContextMenu: (e, index) => { e.preventDefault(); const menu = document.getElementById('context-menu'); const rect = e.target.getBoundingClientRect(); menu.style.top = `${e.clientY}px`; menu.style.left = `${e.clientX}px`; menu.classList.remove('hidden'); menu.dataset.shortcutIndex = index; const handleClickOutside = (event) => { if (!menu.contains(event.target)) { menu.classList.add('hidden'); document.removeEventListener('click', handleClickOutside); } }; setTimeout(() => { document.addEventListener('click', handleClickOutside); }, 0); }, render: () => { const grid = document.getElementById('shortcuts-grid'); const currentShortcuts = Storage.get('shortcuts') || []; const isAnonymous = Storage.get('anonymousMode') || false; grid.innerHTML = ''; currentShortcuts.forEach((shortcut, index) => { const element = document.createElement('div'); element.className = `shortcut ${isAnonymous ? 'blurred' : ''} ${shortcut.isPasswordProtected ? 'password-protected' : ''}`; element.dataset.index = index; const icon = document.createElement('img'); icon.src = `https://www.google.com/s2/favicons?domain=${shortcut.url}&sz=64`; icon.alt = shortcut.name; icon.draggable = false; const name = document.createElement('span'); name.textContent = shortcut.name; element.appendChild(icon); element.appendChild(name); element.addEventListener('click', (e) => { if (!grid.classList.contains('grid-draggable') || !e.target.closest('.shortcut').classList.contains('drag-active')) { if (shortcut.isPasswordProtected) { e.preventDefault(); e.stopPropagation(); const openShortcut = () => { if (e.ctrlKey || e.which === 2 || e.button === 1) { window.open(shortcut.url, '_blank'); } else { window.location.href = shortcut.url; } }; shortcuts.showPasswordDialog(shortcut, openShortcut); return false; } else { if (e.ctrlKey || e.which === 2 || e.button === 1) { window.open(shortcut.url, '_blank'); } else { window.location.href = shortcut.url; } } } }); element.addEventListener('mousedown', (e) => { if (e.button === 1) { e.preventDefault(); if (shortcut.isPasswordProtected) { const openShortcut = () => { window.open(shortcut.url, '_blank'); }; shortcuts.showPasswordDialog(shortcut, openShortcut); } else { window.open(shortcut.url, '_blank'); } } }); element.addEventListener('contextmenu', (e) => { e.preventDefault(); const menu = document.getElementById('context-menu'); menu.style.top = `${e.pageY}px`; menu.style.left = `${e.pageX}px`; menu.classList.remove('hidden'); menu.dataset.shortcutIndex = index; const closeMenu = (event) => { if (!menu.contains(event.target)) { menu.classList.add('hidden'); document.removeEventListener('click', closeMenu); } }; setTimeout(() => { document.addEventListener('click', closeMenu); }, 0); }); grid.appendChild(element); }); }, init: () => { const masterPasswordInput = document.getElementById('master-password'); if (masterPasswordInput) { const savedPassword = Storage.get('masterPassword'); if (savedPassword) { masterPasswordInput.value = savedPassword; } } const addShortcutButton = document.getElementById('add-shortcut'); const modal = document.getElementById('add-shortcut-modal'); const closeBtn = modal.querySelector('.close-modal'); if (closeBtn) { closeBtn.addEventListener('click', () => { modal.classList.remove('active'); setTimeout(() => { modal.classList.add('hidden'); document.getElementById('shortcut-url').value = ''; document.getElementById('shortcut-name').value = ''; }, 300); }); } if (addShortcutButton) { addShortcutButton.addEventListener('click', (e) => { e.stopPropagation(); const currentShortcuts = Storage.get('shortcuts') || []; if (currentShortcuts.length >= shortcuts.MAX_SHORTCUTS) { notifications.show('Maximum shortcuts limit reached!', 'error'); return; } if (modal) { modal.classList.remove('hidden'); modal.classList.add('active'); const urlInput = document.getElementById('shortcut-url'); const nameInput = document.getElementById('shortcut-name'); const saveShortcutButton = document.getElementById('save-shortcut'); if (saveShortcutButton) { saveShortcutButton.onclick = () => { const url = urlInput.value.trim(); const name = nameInput.value.trim(); if (url && name) { try { new URL(url); const isPasswordProtectionEnabled = Storage.get('passwordProtectionEnabled') || false; const passwordProtectCheckbox = document.getElementById('protect-shortcut'); const isPasswordProtected = isPasswordProtectionEnabled && passwordProtectCheckbox && passwordProtectCheckbox.checked; shortcuts.add(url, name, isPasswordProtected); modal.classList.remove('active'); setTimeout(() => { modal.classList.add('hidden'); urlInput.value = ''; nameInput.value = ''; if (passwordProtectCheckbox) passwordProtectCheckbox.checked = false; }, 300); notifications.show('Shortcut added successfully!', 'success'); } catch (e) { notifications.show('Invalid URL format!', 'error'); } } }; } const cancelShortcutButton = document.getElementById('cancel-shortcut'); if (cancelShortcutButton) { cancelShortcutButton.onclick = () => { modal.classList.remove('active'); setTimeout(() => { modal.classList.add('hidden'); urlInput.value = ''; nameInput.value = ''; const passwordProtectCheckbox = document.getElementById('protect-shortcut'); if (passwordProtectCheckbox) passwordProtectCheckbox.checked = false; }, 300); }; } } }); } const anonymousTogglePrivacy = document.getElementById('toggle-anonymous-privacy'); if (anonymousTogglePrivacy) { anonymousTogglePrivacy.checked = Storage.get('anonymousMode') || false; anonymousTogglePrivacy.addEventListener('change', () => { const anonymousToggle = document.getElementById('toggle-anonymous'); if (anonymousToggle) { anonymousToggle.checked = anonymousTogglePrivacy.checked; anonymousToggle.dispatchEvent(new Event('change')); } else { shortcuts.toggleAnonymousMode(); } }); } const shortcutsPasswordToggle = document.getElementById('toggle-shortcuts-password'); if (shortcutsPasswordToggle) { const isEnabled = Storage.get('passwordProtectionEnabled') || false; shortcutsPasswordToggle.checked = isEnabled; const passwordSettings = document.getElementById('password-protection-settings'); if (passwordSettings) { if (isEnabled) { passwordSettings.classList.remove('hidden'); } else { passwordSettings.classList.add('hidden'); } } shortcutsPasswordToggle.addEventListener('change', () => { const isEnabled = shortcutsPasswordToggle.checked; Storage.set('passwordProtectionEnabled', isEnabled); if (passwordSettings) { if (isEnabled) { passwordSettings.classList.remove('hidden'); const masterPasswordInput = document.getElementById('master-password'); if (masterPasswordInput) { setTimeout(() => { masterPasswordInput.focus(); }, 10); } } else { passwordSettings.classList.add('hidden'); } } shortcuts.updateAddShortcutModal(); if (isEnabled) { const masterPassword = Storage.get('masterPassword'); if (!masterPassword) { const masterPasswordInput = document.getElementById('master-password'); if (masterPasswordInput) { masterPasswordInput.focus(); notifications.show('Please set a master password!', 'warning'); } } else { notifications.show('Password protection enabled!', 'success'); shortcuts.createShortcutProtectionManager(); } } else { const currentShortcuts = Storage.get('shortcuts') || []; currentShortcuts.forEach(shortcut => { shortcut.isPasswordProtected = false; }); Storage.set('shortcuts', currentShortcuts); shortcuts.render(); notifications.show('Password protection disabled!', 'info'); } }); } if (Storage.get('passwordProtectionEnabled')) { shortcuts.createShortcutProtectionManager(); } const saveMasterPasswordBtn = document.getElementById('save-master-password'); if (saveMasterPasswordBtn) { saveMasterPasswordBtn.addEventListener('click', () => { const masterPasswordInput = document.getElementById('master-password'); if (masterPasswordInput) { const password = masterPasswordInput.value.trim(); if (password) { Storage.set('masterPassword', password); notifications.show('Master password updated!', 'success'); const shortcutsPasswordToggle = document.getElementById('toggle-shortcuts-password'); if (shortcutsPasswordToggle && !shortcutsPasswordToggle.checked) { shortcutsPasswordToggle.checked = true; Storage.set('passwordProtectionEnabled', true); const passwordSettings = document.getElementById('password-protection-settings'); if (passwordSettings) { passwordSettings.classList.remove('hidden'); } shortcuts.updateAddShortcutModal(); shortcuts.createShortcutProtectionManager(); } else { shortcuts.createShortcutProtectionManager(); } } else { notifications.show('Please enter a valid password!', 'error'); } } }); } shortcuts.updateAddShortcutModal(); const contextMenu = document.getElementById('context-menu'); if (contextMenu) { contextMenu.addEventListener('click', (e) => { const action = e.target.closest('.context-menu-item')?.dataset.action; const index = parseInt(contextMenu.dataset.shortcutIndex); if (action === 'edit') { const currentShortcuts = Storage.get('shortcuts') || []; const shortcut = currentShortcuts[index]; const modal = document.getElementById('edit-shortcut-modal'); if (modal) { const urlInput = document.getElementById('edit-shortcut-url'); const nameInput = document.getElementById('edit-shortcut-name'); urlInput.value = shortcut.url; nameInput.value = shortcut.name; const protectCheckbox = document.getElementById('protect-shortcut-edit'); if (protectCheckbox) { protectCheckbox.checked = shortcut.isPasswordProtected || false; } modal.classList.remove('hidden'); modal.classList.add('active'); const saveButton = document.getElementById('save-edit-shortcut'); const closeButton = document.getElementById('close-edit-shortcut'); const cancelButton = document.getElementById('cancel-edit-shortcut'); const closeModal = () => { modal.classList.remove('active'); setTimeout(() => { modal.classList.add('hidden'); }, 300); }; const handleSave = () => { const newUrl = urlInput.value.trim(); const newName = nameInput.value.trim(); if (newUrl && newName) { const formattedUrl = shortcuts.validateAndFormatUrl(newUrl); if (formattedUrl) { const isPasswordProtectionEnabled = Storage.get('passwordProtectionEnabled') || false; const isPasswordProtected = isPasswordProtectionEnabled && protectCheckbox && protectCheckbox.checked; shortcuts.edit(index, formattedUrl, newName, isPasswordProtected); closeModal(); shortcuts.createShortcutProtectionManager(); } else { notifications.show('Invalid URL format!', 'error'); } } }; saveButton.onclick = handleSave; closeButton.onclick = closeModal; cancelButton.onclick = closeModal; } } else if (action === 'delete') { const currentShortcuts = Storage.get('shortcuts') || []; const shortcut = currentShortcuts[index]; shortcuts.showConfirmDialog( 'Delete Shortcut', `Are you sure you want to delete "${shortcut.name}"?`, () => { shortcuts.remove(index); shortcuts.createShortcutProtectionManager(); } ); } else if (action === 'open-new-tab') { const currentShortcuts = Storage.get('shortcuts') || []; const shortcut = currentShortcuts[index]; if (shortcut && shortcut.url) { if (shortcut.isPasswordProtected) { shortcuts.showPasswordDialog(shortcut, () => { window.open(shortcut.url, '_blank'); }); } else { window.open(shortcut.url, '_blank'); } } } contextMenu.classList.add('hidden'); }); } shortcuts.render(); }, createShortcutProtectionManager: () => { const passwordSettings = document.getElementById('password-protection-settings'); if (!passwordSettings) return; let protectionManager = document.getElementById('shortcut-protection-manager'); if (!protectionManager) { protectionManager = document.createElement('div'); protectionManager.id = 'shortcut-protection-manager'; protectionManager.className = 'shortcut-protection-manager'; const managerTitle = document.createElement('h4'); managerTitle.textContent = 'Protect Specific Shortcuts'; const managerDescription = document.createElement('p'); managerDescription.className = 'setting-description'; managerDescription.textContent = 'Select which shortcuts to password protect:'; protectionManager.appendChild(managerTitle); protectionManager.appendChild(managerDescription); passwordSettings.appendChild(protectionManager); } else { const children = Array.from(protectionManager.children); children.forEach((child, index) => { if (index > 1) protectionManager.removeChild(child); }); } const currentShortcuts = Storage.get('shortcuts') || []; const selectedShortcutsContainer = document.createElement('div'); selectedShortcutsContainer.className = 'selected-shortcuts-container'; const protectedShortcuts = currentShortcuts.filter(shortcut => shortcut.isPasswordProtected); if (protectedShortcuts.length > 0) { protectedShortcuts.forEach((shortcut, index) => { const shortcutChip = document.createElement('div'); shortcutChip.className = 'shortcut-chip'; shortcutChip.dataset.index = currentShortcuts.indexOf(shortcut); const shortcutIcon = document.createElement('img'); shortcutIcon.src = `https://www.google.com/s2/favicons?domain=${shortcut.url}&sz=64`; shortcutIcon.alt = shortcut.name; const shortcutName = document.createElement('span'); shortcutName.textContent = shortcut.name; const removeButton = document.createElement('button'); removeButton.className = 'remove-chip-btn'; removeButton.innerHTML = '×'; removeButton.title = 'Remove protection'; shortcutChip.appendChild(shortcutIcon); shortcutChip.appendChild(shortcutName); shortcutChip.appendChild(removeButton); removeButton.addEventListener('click', (e) => { e.stopPropagation(); currentShortcuts[shortcutChip.dataset.index].isPasswordProtected = false; Storage.set('shortcuts', currentShortcuts); shortcuts.render(); shortcuts.createShortcutProtectionManager(); notifications.show(`Removed protection from: ${shortcut.name}`, 'info'); }); selectedShortcutsContainer.appendChild(shortcutChip); }); } else if (currentShortcuts.length > 0) { const emptyState = document.createElement('p'); emptyState.className = 'empty-protection-state'; emptyState.textContent = 'No protected shortcuts yet.'; selectedShortcutsContainer.appendChild(emptyState); } protectionManager.appendChild(selectedShortcutsContainer); const selectorContainer = document.createElement('div'); selectorContainer.className = 'shortcut-selector-container'; const unprotectedShortcuts = currentShortcuts.filter(shortcut => !shortcut.isPasswordProtected); if (unprotectedShortcuts.length > 0) { const dropdown = document.createElement('div'); dropdown.className = 'shortcut-dropdown'; const selected = document.createElement('div'); selected.className = 'shortcut-dropdown-selected'; selected.textContent = 'Select a shortcut to protect...'; const dropdownItems = document.createElement('div'); dropdownItems.className = 'shortcut-dropdown-items'; dropdownItems.classList.add('hidden'); unprotectedShortcuts.forEach(shortcut => { const item = document.createElement('div'); item.className = 'shortcut-dropdown-item'; item.dataset.index = currentShortcuts.indexOf(shortcut); const icon = document.createElement('img'); icon.src = `https://www.google.com/s2/favicons?domain=${shortcut.url}&sz=64`; icon.alt = shortcut.name; icon.style.width = '16px'; icon.style.height = '16px'; const name = document.createElement('span'); name.textContent = shortcut.name; item.appendChild(icon); item.appendChild(name); item.addEventListener('click', () => { currentShortcuts[item.dataset.index].isPasswordProtected = true; Storage.set('shortcuts', currentShortcuts); shortcuts.render(); shortcuts.createShortcutProtectionManager(); dropdownItems.classList.remove('active'); selected.classList.remove('active'); dropdownItems.classList.add('hidden'); notifications.show(`Protected shortcut: ${shortcut.name}`, 'success'); }); dropdownItems.appendChild(item); }); selected.addEventListener('click', (e) => { e.stopPropagation(); dropdownItems.classList.toggle('hidden'); dropdownItems.classList.toggle('active'); selected.classList.toggle('active'); }); document.addEventListener('click', (e) => { if (!dropdown.contains(e.target)) { dropdownItems.classList.add('hidden'); dropdownItems.classList.remove('active'); selected.classList.remove('active'); } }); dropdown.appendChild(selected); dropdown.appendChild(dropdownItems); selectorContainer.appendChild(dropdown); } else if (currentShortcuts.length === 0) { const noShortcutsMessage = document.createElement('p'); noShortcutsMessage.className = 'no-shortcuts-message'; noShortcutsMessage.textContent = 'Add shortcuts to protect them with a password.'; selectorContainer.appendChild(noShortcutsMessage); } else { const allProtectedMessage = document.createElement('p'); allProtectedMessage.className = 'empty-protection-state'; allProtectedMessage.textContent = 'All shortcuts are password protected.'; selectorContainer.appendChild(allProtectedMessage); } protectionManager.appendChild(selectorContainer); }, updateAddShortcutModal: () => { const modal = document.getElementById('add-shortcut-modal'); if (!modal) return; const modalContent = modal.querySelector('.modal-content'); if (!modalContent) return; const existingCheckbox = document.getElementById('protect-shortcut-container'); if (existingCheckbox) return; const isPasswordProtectionEnabled = Storage.get('passwordProtectionEnabled') || false; if (!isPasswordProtectionEnabled) return; const checkboxContainer = document.createElement('div'); checkboxContainer.id = 'protect-shortcut-container'; checkboxContainer.className = 'checkbox-container'; checkboxContainer.innerHTML = ` `; const saveButton = modal.querySelector('#save-shortcut'); if (saveButton) { modalContent.insertBefore(checkboxContainer, saveButton); } else { modalContent.appendChild(checkboxContainer); } const editModal = document.getElementById('edit-shortcut-modal'); if (editModal) { const existingEditCheckbox = document.getElementById('protect-shortcut-edit-container'); if (!existingEditCheckbox) { const editModalContent = editModal.querySelector('.modal-content'); const editCheckboxContainer = document.createElement('div'); editCheckboxContainer.id = 'protect-shortcut-edit-container'; editCheckboxContainer.className = 'checkbox-container'; editCheckboxContainer.innerHTML = ` `; const modalActions = editModal.querySelector('.modal-actions'); if (modalActions) { editModalContent.insertBefore(editCheckboxContainer, modalActions); } else { editModalContent.appendChild(editCheckboxContainer); } } } }, toggleAnonymousMode: () => { const isAnonymous = Storage.get('anonymousMode') || false; Storage.set('anonymousMode', !isAnonymous); if (!isAnonymous) { const randomName = anonymousNames.generate(); Storage.set('anonymousName', randomName); notifications.show('Anonymous mode enabled!', 'info'); } else { Storage.remove('anonymousName'); notifications.show('Anonymous mode disabled!', 'info'); } shortcuts.render(); updateGreeting(); } };