1/** 2 * JS modal dialog functionality 3 */ 4window.VibeMapModal = { 5 /** 6 * Creates and shows a modal dialog 7 * @param {Object} options Modal configuration 8 * @param {string} options.content HTML content for the modal 9 * @param {string} [options.className] Additional CSS class for styling 10 * @returns {HTMLDialogElement} The modal element 11 */ 12 show({ content, className = '' }) { 13 // Create or get existing dialog 14 let dialog = document.getElementById('vibemap-modal'); 15 if (!dialog) { 16 dialog = document.createElement('dialog'); 17 dialog.id = 'vibemap-modal'; 18 document.body.appendChild(dialog); 19 } 20 21 // Set content and classes 22 dialog.className = `vibemap-modal ${className}`; 23 // Clear any stale closing state (e.g. previous Escape/cancel close) 24 dialog.removeAttribute('closing'); 25 26 // Get the icon color from settings and set it as a CSS variable 27 const iconColor = window.VibeMapBookmarksData?.bookmarks_icon_color || '#3498db'; 28 dialog.style.setProperty('--vibemap-bookmarks-icon-color', iconColor); 29 30 dialog.innerHTML = ` 31 <div class="vibemap-modal-content"> 32 <button class="vibemap-modal-close" aria-label="Close">×</button> 33 ${content} 34 </div> 35 `; 36 37 // Add event listeners 38 const closeBtn = dialog.querySelector('.vibemap-modal-close'); 39 closeBtn.addEventListener('click', () => this.close()); 40 41 dialog.addEventListener('click', (e) => { 42 if (e.target === dialog) this.close(); 43 }); 44 45 // Show the modal 46 dialog.showModal(); 47 return dialog; 48 }, 49 50 /** 51 * Closes the current modal if one exists 52 */ 53 close() { 54 const dialog = document.getElementById('vibemap-modal'); 55 if (dialog) { 56 // Add closing animation 57 dialog.setAttribute('closing', ''); 58 59 // Actually close, whichever trigger fires first (animationend or timeout). 60 // Gating solely on animationend can trap the page open if the closing 61 // animation never runs (CSS blocked, prefers-reduced-motion, etc.). 62 let finished = false; 63 const finishClose = () => { 64 if (finished) return; 65 finished = true; 66 clearTimeout(fallbackTimer); 67 dialog.close(); 68 dialog.removeAttribute('closing'); 69 }; 70 71 // Wait for animation to finish before actually closing 72 dialog.addEventListener('animationend', finishClose, { once: true }); 73 74 // Fallback in case the closing animation never fires 75 const fallbackTimer = setTimeout(finishClose, 250); 76 } 77 } 78};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.