// Main JavaScript file for the application
// Wait for DOM to load
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM loaded');
// Initialize tooltips
initTooltips();
// Initialize popovers
initPopovers();
// Add smooth scrolling
initSmoothScroll();
// Auto-hide alerts after 5 seconds
initAutoHideAlerts();
// Add active class to current nav item
setActiveNavItem();
// Initialize file upload preview
initFileUpload();
// Initialize form validation
initFormValidation();
});
// Initialize Bootstrap tooltips
function initTooltips() {
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function(tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
}
// Initialize Bootstrap popovers
function initPopovers() {
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
popoverTriggerList.map(function(popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl);
});
}
// Smooth scrolling for anchor links
function initSmoothScroll() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
// Auto-hide alerts after 5 seconds
function initAutoHideAlerts() {
// Only auto-hide top-of-page flash alerts (container.mt-3). Do NOT auto-hide
// the diagnosis result container (#hasil) so users can read results at their pace.
setTimeout(function() {
document.querySelectorAll('.container.mt-3 .alert').forEach(alert => {
const bsAlert = new bootstrap.Alert(alert);
bsAlert.close();
});
}, 15000); // 15s so users have more time to read messages
}
// Set active class to current nav item
function setActiveNavItem() {
const currentPath = window.location.pathname;
document.querySelectorAll('.navbar-nav .nav-link').forEach(link => {
if (link.getAttribute('href') === currentPath) {
link.classList.add('active');
}
});
}
// Initialize file upload preview
function initFileUpload() {
const fileInput = document.querySelector('input[type="file"]');
if (fileInput) {
fileInput.addEventListener('change', function(e) {
if (this.files && this.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
const preview = document.getElementById('preview');
if (preview) {
preview.src = e.target.result;
preview.classList.remove('d-none');
}
// Show file info
const fileInfo = document.getElementById('fileInfo');
const fileName = document.getElementById('fileName');
if (fileInfo && fileName) {
fileName.textContent = this.files[0].name;
fileInfo.classList.remove('d-none');
}
// Get image dimensions
const img = new Image();
img.onload = function() {
const dimensions = document.getElementById('imageDimensions');
if (dimensions) {
dimensions.textContent = `${this.width} x ${this.height} pixels`;
}
};
img.src = e.target.result;
}.bind(this);
reader.readAsDataURL(this.files[0]);
}
});
}
}
// Initialize form validation
function initFormValidation() {
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);
});
}
// AJAX function for API calls
async function fetchAPI(url, method = 'GET', data = null) {
const options = {
method: method,
headers: {
'Content-Type': 'application/json',
}
};
if (data) {
options.body = JSON.stringify(data);
}
try {
const response = await fetch(url, options);
const result = await response.json();
return result;
} catch (error) {
console.error('API Error:', error);
showNotification('error', 'Terjadi kesalahan saat menghubungi server');
return null;
}
}
// Show notification
function showNotification(type, message) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type === 'error' ? 'danger' : type} alert-dismissible fade show position-fixed top-0 end-0 m-3`;
alertDiv.style.zIndex = '9999';
alertDiv.innerHTML = `
${message}
`;
document.body.appendChild(alertDiv);
setTimeout(() => {
alertDiv.remove();
}, 5000);
}
// Format date
function formatDate(date) {
return new Date(date).toLocaleString('id-ID', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
// Format file size
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Copy to clipboard
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
showNotification('success', 'Teks berhasil disalin');
}).catch(() => {
showNotification('error', 'Gagal menyalin teks');
});
}
// Download file
function downloadFile(content, fileName, contentType) {
const a = document.createElement('a');
const file = new Blob([content], { type: contentType });
a.href = URL.createObjectURL(file);
a.download = fileName;
a.click();
URL.revokeObjectURL(a.href);
}
// Debounce function for search inputs
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Throttle function for scroll events
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Add to favorites
function addToFavorites(title, url) {
if (window.sidebar && window.sidebar.addPanel) { // Firefox
window.sidebar.addPanel(title, url, '');
} else if (window.external && ('AddFavorite' in window.external)) { // IE
window.external.AddFavorite(url, title);
} else { // Chrome, Safari
showNotification('info', 'Tekan Ctrl+D untuk menambahkan ke bookmark');
}
}
// Detect browser
function getBrowser() {
const ua = navigator.userAgent;
let browser = 'Unknown';
if (ua.indexOf('Firefox') > -1) browser = 'Firefox';
else if (ua.indexOf('Chrome') > -1) browser = 'Chrome';
else if (ua.indexOf('Safari') > -1) browser = 'Safari';
else if (ua.indexOf('MSIE') > -1 || ua.indexOf('Trident') > -1) browser = 'Internet Explorer';
else if (ua.indexOf('Edge') > -1) browser = 'Edge';
return browser;
}
// Check online status
window.addEventListener('online', () => {
showNotification('success', 'Koneksi internet tersambung kembali');
});
window.addEventListener('offline', () => {
showNotification('warning', 'Koneksi internet terputus');
});
// Lazy loading images
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('fade-in');
imageObserver.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
}
// Prevent double form submission
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', function() {
const submitButton = this.querySelector('button[type="submit"]');
if (submitButton) {
submitButton.disabled = true;
setTimeout(() => {
submitButton.disabled = false;
}, 3000);
}
});
});
// Dark mode toggle
function toggleDarkMode() {
document.body.classList.toggle('dark-mode');
const isDark = document.body.classList.contains('dark-mode');
localStorage.setItem('darkMode', isDark);
}
// Load dark mode preference
if (localStorage.getItem('darkMode') === 'true') {
document.body.classList.add('dark-mode');
}
// Export functions for use in HTML
window.showNotification = showNotification;
window.formatDate = formatDate;
window.formatFileSize = formatFileSize;
window.copyToClipboard = copyToClipboard;
window.downloadFile = downloadFile;