// Main JavaScript for TikDown - Professional Version document.addEventListener('DOMContentLoaded', function() { // Initialize Bootstrap components initializeBootstrapComponents(); // Initialize theme initializeTheme(); // Initialize URL validation initializeURLValidation(); // Initialize password strength initializePasswordStrength(); // Lazy load images lazyLoadImages(); // Initialize tooltips initializeTooltips(); }); // Bootstrap Components Initialization function initializeBootstrapComponents() { // Tooltips const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.forEach(tooltipTriggerEl => { new bootstrap.Tooltip(tooltipTriggerEl); }); // Popovers const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); popoverTriggerList.forEach(popoverTriggerEl => { new bootstrap.Popover(popoverTriggerEl); }); // Auto-hide alerts setTimeout(() => { const alerts = document.querySelectorAll('.alert:not(.alert-permanent)'); alerts.forEach(alert => { const bsAlert = new bootstrap.Alert(alert); bsAlert.close(); }); }, 5000); } // Theme Initialization function initializeTheme() { const themeToggle = document.getElementById('themeToggle'); const currentTheme = localStorage.getItem('theme') || 'light'; if (currentTheme === 'dark') { document.body.classList.add('dark-theme'); if (themeToggle) themeToggle.checked = true; } if (themeToggle) { themeToggle.addEventListener('change', function() { if (this.checked) { document.body.classList.add('dark-theme'); localStorage.setItem('theme', 'dark'); } else { document.body.classList.remove('dark-theme'); localStorage.setItem('theme', 'light'); } }); } } // URL Validation function initializeURLValidation() { const urlInput = document.getElementById('tiktokUrl'); if (urlInput) { urlInput.addEventListener('input', function() { const url = this.value.trim(); const isValid = validateTikTokUrl(url); if (url && !isValid) { this.classList.add('is-invalid'); this.classList.remove('is-valid'); } else if (url && isValid) { this.classList.remove('is-invalid'); this.classList.add('is-valid'); } else { this.classList.remove('is-invalid', 'is-valid'); } }); } } // Password Strength Indicator function initializePasswordStrength() { const passwordInput = document.getElementById('password'); if (passwordInput) { passwordInput.addEventListener('input', updatePasswordStrength); } } // Lazy Load Images function lazyLoadImages() { const images = document.querySelectorAll('img[data-src]'); const imageObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.removeAttribute('data-src'); observer.unobserve(img); } }); }); images.forEach(img => imageObserver.observe(img)); } // Tooltips Initialization function initializeTooltips() { // Initialize any additional tooltips here } // Validate TikTok URL function validateTikTokUrl(url) { if (!url) return false; const patterns = [ /^https?:\/\/(vm|vt|www)\.tiktok\.com\/.+/, /^https?:\/\/(www\.)?tiktok\.com\/@.+\/video\/\d+/, /tiktok\.com\/.+\/video\/\d+/, /^https?:\/\/tiktok\.com\/t\/[a-zA-Z0-9]+/, /^https?:\/\/(vm|vt)\.tiktok\.com\/[a-zA-Z0-9]+/ ]; return patterns.some(pattern => pattern.test(url)); } // Update Password Strength function updatePasswordStrength() { const password = this.value; const strength = calculatePasswordStrength(password); const strengthBar = document.getElementById('password-strength-bar'); const strengthText = document.getElementById('password-strength-text'); if (strengthBar && strengthText) { strengthBar.style.width = strength.percentage + '%'; strengthBar.className = 'progress-bar ' + strength.class; strengthText.textContent = strength.text; strengthText.className = 'text-' + strength.color; } } // Calculate Password Strength function calculatePasswordStrength(password) { let score = 0; const requirements = []; if (password.length >= 8) { score++; requirements.push('length'); } if (/[A-Z]/.test(password)) { score++; requirements.push('uppercase'); } if (/[0-9]/.test(password)) { score++; requirements.push('number'); } if (/[^A-Za-z0-9]/.test(password)) { score++; requirements.push('special'); } const levels = [ { class: 'bg-danger', color: 'danger', text: 'Rất yếu', percentage: 20 }, { class: 'bg-danger', color: 'danger', text: 'Yếu', percentage: 40 }, { class: 'bg-warning', color: 'warning', text: 'Trung bình', percentage: 60 }, { class: 'bg-info', color: 'info', text: 'Mạnh', percentage: 80 }, { class: 'bg-success', color: 'success', text: 'Rất mạnh', percentage: 100 } ]; return levels[Math.min(score, 4)]; } // Copy to Clipboard function copyToClipboard(text, element = null) { navigator.clipboard.writeText(text).then(() => { showNotification('Đã sao chép vào clipboard', 'success'); if (element) { const originalText = element.innerHTML; element.innerHTML = ' Đã sao chép'; setTimeout(() => { element.innerHTML = originalText; }, 2000); } }).catch(err => { showNotification('Không thể sao chép: ' + (err.message || err), 'error'); }); } // Show Notification function showNotification(message, type = 'info') { const alertTypes = { 'success': 'alert-success', 'error': 'alert-danger', 'warning': 'alert-warning', 'info': 'alert-info' }; const alertClass = alertTypes[type] || 'alert-info'; // Create alert element const alert = document.createElement('div'); alert.className = `alert ${alertClass} alert-dismissible fade show position-fixed`; alert.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;'; alert.innerHTML = `