document.addEventListener('DOMContentLoaded', function() { // Initialize Bootstrap tooltips var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); // Initialize Bootstrap popovers var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); var popoverList = popoverTriggerList.map(function (popoverTriggerEl) { return new bootstrap.Popover(popoverTriggerEl); }); // Password strength checker const passwordInput = document.getElementById('password'); const passwordStrength = document.getElementById('password-strength'); if (passwordInput && passwordStrength) { passwordInput.addEventListener('input', function() { const password = passwordInput.value; let strength = 0; if (password.length >= 8) strength += 1; if (password.match(/[a-z]+/)) strength += 1; if (password.match(/[A-Z]+/)) strength += 1; if (password.match(/[0-9]+/)) strength += 1; if (password.match(/[^a-zA-Z0-9]+/)) strength += 1; switch (strength) { case 0: case 1: passwordStrength.className = 'text-danger'; passwordStrength.textContent = '매우 약함'; break; case 2: passwordStrength.className = 'text-warning'; passwordStrength.textContent = '약함'; break; case 3: passwordStrength.className = 'text-info'; passwordStrength.textContent = '보통'; break; case 4: passwordStrength.className = 'text-primary'; passwordStrength.textContent = '강함'; break; case 5: passwordStrength.className = 'text-success'; passwordStrength.textContent = '매우 강함'; break; } }); } // Region tabs functionality const regionTabs = document.querySelectorAll('.region-tab'); const regionContents = document.querySelectorAll('.region-content'); const regionPoints = document.querySelectorAll('.region-point, .region-point-empty'); if (regionTabs.length > 0 && regionContents.length > 0) { // Add active class for styling regionTabs.forEach(tab => { if (!tab.classList.contains('disabled')) { tab.addEventListener('click', () => { const region = tab.getAttribute('data-region'); // Update active tab regionTabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); // Update active point on map regionPoints.forEach(point => { point.classList.remove('active-point'); if (point.getAttribute('data-region') === region) { point.classList.add('active-point'); } }); // Show corresponding content regionContents.forEach(content => { if (content.getAttribute('data-region') === region) { content.style.display = 'block'; } else { content.style.display = 'none'; } }); }); } }); // Activate first non-disabled tab by default const firstActiveTab = Array.from(regionTabs).find(tab => !tab.classList.contains('disabled')); if (firstActiveTab) { firstActiveTab.click(); } } // Korea map region points if (regionPoints.length > 0) { regionPoints.forEach(point => { point.addEventListener('click', () => { const region = point.getAttribute('data-region'); // Find and click the corresponding tab if not disabled regionTabs.forEach(tab => { if (tab.getAttribute('data-region') === region && !tab.classList.contains('disabled')) { tab.click(); // Scroll to content const contentSection = document.querySelector('#region-contents'); if (contentSection) { contentSection.scrollIntoView({ behavior: 'smooth' }); } } }); }); }); } // Smooth scrolling for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { if (!anchor.classList.contains('dropdown-toggle')) { // Don't apply to dropdown toggles anchor.addEventListener('click', function (e) { e.preventDefault(); const targetId = this.getAttribute('href'); if (targetId === '#') return; const targetElement = document.querySelector(targetId); if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth' }); } }); } }); // Form validation const forms = document.querySelectorAll('.needs-validation'); Array.from(forms).forEach(form => { form.addEventListener('submit', event => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); } form.classList.add('was-validated'); }, false); }); // File input preview const fileInputs = document.querySelectorAll('.custom-file-input'); fileInputs.forEach(input => { input.addEventListener('change', function() { const fileName = this.files[0]?.name; const label = this.nextElementSibling; if (label && fileName) { label.textContent = fileName; } // Image preview const previewId = this.getAttribute('data-preview'); if (previewId && this.files[0]) { const preview = document.getElementById(previewId); const reader = new FileReader(); reader.onload = function(e) { preview.src = e.target.result; preview.style.display = 'block'; } reader.readAsDataURL(this.files[0]); } }); }); // Chat functionality const chatForm = document.getElementById('chat-form'); const chatMessages = document.getElementById('chat-messages'); if (chatForm && chatMessages) { chatForm.addEventListener('submit', function(e) { e.preventDefault(); const messageInput = document.getElementById('chat-message'); const message = messageInput.value.trim(); if (message) { // AJAX request to send message fetch('/crafts/includes/send_chat.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'message=' + encodeURIComponent(message) }) .then(response => response.json()) .then(data => { if (data.success) { // Clear input messageInput.value = ''; // Reload messages loadChatMessages(); } }) .catch(error => console.error('Error:', error)); } }); // Load chat messages function loadChatMessages() { fetch('/crafts/includes/get_chat.php') .then(response => response.json()) .then(data => { if (data.success) { chatMessages.innerHTML = ''; data.messages.forEach(msg => { const messageDiv = document.createElement('div'); messageDiv.className = 'chat-message ' + (msg.is_mine ? 'chat-message-mine' : ''); messageDiv.innerHTML = `
${msg.username} ${msg.time}
${msg.message}
`; chatMessages.appendChild(messageDiv); }); // Scroll to bottom chatMessages.scrollTop = chatMessages.scrollHeight; } }) .catch(error => console.error('Error:', error)); } // Initial load and periodic refresh loadChatMessages(); setInterval(loadChatMessages, 10000); } // Notifications function loadNotifications() { const notificationBadge = document.getElementById('notification-badge'); const notificationList = document.getElementById('notification-list'); if (notificationBadge && notificationList) { fetch('/crafts/includes/get_notifications.php') .then(response => response.json()) .then(data => { if (data.success) { // Update badge notificationBadge.textContent = data.unread_count; notificationBadge.style.display = data.unread_count > 0 ? 'block' : 'none'; // Update list notificationList.innerHTML = ''; if (data.notifications.length === 0) { notificationList.innerHTML = ''; } else { data.notifications.forEach(notification => { const li = document.createElement('li'); li.className = notification.is_read ? 'dropdown-item' : 'dropdown-item unread'; li.innerHTML = `
${notification.message}
${notification.time}
`; notificationList.appendChild(li); // Mark as read on click li.addEventListener('click', function() { const notificationId = this.querySelector('.notification-item').getAttribute('data-id'); fetch('/crafts/includes/mark_notification_read.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'id=' + encodeURIComponent(notificationId) }) .then(response => response.json()) .then(data => { if (data.success) { this.classList.remove('unread'); loadNotifications(); } }) .catch(error => console.error('Error:', error)); }); }); } } }) .catch(error => console.error('Error:', error)); } } // Initial load and periodic refresh loadNotifications(); setInterval(loadNotifications, 30000); // 드롭다운 메뉴 동작 - PHP 5.2 및 MySQL 5.1 환경 호환성 개선 function handleDropdowns() { const isDesktop = window.innerWidth >= 992; const dropdowns = document.querySelectorAll('.navbar-nav .dropdown'); // 기존 이벤트 리스너 제거 dropdowns.forEach(dropdown => { const menu = dropdown.querySelector('.dropdown-menu'); const toggle = dropdown.querySelector('.dropdown-toggle'); dropdown.removeEventListener('mouseenter', showDropdown); dropdown.removeEventListener('mouseleave', hideDropdown); if (toggle) { toggle.removeEventListener('click', toggleDropdown); } }); // 데스크톱에서는 호버 이벤트 if (isDesktop) { dropdowns.forEach(dropdown => { dropdown.addEventListener('mouseenter', showDropdown); dropdown.addEventListener('mouseleave', hideDropdown); }); } // 모바일에서는 클릭 이벤트 else { dropdowns.forEach(dropdown => { const toggle = dropdown.querySelector('.dropdown-toggle'); if (toggle) { toggle.addEventListener('click', toggleDropdown); } }); } } function showDropdown() { const menu = this.querySelector('.dropdown-menu'); if (menu) { menu.classList.add('show'); } } function hideDropdown() { const menu = this.querySelector('.dropdown-menu'); if (menu) { menu.classList.remove('show'); } } function toggleDropdown(e) { e.preventDefault(); e.stopPropagation(); const dropdown = this.parentElement; const menu = dropdown.querySelector('.dropdown-menu'); // 다른 열린 드롭다운 메뉴 닫기 const openMenus = document.querySelectorAll('.dropdown-menu.show'); openMenus.forEach(openMenu => { if (openMenu !== menu) { openMenu.classList.remove('show'); } }); // 현재 메뉴 토글 menu.classList.toggle('show'); } // 초기 설정 handleDropdowns(); // 윈도우 크기 변경 시 처리 window.addEventListener('resize', function() { handleDropdowns(); }); // 문서 클릭 시 열린 드롭다운 메뉴 닫기 (모바일) document.addEventListener('click', function(e) { if (window.innerWidth < 992) { const openMenus = document.querySelectorAll('.dropdown-menu.show'); const isDropdownToggle = e.target.classList.contains('dropdown-toggle') || e.target.closest('.dropdown-toggle'); if (!isDropdownToggle) { openMenus.forEach(menu => { menu.classList.remove('show'); }); } } }); }); // 이미지 미리보기 기능 function previewImage(input, previewId) { if (input.files && input.files[0]) { const reader = new FileReader(); reader.onload = function(e) { const preview = document.getElementById(previewId); if (preview) { preview.src = e.target.result; preview.style.display = 'block'; } }; reader.readAsDataURL(input.files[0]); } } // 파일 업로드 필드 동적 추가 function addFileField(containerId, limit) { const container = document.getElementById(containerId); if (!container) return; const fileFields = container.querySelectorAll('.file-field'); if (fileFields.length >= limit) { alert(`최대 ${limit}개까지 파일을 추가할 수 있습니다.`); return; } const newField = document.createElement('div'); newField.className = 'file-field mb-2'; newField.innerHTML = `
`; container.appendChild(newField); } // 파일 업로드 필드 제거 function removeFileField(button) { const fieldDiv = button.closest('.file-field'); if (fieldDiv) { fieldDiv.remove(); } }