PageSourceSearch

https://pupwife.com/projects/chatroom/scripts/imageLightbox.js

js pupwife.com collected 2026-09-30 15:07:14 UTC 6,628 bytes, 191 lines download raw bytes

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.