218 lines
9.6 KiB
JavaScript
218 lines
9.6 KiB
JavaScript
// 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'));
|
|
}
|
|
|
|
// --- 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 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);
|
|
} |