1/** 2 * Full-size image lightbox for chat message attachments. 3 */ 4window.ChatroomImageLightbox = (function () { 5 'use strict'; 6 7 var overlay = null; 8 var lightboxImg = null; 9 var openLink = null; 10 var closeBtn = null; 11 var clickHandler = null; 12 var keyHandler = null; 13 var initialized = false; 14 15 function getPageWrap() { 16 if (window.ChatroomSpa && typeof window.ChatroomSpa.getPageWrap === 'function') { 17 return window.ChatroomSpa.getPageWrap(); 18 } 19 return document.getElementById('page-wrap'); 20 } 21 22 function isChatroomImage(img) { 23 if (!img || img.tagName !== 'IMG') return false; 24 if (!img.classList.contains('message-image')) return false; 25 if (img.closest('.chatroom-image-lightbox')) return false; 26 27 var wrap = getPageWrap(); 28 if (wrap) return wrap.contains(img); 29 30 return document.body.classList.contains('page-chatroom') || 31 !!document.querySelector('.citrus-window__content.page-chatroom, .citrus-fullscreen__content.page-chatroom'); 32 } 33 34 function readChatroomThemeSettings() { 35 var theme = 'default'; 36 var colorMode = 'dark'; 37 try { 38 var saved = JSON.parse(localStorage.getItem('chatroom_settings') || '{}'); 39 if (saved.theme === 'dark') { 40 theme = 'default'; 41 colorMode = 'dark'; 42 } else if (saved.theme === 'light') { 43 theme = 'default'; 44 colorMode = 'light'; 45 } else if (saved.theme) { 46 theme = saved.theme; 47 } 48 if (saved.colorMode === 'light' || saved.colorMode === 'dark') { 49 colorMode = saved.colorMode; 50 } 51 var site = JSON.parse(localStorage.getItem('puppywifey_settings') || '{}'); 52 if (typeof site.darkMode === 'boolean' && !saved.colorMode) { 53 colorMode = site.darkMode ? 'dark' : 'light'; 54 } 55 } catch (e) { /* ignore */ } 56 return { theme: theme, colorMode: colorMode }; 57 } 58 59 function applyTheme() { 60 if (!overlay) return; 61 var settings = readChatroomThemeSettings(); 62 overlay.setAttribute('data-chatroom-theme', settings.theme); 63 overlay.setAttribute('data-chatroom-color-mode', settings.colorMode); 64 } 65 66 function ensureOverlay() { 67 if (overlay) return overlay; 68 69 overlay = document.createElement('div'); 70 overlay.id = 'chatroom-image-lightbox'; 71 overlay.className = 'chatroom-image-lightbox'; 72 overlay.setAttribute('role', 'dialog'); 73 overlay.setAttribute('aria-modal', 'true'); 74 overlay.setAttribute('aria-label', 'Image preview'); 75 overlay.setAttribute('aria-hidden', 'true'); 76 77 var backdrop = document.createElement('button'); 78 backdrop.type = 'button'; 79 backdrop.className = 'chatroom-image-lightbox-backdrop'; 80 backdrop.setAttribute('aria-label', 'Close image preview'); 81 82 var panel = document.createElement('div'); 83 panel.className = 'chatroom-image-lightbox-panel'; 84 85 closeBtn = document.createElement('button'); 86 closeBtn.type = 'button'; 87 closeBtn.className = 'chatroom-image-lightbox-close cr-icon-btn'; 88 closeBtn.setAttribute('aria-label', 'Close image preview'); 89 if (window.ChatroomIcons && typeof window.ChatroomIcons.set === 'function') { 90 window.ChatroomIcons.set(closeBtn, 'xmark'); 91 } else { 92 closeBtn.innerHTML = '<i class="fa-solid fa-xmark" aria-hidden="true"></i>'; 93 } 94 95 lightboxImg = document.createElement('img'); 96 lightboxImg.className = 'chatroom-image-lightbox-img'; 97 lightboxImg.alt = ''; 98 99 openLink = document.createElement('a'); 100 openLink.className = 'chatroom-image-lightbox-open'; 101 openLink.target = '_blank'; 102 openLink.rel = 'noopener noreferrer'; 103 openLink.title = 'Open image in new tab'; 104 openLink.innerHTML = '<i class="fa-solid fa-up-right-from-square" aria-hidden="true"></i><span>Open</span>'; 105 106 panel.appendChild(closeBtn); 107 panel.appendChild(lightboxImg); 108 panel.appendChild(openLink); 109 overlay.appendChild(backdrop); 110 overlay.appendChild(panel); 111 document.body.appendChild(overlay); 112 113 backdrop.addEventListener('click', close); 114 closeBtn.addEventListener('click', function (e) { 115 e.preventDefault(); 116 close(); 117 }); 118 panel.addEventListener('click', function (e) { 119 e.stopPropagation(); 120 }); 121 openLink.addEventListener('click', function (e) { 122 e.stopPropagation(); 123 }); 124 125 return overlay; 126 } 127 128 function open(src, alt) { 129 if (!src) return; 130 ensureOverlay(); 131 applyTheme(); 132 lightboxImg.src = src; 133 lightboxImg.alt = alt || 'Image'; 134 openLink.href = src; 135 overlay.classList.add('is-open'); 136 overlay.setAttribute('aria-hidden', 'false'); 137 document.body.classList.add('chatroom-image-lightbox-open'); 138 closeBtn.focus(); 139 } 140 141 function close() { 142 if (!overlay) return; 143 overlay.classList.remove('is-open'); 144 overlay.setAttribute('aria-hidden', 'true'); 145 document.body.classList.remove('chatroom-image-lightbox-open'); 146 lightboxImg.removeAttribute('src'); 147 openLink.removeAttribute('href'); 148 } 149 150 function onDocumentClick(e) { 151 var img = e.target; 152 if (!isChatroomImage(img)) return; 153 if (e.target.closest('.message-image-link')) return; 154 155 e.preventDefault(); 156 e.stopPropagation(); 157 open(img.currentSrc || img.src, img.alt); 158 } 159 160 function onDocumentKeydown(e) { 161 if (e.key !== 'Escape' || !overlay || !overlay.classList.contains('is-open')) return; 162 e.preventDefault(); 163 close(); 164 } 165
166 function init() { 167 if (initialized) return; 168 initialized = true; 169 clickHandler = onDocumentClick; 170 keyHandler = onDocumentKeydown; 171 document.addEventListener('click', clickHandler, true); 172 document.addEventListener('keydown', keyHandler); 173 } 174 175 function destroy() { 176 close(); 177 if (!initialized) return; 178 document.removeEventListener('click', clickHandler, true); 179 document.removeEventListener('keydown', keyHandler); 180 initialized = false; 181 clickHandler = null; 182 keyHandler = null; 183 } 184 185 return { 186 init: init, 187 destroy: destroy, 188 open: open, 189 close: close 190 }; 191})();
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.