// public/script.js document.addEventListener('DOMContentLoaded', () => { // --- Password Strength Checker for Registration --- const passwordInput = document.getElementById('password'); const passwordStrengthDiv = document.getElementById('password-strength'); const passwordCriteriaUl = passwordStrengthDiv ? passwordStrengthDiv.querySelector('ul') : null; if (passwordInput && passwordStrengthDiv && passwordCriteriaUl) { const criteria = [ { regex: /.{8,}/, message: "At least 8 characters", id: "len" }, { regex: /[A-Z]/, message: "An uppercase letter", id: "upper" }, { regex: /[a-z]/, message: "A lowercase letter", id: "lower" }, { regex: /[0-9]/, message: "A number", id: "num" }, { regex: /[^A-Za-z0-9]/, message: "A special character", id: "special" } ]; // Populate criteria list criteria.forEach(c => { const li = document.createElement('li'); li.id = `crit-${c.id}`; li.textContent = `✗ ${c.message}`; li.classList.add('invalid'); passwordCriteriaUl.appendChild(li); }); passwordInput.addEventListener('input', () => { const password = passwordInput.value; let strengthScore = 0; criteria.forEach(c => { const li = document.getElementById(`crit-${c.id}`); if (c.regex.test(password)) { li.textContent = `✓ ${c.message}`; li.classList.remove('invalid'); li.classList.add('valid'); strengthScore++; } else { li.textContent = `✗ ${c.message}`; li.classList.remove('valid'); li.classList.add('invalid'); } }); const strengthBar = document.querySelector('.password-strength-meter div'); if (strengthBar) { strengthBar.style.width = (strengthScore / criteria.length * 100) + '%'; if (strengthScore < 2) strengthBar.style.backgroundColor = 'red'; else if (strengthScore < 4) strengthBar.style.backgroundColor = 'orange'; else strengthBar.style.backgroundColor = 'green'; } if (password.length === 0) { if (strengthBar) strengthBar.style.width = '0%'; } }); // Trigger initial check if there's a value (e.g. browser autofill) if(passwordInput.value) passwordInput.dispatchEvent(new Event('input')); } // --- AJAX Form Submissions --- handleAjaxForm('login-form'); handleAjaxForm('register-form'); handleAjaxForm('note-form'); handleAjaxForm('logout-form'); // Added for logout // --- Delete Note Confirmation and AJAX --- document.querySelectorAll('.delete-note-btn').forEach(button => { button.addEventListener('click', function(e) { e.preventDefault(); if (confirm('Are you sure you want to delete this note?')) { const noteId = this.dataset.noteId; // Create a temporary form to submit for delete const tempForm = document.createElement('form'); tempForm.method = 'POST'; tempForm.style.display = 'none'; const actionInput = document.createElement('input'); actionInput.type = 'hidden'; actionInput.name = 'action'; actionInput.value = 'delete_note'; tempForm.appendChild(actionInput); const noteIdInput = document.createElement('input'); noteIdInput.type = 'hidden'; noteIdInput.name = 'note_id'; noteIdInput.value = noteId; tempForm.appendChild(noteIdInput); document.body.appendChild(tempForm); // Form must be in DOM to submit // Use handleAjaxForm for consistency const formData = new FormData(tempForm); fetch('index.php', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { displayMessage(data.message, data.success ? 'success' : 'danger'); if (data.success) { // Refresh page or remove row dynamically for better UX // For simplicity, reload. A more advanced way is to find and remove table row. setTimeout(() => { window.location.reload(); }, 1000); } }) .catch(error => { console.error('Error:', error); displayMessage('An error occurred during deletion.', 'danger'); }) .finally(() => { document.body.removeChild(tempForm); // Clean up }); } }); }); // --- Drag and Drop File Upload --- // ... (no changes from previous, ensure it's present) const dropZone = document.getElementById('drop-zone'); const noteTitleInput = document.getElementById('title'); const noteContentInput = document.getElementById('content'); const markdownPreview = document.getElementById('markdown-preview'); if (dropZone && noteTitleInput && noteContentInput) { dropZone.addEventListener('dragover', (event) => { event.preventDefault(); dropZone.classList.add('drag-over'); }); dropZone.addEventListener('dragleave', () => { dropZone.classList.remove('drag-over'); }); dropZone.addEventListener('drop', (event) => { event.preventDefault(); dropZone.classList.remove('drag-over'); const files = event.dataTransfer.files; if (files.length > 0) { const file = files[0]; if (file.type === 'text/plain' || file.type === 'text/markdown' || file.name.endsWith('.md') || file.name.endsWith('.txt')) { const reader = new FileReader(); reader.onload = (e) => { noteTitleInput.value = file.name.replace(/\.(md|txt)$/i, ''); noteContentInput.value = e.target.result; if (window.updateMarkdownPreview) updateMarkdownPreview(); }; reader.readAsText(file); } else { displayMessage('Please drop a .txt or .markdown file.', 'info'); } } }); } if (noteContentInput && markdownPreview && typeof showdown !== 'undefined') { const converter = new showdown.Converter({sanitize: true}); window.updateMarkdownPreview = function() { markdownPreview.innerHTML = converter.makeHtml(noteContentInput.value); } noteContentInput.addEventListener('input', window.updateMarkdownPreview); if(noteContentInput.value) window.updateMarkdownPreview(); } else if (noteContentInput && markdownPreview) { noteContentInput.addEventListener('input', () => { markdownPreview.textContent = "Preview updates on save (or include Showdown.js library for live preview)."; }); if(noteContentInput.value) markdownPreview.textContent = "Preview updates on save (or include Showdown.js library for live preview)."; } // --- Table Sorting --- document.querySelectorAll('.notes-table th[data-sort]').forEach(headerCell => { headerCell.addEventListener('click', () => { const sortBy = headerCell.dataset.sort; const currentUrl = new URL(window.location.href); const currentSortBy = currentUrl.searchParams.get('sort_by'); const currentSortOrder = currentUrl.searchParams.get('sort_order') || 'asc'; // Default to asc if not set let newSortOrder = 'asc'; if (sortBy === currentSortBy && currentSortOrder.toLowerCase() === 'asc') { newSortOrder = 'desc'; } // If different column, or current is desc, start with asc for the new column currentUrl.searchParams.set('sort_by', sortBy); currentUrl.searchParams.set('sort_order', newSortOrder); window.location.href = currentUrl.toString(); }); }); }); // End DOMContentLoaded function handleAjaxForm(formId) { // Removed successRedirectPage, rely on JSON const form = document.getElementById(formId); if (form) { form.addEventListener('submit', function(e) { e.preventDefault(); const formData = new FormData(form); const action = formData.get('action'); // Get action from FormData if (action === 'register') { const password = formData.get('password'); const confirmPassword = formData.get('confirm_password'); if (password !== confirmPassword) { displayMessage('Passwords do not match.', 'danger'); return; } let criteriaMet = 0; const criteriaElements = document.querySelectorAll('#password-strength ul li'); criteriaElements.forEach(li => { if (li.classList.contains('valid')) { criteriaMet++; } }); if (criteriaMet < criteriaElements.length) { // Check if all criteria are met displayMessage('Password does not meet all requirements.', 'danger'); return; } } fetch('index.php', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.message) { // Display message if provided displayMessage(data.message, data.success ? 'success' : 'danger'); } if (data.success && data.redirect) { setTimeout(() => { window.location.href = data.redirect; }, data.message ? 1000 : 0); // Delay if message shown } else if (data.success && (action === 'create_note' || action === 'update_note')) { // Specific redirect for note actions if not overridden by data.redirect setTimeout(() => { window.location.href = 'index.php?page=dashboard'; }, data.message ? 1000 : 0); } }) .catch(error => { console.error('Error:', error); displayMessage('A network error occurred. Please try again.', 'danger'); }); }); } } function displayMessage(message, type = 'info') { const existingAlert = document.querySelector('#alert-container .alert'); if (existingAlert) { existingAlert.remove(); } const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type}`; alertDiv.innerHTML = message; // Use innerHTML if message contains HTML (e.g. list from server) let alertContainer = document.getElementById('alert-container'); if (!alertContainer) { alertContainer = document.createElement('div'); alertContainer.id = 'alert-container'; alertContainer.style.position = 'fixed'; alertContainer.style.top = '70px'; alertContainer.style.left = '50%'; alertContainer.style.transform = 'translateX(-50%)'; alertContainer.style.zIndex = '1000'; alertContainer.style.width = 'auto'; // Fit content alertContainer.style.minWidth = '300px'; alertContainer.style.maxWidth = '90%'; document.body.prepend(alertContainer); } alertContainer.appendChild(alertDiv); setTimeout(() => { alertDiv.style.opacity = '0'; alertDiv.style.transition = 'opacity 0.5s ease-out'; setTimeout(() => { if (alertDiv.parentNode) alertDiv.remove(); }, 500); }, 5000); }