/** * Modal Alerts - Bootstrap Modal replacement for native alert() and confirm() * Fixes Chrome auto-dismiss issue with native dialogs * * Usage: * showAlert('Message', 'success'); * showConfirm('Delete this?', function() { }); */ (function () { 'use strict'; // Create and inject modal HTML into page if not already present function ensureModalsExist() { if (document.getElementById('globalAlertModal')) { return; // Already exists } const modalsHTML = `
`; // Inject modals at end of body document.body.insertAdjacentHTML('beforeend', modalsHTML); } /** * Show an alert message using Bootstrap modal * @param {string} message - The message to display * @param {string|object} typeOrOptions - Type ('success', 'error', 'warning', 'info') or options object * @param {string} title - Optional custom title */ window.showAlert = function (message, typeOrOptions, title) { ensureModalsExist(); let type = 'info'; let isHtml = false; if (typeof typeOrOptions === 'object' && typeOrOptions !== null) { type = typeOrOptions.type || 'info'; isHtml = typeOrOptions.isHtml || false; title = typeOrOptions.title || title; } else if (typeof typeOrOptions === 'string') { type = typeOrOptions; } const modal = document.getElementById('globalAlertModal'); const header = document.getElementById('globalAlertModalHeader'); const titleEl = document.getElementById('globalAlertModalTitle'); const bodyEl = document.getElementById('globalAlertModalBody'); const iconEl = document.getElementById('globalAlertModalIcon'); // Configuration for different types const types = { 'success': { title: 'Success', icon: 'fa-check-circle', headerClass: 'bg-success text-white', btnClose: 'btn-close-white' }, 'error': { title: 'Error', icon: 'fa-exclamation-triangle', headerClass: 'bg-danger text-white', btnClose: 'btn-close-white' }, 'warning': { title: 'Warning', icon: 'fa-exclamation-circle', headerClass: 'bg-warning text-dark', btnClose: '' }, 'info': { title: 'Notice', icon: 'fa-info-circle', headerClass: 'bg-info text-white', btnClose: 'btn-close-white' } }; const config = types[type] || types['info']; // Update header styling header.className = 'modal-header ' + config.headerClass; const closeBtn = header.querySelector('.btn-close'); closeBtn.className = 'btn-close ' + config.btnClose; // Update icon iconEl.className = 'fas ' + config.icon + ' me-2'; // Update title titleEl.textContent = title || config.title; // Update body - support HTML or text if (isHtml || message.includes('') || message.includes('
' + escapeHtml(message) + '
'; } // Show modal const bsModal = new bootstrap.Modal(modal); bsModal.show(); }; /** * Show a confirmation dialog using Bootstrap modal * @param {string} message - The confirmation message * @param {function} onConfirm - Callback function if user confirms * @param {function|object} onCancelOrOptions - Optional callback or options object * @param {string} title - Optional custom title */ window.showConfirm = function (message, onConfirm, onCancelOrOptions, title) { ensureModalsExist(); let onCancel = null; let isHtml = false; if (typeof onCancelOrOptions === 'object' && onCancelOrOptions !== null) { onCancel = onCancelOrOptions.onCancel; isHtml = onCancelOrOptions.isHtml || false; title = onCancelOrOptions.title || null; } else { onCancel = onCancelOrOptions; } const modalEl = document.getElementById('globalConfirmModal'); const bodyEl = document.getElementById('globalConfirmModalBody'); const titleEl = document.getElementById('globalConfirmModalTitleText'); const okBtn = document.getElementById('globalConfirmOkBtn'); const cancelBtn = document.getElementById('globalConfirmCancelBtn'); // Set title if (title) { titleEl.textContent = title; } else { titleEl.textContent = 'Confirm Action'; } // Set message if (isHtml || message.includes('') || message.includes('
' + escapeHtml(message) + '
'; } // Remove old event listeners by cloning buttons const newOkBtn = okBtn.cloneNode(true); const newCancelBtn = cancelBtn.cloneNode(true); okBtn.parentNode.replaceChild(newOkBtn, okBtn); cancelBtn.parentNode.replaceChild(newCancelBtn, cancelBtn); const modal = new bootstrap.Modal(modalEl); // Add event listeners newOkBtn.addEventListener('click', function () { modal.hide(); if (typeof onConfirm === 'function') { onConfirm(); } }); newCancelBtn.addEventListener('click', function () { modal.hide(); if (typeof onCancel === 'function') { onCancel(); } }); modal.show(); }; /** * Show a delete confirmation dialog * @param {string} itemName - Name of the item to delete * @param {function} onConfirm - Callback function if user confirms deletion * @param {string} message - Optional custom message (default: "Are you sure you want to delete this item?") */ window.showDeleteConfirm = function (itemName, onConfirm, message) { ensureModalsExist(); const modalEl = document.getElementById('globalDeleteModal'); const messageEl = document.getElementById('globalDeleteModalMessage'); const itemNameEl = document.getElementById('globalDeleteModalItemName'); const confirmBtn = document.getElementById('globalDeleteConfirmBtn'); // Set custom message if provided if (message) { messageEl.textContent = message; } else { messageEl.textContent = 'Are you sure you want to delete this item?'; } // Set item name itemNameEl.textContent = itemName; // Remove old event listener by cloning button const newConfirmBtn = confirmBtn.cloneNode(true); confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn); const modal = new bootstrap.Modal(modalEl); // Add new event listener newConfirmBtn.addEventListener('click', function () { modal.hide(); if (typeof onConfirm === 'function') { onConfirm(); } }); modal.show(); }; /** * Escape HTML to prevent XSS */ function escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function (m) { return map[m]; }); } // Auto-initialize on DOMContentLoaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', ensureModalsExist); } else { ensureModalsExist(); } /** * AUTOMATIC OVERRIDE of native alert() * This makes ALL existing alert() calls automatically use Bootstrap modals */ window.alert = function (message) { // Auto-detect message type from content let type = 'info'; const msg = String(message || ''); const msgLower = msg.toLowerCase(); // Refined type inference to avoid false positives if (msgLower.includes('success') || msgLower.includes('successfully') || msgLower.includes('created') || msgLower.includes('updated') || msgLower.includes('saved')) { // Avoid "not successful" if (!msgLower.includes('not success')) { type = 'success'; } } if (type === 'info') { if (msgLower.includes('error') || msgLower.includes('failed') || msgLower.includes('invalid') || msgLower.includes('exception')) { type = 'error'; } else if (msgLower.includes('warning') || msgLower.includes('required') || msgLower.includes('attention')) { type = 'warning'; } } showAlert(msg, type); }; console.log('Modal Alerts library loaded - native alert() overridden'); console.log('For confirm(), use showConfirm() or showDeleteConfirm() instead of native confirm()'); })();