bloggy 0.1.43__py3-none-any.whl → 0.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bloggy/__init__.py +1 -1
- bloggy/build.py +15 -4
- bloggy/config.py +8 -0
- bloggy/core.py +707 -91
- bloggy/main.py +3 -3
- bloggy/static/scripts.js +657 -45
- bloggy-0.2.3.dist-info/METADATA +167 -0
- bloggy-0.2.3.dist-info/RECORD +13 -0
- bloggy-0.1.43.dist-info/METADATA +0 -926
- bloggy-0.1.43.dist-info/RECORD +0 -13
- {bloggy-0.1.43.dist-info → bloggy-0.2.3.dist-info}/WHEEL +0 -0
- {bloggy-0.1.43.dist-info → bloggy-0.2.3.dist-info}/entry_points.txt +0 -0
- {bloggy-0.1.43.dist-info → bloggy-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {bloggy-0.1.43.dist-info → bloggy-0.2.3.dist-info}/top_level.txt +0 -0
bloggy/static/scripts.js
CHANGED
|
@@ -1,12 +1,146 @@
|
|
|
1
1
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
2
2
|
|
|
3
3
|
const mermaidStates = {};
|
|
4
|
+
const mermaidDebugEnabled = () => (
|
|
5
|
+
window.BLOGGY_DEBUG_MERMAID === true ||
|
|
6
|
+
localStorage.getItem('bloggyDebugMermaid') === '1'
|
|
7
|
+
);
|
|
8
|
+
const mermaidDebugLog = (...args) => {
|
|
9
|
+
if (mermaidDebugEnabled()) {
|
|
10
|
+
console.log('[bloggy][mermaid]', ...args);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const mermaidDebugSnapshot = (label) => {
|
|
14
|
+
if (!mermaidDebugEnabled()) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const wrappers = Array.from(document.querySelectorAll('.mermaid-wrapper'));
|
|
18
|
+
const withSvg = wrappers.filter(w => w.querySelector('svg'));
|
|
19
|
+
const interactive = wrappers.filter(w => w.dataset.mermaidInteractive === 'true');
|
|
20
|
+
const last = wrappers[wrappers.length - 1];
|
|
21
|
+
let lastRect = null;
|
|
22
|
+
if (last) {
|
|
23
|
+
const rect = last.getBoundingClientRect();
|
|
24
|
+
lastRect = {
|
|
25
|
+
id: last.id,
|
|
26
|
+
width: Math.round(rect.width),
|
|
27
|
+
height: Math.round(rect.height),
|
|
28
|
+
hasSvg: !!last.querySelector('svg'),
|
|
29
|
+
interactive: last.dataset.mermaidInteractive === 'true'
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
mermaidDebugLog(label, {
|
|
33
|
+
total: wrappers.length,
|
|
34
|
+
withSvg: withSvg.length,
|
|
35
|
+
interactive: interactive.length,
|
|
36
|
+
last: lastRect
|
|
37
|
+
});
|
|
38
|
+
};
|
|
4
39
|
const GANTT_WIDTH = 1200;
|
|
5
40
|
|
|
41
|
+
function handleCodeCopyClick(event) {
|
|
42
|
+
const button = event.target.closest('.code-copy-button, .hljs-copy-button');
|
|
43
|
+
if (!button) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
event.stopPropagation();
|
|
48
|
+
const container = button.closest('.code-block') || button.closest('pre') || button.parentElement;
|
|
49
|
+
const textarea = container ? container.querySelector('textarea[id$="-clipboard"]') : null;
|
|
50
|
+
let text = '';
|
|
51
|
+
if (textarea && textarea.value) {
|
|
52
|
+
text = textarea.value;
|
|
53
|
+
} else {
|
|
54
|
+
const codeEl = (container && container.querySelector('pre > code')) ||
|
|
55
|
+
(container && container.querySelector('code')) ||
|
|
56
|
+
button.closest('pre');
|
|
57
|
+
if (!codeEl) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
text = codeEl.innerText || codeEl.textContent || '';
|
|
61
|
+
}
|
|
62
|
+
const showToast = () => {
|
|
63
|
+
let toast = document.getElementById('code-copy-toast');
|
|
64
|
+
if (!toast) {
|
|
65
|
+
toast = document.createElement('div');
|
|
66
|
+
toast.id = 'code-copy-toast';
|
|
67
|
+
toast.className = 'fixed top-6 right-6 z-[10000] text-xs bg-slate-900 text-white px-3 py-2 rounded shadow-lg opacity-0 transition-opacity duration-300';
|
|
68
|
+
toast.textContent = 'Copied';
|
|
69
|
+
document.body.appendChild(toast);
|
|
70
|
+
}
|
|
71
|
+
toast.classList.remove('opacity-0');
|
|
72
|
+
toast.classList.add('opacity-100');
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
toast.classList.remove('opacity-100');
|
|
75
|
+
toast.classList.add('opacity-0');
|
|
76
|
+
}, 1400);
|
|
77
|
+
};
|
|
78
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
79
|
+
navigator.clipboard.writeText(text).then(showToast).catch(() => {
|
|
80
|
+
const textarea = document.createElement('textarea');
|
|
81
|
+
textarea.value = text;
|
|
82
|
+
textarea.setAttribute('readonly', '');
|
|
83
|
+
textarea.style.position = 'absolute';
|
|
84
|
+
textarea.style.left = '-9999px';
|
|
85
|
+
document.body.appendChild(textarea);
|
|
86
|
+
textarea.select();
|
|
87
|
+
document.execCommand('copy');
|
|
88
|
+
document.body.removeChild(textarea);
|
|
89
|
+
showToast();
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
const textarea = document.createElement('textarea');
|
|
93
|
+
textarea.value = text;
|
|
94
|
+
textarea.setAttribute('readonly', '');
|
|
95
|
+
textarea.style.position = 'absolute';
|
|
96
|
+
textarea.style.left = '-9999px';
|
|
97
|
+
document.body.appendChild(textarea);
|
|
98
|
+
textarea.select();
|
|
99
|
+
document.execCommand('copy');
|
|
100
|
+
document.body.removeChild(textarea);
|
|
101
|
+
showToast();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
document.addEventListener('click', handleCodeCopyClick, true);
|
|
106
|
+
|
|
6
107
|
function initMermaidInteraction() {
|
|
7
|
-
document.querySelectorAll('.mermaid-wrapper')
|
|
108
|
+
const wrappers = Array.from(document.querySelectorAll('.mermaid-wrapper'));
|
|
109
|
+
if (mermaidDebugEnabled()) {
|
|
110
|
+
const pending = wrappers.filter(w => !w.querySelector('svg'));
|
|
111
|
+
const last = wrappers[wrappers.length - 1];
|
|
112
|
+
mermaidDebugLog('initMermaidInteraction: total', wrappers.length, 'pending', pending.length);
|
|
113
|
+
if (last) {
|
|
114
|
+
mermaidDebugLog('initMermaidInteraction: last wrapper', last.id, 'hasSvg', !!last.querySelector('svg'));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
wrappers.forEach((wrapper, idx) => {
|
|
8
118
|
const svg = wrapper.querySelector('svg');
|
|
9
|
-
|
|
119
|
+
const alreadyInteractive = wrapper.dataset.mermaidInteractive === 'true';
|
|
120
|
+
if (mermaidDebugEnabled()) {
|
|
121
|
+
mermaidDebugLog(
|
|
122
|
+
'initMermaidInteraction: wrapper',
|
|
123
|
+
idx,
|
|
124
|
+
wrapper.id,
|
|
125
|
+
'hasSvg',
|
|
126
|
+
!!svg,
|
|
127
|
+
'interactive',
|
|
128
|
+
alreadyInteractive
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const getSvg = () => wrapper.querySelector('svg');
|
|
132
|
+
const applySvgState = (currentSvg) => {
|
|
133
|
+
if (!currentSvg) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
currentSvg.style.pointerEvents = 'none';
|
|
137
|
+
currentSvg.style.transform = `translate(${state.translateX}px, ${state.translateY}px) scale(${state.scale})`;
|
|
138
|
+
currentSvg.style.transformOrigin = 'center center';
|
|
139
|
+
};
|
|
140
|
+
if (svg) {
|
|
141
|
+
svg.style.pointerEvents = 'none';
|
|
142
|
+
}
|
|
143
|
+
if (!svg || alreadyInteractive) return;
|
|
10
144
|
|
|
11
145
|
// DEBUG: Log initial state
|
|
12
146
|
console.group(`🔍 initMermaidInteraction: ${wrapper.id}`);
|
|
@@ -26,16 +160,28 @@ function initMermaidInteraction() {
|
|
|
26
160
|
|
|
27
161
|
// For very wide diagrams (like Gantt charts), prefer width scaling even if it exceeds height
|
|
28
162
|
const aspectRatio = svgRect.width / svgRect.height;
|
|
163
|
+
const maxUpscale = 1;
|
|
29
164
|
let initialScale;
|
|
30
165
|
if (aspectRatio > 3) {
|
|
31
|
-
// Wide diagram: scale to fit width,
|
|
32
|
-
initialScale = scaleX;
|
|
166
|
+
// Wide diagram: scale to fit width, but do not upscale by default
|
|
167
|
+
initialScale = Math.min(scaleX, maxUpscale);
|
|
33
168
|
console.log('Wide diagram detected (aspect ratio > 3):', aspectRatio, 'Using scaleX:', initialScale);
|
|
34
169
|
} else {
|
|
35
|
-
// Normal diagram: fit to smaller dimension, but
|
|
36
|
-
initialScale = Math.min(scaleX, scaleY,
|
|
170
|
+
// Normal diagram: fit to smaller dimension, but do not upscale by default
|
|
171
|
+
initialScale = Math.min(scaleX, scaleY, maxUpscale);
|
|
37
172
|
console.log('Normal diagram (aspect ratio <=3):', aspectRatio, 'Using min scale:', initialScale);
|
|
38
173
|
}
|
|
174
|
+
|
|
175
|
+
if (mermaidDebugEnabled()) {
|
|
176
|
+
mermaidDebugLog('initMermaidInteraction: sizing', {
|
|
177
|
+
id: wrapper.id,
|
|
178
|
+
wrapperWidth: wrapperRect.width,
|
|
179
|
+
wrapperHeight: wrapperRect.height,
|
|
180
|
+
svgWidth: svgRect.width,
|
|
181
|
+
svgHeight: svgRect.height,
|
|
182
|
+
initialScale
|
|
183
|
+
});
|
|
184
|
+
}
|
|
39
185
|
|
|
40
186
|
const state = {
|
|
41
187
|
scale: initialScale,
|
|
@@ -46,22 +192,46 @@ function initMermaidInteraction() {
|
|
|
46
192
|
startY: 0
|
|
47
193
|
};
|
|
48
194
|
mermaidStates[wrapper.id] = state;
|
|
195
|
+
wrapper.dataset.mermaidInteractive = 'true';
|
|
49
196
|
console.log('Final state:', state);
|
|
50
197
|
console.groupEnd();
|
|
198
|
+
|
|
199
|
+
if (mermaidDebugEnabled() && !wrapper.dataset.mermaidDebugBound) {
|
|
200
|
+
wrapper.dataset.mermaidDebugBound = 'true';
|
|
201
|
+
const logEvent = (name, event) => {
|
|
202
|
+
const target = event.target && event.target.tagName ? event.target.tagName : 'unknown';
|
|
203
|
+
mermaidDebugLog(`${name} on ${wrapper.id}`, { type: event.type, target });
|
|
204
|
+
};
|
|
205
|
+
wrapper.addEventListener('pointerdown', (e) => logEvent('pointerdown', e));
|
|
206
|
+
wrapper.addEventListener('pointermove', (e) => logEvent('pointermove', e));
|
|
207
|
+
wrapper.addEventListener('pointerup', (e) => logEvent('pointerup', e));
|
|
208
|
+
wrapper.addEventListener('wheel', (e) => logEvent('wheel', e));
|
|
209
|
+
}
|
|
51
210
|
|
|
52
211
|
function updateTransform() {
|
|
53
|
-
|
|
54
|
-
svg.style.transformOrigin = 'center center';
|
|
212
|
+
applySvgState(getSvg());
|
|
55
213
|
}
|
|
56
214
|
|
|
57
215
|
// Apply initial scale
|
|
58
216
|
updateTransform();
|
|
217
|
+
|
|
218
|
+
if (!wrapper.dataset.mermaidObserver) {
|
|
219
|
+
const observer = new MutationObserver(() => {
|
|
220
|
+
applySvgState(getSvg());
|
|
221
|
+
});
|
|
222
|
+
observer.observe(wrapper, { childList: true, subtree: true });
|
|
223
|
+
wrapper.dataset.mermaidObserver = 'true';
|
|
224
|
+
}
|
|
59
225
|
|
|
60
226
|
// Mouse wheel zoom (zooms towards cursor position)
|
|
61
227
|
wrapper.addEventListener('wheel', (e) => {
|
|
62
228
|
e.preventDefault();
|
|
63
229
|
|
|
64
|
-
const
|
|
230
|
+
const currentSvg = getSvg();
|
|
231
|
+
if (!currentSvg) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const rect = currentSvg.getBoundingClientRect();
|
|
65
235
|
|
|
66
236
|
// Mouse position relative to SVG's current position
|
|
67
237
|
const mouseX = e.clientX - rect.left - rect.width / 2;
|
|
@@ -81,34 +251,75 @@ function initMermaidInteraction() {
|
|
|
81
251
|
updateTransform();
|
|
82
252
|
}, { passive: false });
|
|
83
253
|
|
|
84
|
-
// Pan with mouse
|
|
85
|
-
wrapper.
|
|
86
|
-
|
|
254
|
+
// Pan with pointer drag (mouse + touch)
|
|
255
|
+
wrapper.style.cursor = 'grab';
|
|
256
|
+
wrapper.style.touchAction = 'none';
|
|
257
|
+
wrapper.addEventListener('pointerdown', (e) => {
|
|
258
|
+
if (e.pointerType === 'mouse' && e.button !== 0) return;
|
|
87
259
|
state.isPanning = true;
|
|
88
260
|
state.startX = e.clientX - state.translateX;
|
|
89
261
|
state.startY = e.clientY - state.translateY;
|
|
262
|
+
wrapper.setPointerCapture(e.pointerId);
|
|
90
263
|
wrapper.style.cursor = 'grabbing';
|
|
91
264
|
e.preventDefault();
|
|
92
265
|
});
|
|
93
266
|
|
|
94
|
-
|
|
267
|
+
wrapper.addEventListener('pointermove', (e) => {
|
|
95
268
|
if (!state.isPanning) return;
|
|
96
269
|
state.translateX = e.clientX - state.startX;
|
|
97
270
|
state.translateY = e.clientY - state.startY;
|
|
98
271
|
updateTransform();
|
|
272
|
+
if (mermaidDebugEnabled()) {
|
|
273
|
+
mermaidDebugLog('pan update', wrapper.id, {
|
|
274
|
+
translateX: state.translateX,
|
|
275
|
+
translateY: state.translateY,
|
|
276
|
+
scale: state.scale,
|
|
277
|
+
svgTransform: (getSvg() && getSvg().style.transform) || ''
|
|
278
|
+
});
|
|
279
|
+
}
|
|
99
280
|
});
|
|
100
281
|
|
|
101
|
-
|
|
102
|
-
if (state.isPanning)
|
|
103
|
-
|
|
104
|
-
|
|
282
|
+
const stopPanning = (e) => {
|
|
283
|
+
if (!state.isPanning) return;
|
|
284
|
+
state.isPanning = false;
|
|
285
|
+
try {
|
|
286
|
+
wrapper.releasePointerCapture(e.pointerId);
|
|
287
|
+
} catch {
|
|
288
|
+
// Ignore if pointer capture is not active
|
|
105
289
|
}
|
|
106
|
-
|
|
290
|
+
wrapper.style.cursor = 'grab';
|
|
291
|
+
};
|
|
107
292
|
|
|
108
|
-
wrapper.
|
|
293
|
+
wrapper.addEventListener('pointerup', stopPanning);
|
|
294
|
+
wrapper.addEventListener('pointercancel', stopPanning);
|
|
109
295
|
});
|
|
110
296
|
}
|
|
111
297
|
|
|
298
|
+
function scheduleMermaidInteraction({ maxAttempts = 12, delayMs = 80, onReady } = {}) {
|
|
299
|
+
let attempt = 0;
|
|
300
|
+
const check = () => {
|
|
301
|
+
const wrappers = Array.from(document.querySelectorAll('.mermaid-wrapper'));
|
|
302
|
+
const pending = wrappers.filter(wrapper => !wrapper.querySelector('svg'));
|
|
303
|
+
if (mermaidDebugEnabled()) {
|
|
304
|
+
const last = wrappers[wrappers.length - 1];
|
|
305
|
+
mermaidDebugLog('scheduleMermaidInteraction attempt', attempt, 'pending', pending.length);
|
|
306
|
+
if (last) {
|
|
307
|
+
mermaidDebugLog('scheduleMermaidInteraction last wrapper', last.id, 'hasSvg', !!last.querySelector('svg'));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (pending.length === 0 || attempt >= maxAttempts) {
|
|
311
|
+
initMermaidInteraction();
|
|
312
|
+
if (typeof onReady === 'function') {
|
|
313
|
+
onReady();
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
attempt += 1;
|
|
318
|
+
setTimeout(check, delayMs);
|
|
319
|
+
};
|
|
320
|
+
check();
|
|
321
|
+
}
|
|
322
|
+
|
|
112
323
|
window.resetMermaidZoom = function(id) {
|
|
113
324
|
const state = mermaidStates[id];
|
|
114
325
|
if (state) {
|
|
@@ -263,6 +474,11 @@ function reinitializeMermaid() {
|
|
|
263
474
|
});
|
|
264
475
|
|
|
265
476
|
// Find all mermaid wrappers and re-render them
|
|
477
|
+
const shouldLockHeight = (wrapper) => {
|
|
478
|
+
const height = (wrapper.style.height || '').trim();
|
|
479
|
+
return height && height !== 'auto' && height !== 'initial' && height !== 'unset';
|
|
480
|
+
};
|
|
481
|
+
|
|
266
482
|
document.querySelectorAll('.mermaid-wrapper').forEach(wrapper => {
|
|
267
483
|
const originalCode = wrapper.getAttribute('data-mermaid-code');
|
|
268
484
|
if (originalCode) {
|
|
@@ -271,12 +487,15 @@ function reinitializeMermaid() {
|
|
|
271
487
|
console.log('BEFORE clear - wrapper rect:', wrapper.getBoundingClientRect());
|
|
272
488
|
|
|
273
489
|
// Preserve the current computed height before clearing (height should already be set explicitly)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
490
|
+
if (shouldLockHeight(wrapper)) {
|
|
491
|
+
const currentHeight = wrapper.getBoundingClientRect().height;
|
|
492
|
+
console.log('Preserving height:', currentHeight);
|
|
493
|
+
wrapper.style.height = currentHeight + 'px';
|
|
494
|
+
}
|
|
277
495
|
|
|
278
496
|
// Delete the old state so it can be recreated
|
|
279
497
|
delete mermaidStates[wrapper.id];
|
|
498
|
+
delete wrapper.dataset.mermaidInteractive;
|
|
280
499
|
|
|
281
500
|
// Decode HTML entities
|
|
282
501
|
const textarea = document.createElement('textarea');
|
|
@@ -298,11 +517,12 @@ function reinitializeMermaid() {
|
|
|
298
517
|
|
|
299
518
|
// Re-run mermaid
|
|
300
519
|
mermaid.run().then(() => {
|
|
301
|
-
console.log('Mermaid re-render complete,
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
520
|
+
console.log('Mermaid re-render complete, scheduling initMermaidInteraction');
|
|
521
|
+
scheduleMermaidInteraction({
|
|
522
|
+
onReady: () => {
|
|
523
|
+
console.groupEnd();
|
|
524
|
+
}
|
|
525
|
+
});
|
|
306
526
|
});
|
|
307
527
|
}
|
|
308
528
|
|
|
@@ -312,7 +532,7 @@ const initialGanttWidth = getDynamicGanttWidth();
|
|
|
312
532
|
console.log('Using initial Gantt width:', initialGanttWidth);
|
|
313
533
|
|
|
314
534
|
mermaid.initialize({
|
|
315
|
-
startOnLoad:
|
|
535
|
+
startOnLoad: false,
|
|
316
536
|
theme: getCurrentTheme(),
|
|
317
537
|
gantt: {
|
|
318
538
|
useWidth: initialGanttWidth,
|
|
@@ -324,20 +544,32 @@ mermaid.initialize({
|
|
|
324
544
|
let isInitialLoad = true;
|
|
325
545
|
|
|
326
546
|
// Initialize interaction after mermaid renders
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
547
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
548
|
+
mermaidDebugSnapshot('before mermaid.run (DOMContentLoaded)');
|
|
549
|
+
mermaid.run().then(() => {
|
|
550
|
+
mermaidDebugSnapshot('after mermaid.run (DOMContentLoaded)');
|
|
551
|
+
console.log('Initial mermaid render complete');
|
|
552
|
+
scheduleMermaidInteraction({
|
|
553
|
+
onReady: () => {
|
|
554
|
+
console.log('Calling initial initMermaidInteraction');
|
|
555
|
+
|
|
556
|
+
// After initial render, set explicit heights on all wrappers so theme switching works
|
|
557
|
+
const shouldLockHeight = (wrapper) => {
|
|
558
|
+
const height = (wrapper.style.height || '').trim();
|
|
559
|
+
return height && height !== 'auto' && height !== 'initial' && height !== 'unset';
|
|
560
|
+
};
|
|
561
|
+
document.querySelectorAll('.mermaid-wrapper').forEach(wrapper => {
|
|
562
|
+
if (!shouldLockHeight(wrapper)) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const currentHeight = wrapper.getBoundingClientRect().height;
|
|
566
|
+
console.log(`Setting initial height for ${wrapper.id}:`, currentHeight);
|
|
567
|
+
wrapper.style.height = currentHeight + 'px';
|
|
568
|
+
});
|
|
569
|
+
isInitialLoad = false;
|
|
570
|
+
}
|
|
338
571
|
});
|
|
339
|
-
|
|
340
|
-
}, 100);
|
|
572
|
+
});
|
|
341
573
|
});
|
|
342
574
|
|
|
343
575
|
// Reveal current file in sidebar
|
|
@@ -346,7 +578,8 @@ function revealInSidebar(rootElement = document) {
|
|
|
346
578
|
return;
|
|
347
579
|
}
|
|
348
580
|
|
|
349
|
-
|
|
581
|
+
// Decode the URL path to handle special characters and spaces
|
|
582
|
+
const currentPath = decodeURIComponent(window.location.pathname.replace(/^\/posts\//, ''));
|
|
350
583
|
const activeLink = rootElement.querySelector(`.post-link[data-path="${currentPath}"]`);
|
|
351
584
|
|
|
352
585
|
if (activeLink) {
|
|
@@ -390,11 +623,18 @@ function revealInSidebar(rootElement = document) {
|
|
|
390
623
|
|
|
391
624
|
function initPostsSidebarAutoReveal() {
|
|
392
625
|
const postSidebars = document.querySelectorAll('details[data-sidebar="posts"]');
|
|
626
|
+
|
|
393
627
|
postSidebars.forEach((sidebar) => {
|
|
394
628
|
if (sidebar.dataset.revealBound === 'true') {
|
|
395
629
|
return;
|
|
396
630
|
}
|
|
397
631
|
sidebar.dataset.revealBound = 'true';
|
|
632
|
+
|
|
633
|
+
// Reveal immediately if sidebar is already open
|
|
634
|
+
if (sidebar.open) {
|
|
635
|
+
revealInSidebar(sidebar);
|
|
636
|
+
}
|
|
637
|
+
|
|
398
638
|
sidebar.addEventListener('toggle', () => {
|
|
399
639
|
if (!sidebar.open) {
|
|
400
640
|
return;
|
|
@@ -410,6 +650,214 @@ function initFolderChevronState(rootElement = document) {
|
|
|
410
650
|
});
|
|
411
651
|
}
|
|
412
652
|
|
|
653
|
+
function initSearchPlaceholderCycle(rootElement = document) {
|
|
654
|
+
const inputs = rootElement.querySelectorAll('input[data-placeholder-cycle]');
|
|
655
|
+
inputs.forEach((input) => {
|
|
656
|
+
if (input.dataset.placeholderCycleBound === 'true') {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
input.dataset.placeholderCycleBound = 'true';
|
|
660
|
+
const primary = input.dataset.placeholderPrimary || input.getAttribute('placeholder') || '';
|
|
661
|
+
const alt = input.dataset.placeholderAlt || '';
|
|
662
|
+
if (!alt) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
let showAlt = false;
|
|
666
|
+
setInterval(() => {
|
|
667
|
+
if (input.value) {
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
showAlt = !showAlt;
|
|
671
|
+
input.setAttribute('placeholder', showAlt ? alt : primary);
|
|
672
|
+
}, 10000);
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function initCodeBlockCopyButtons(rootElement = document) {
|
|
677
|
+
const buttons = rootElement.querySelectorAll('.code-copy-button');
|
|
678
|
+
buttons.forEach((button) => {
|
|
679
|
+
if (button.dataset.copyBound === 'true') {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
button.dataset.copyBound = 'true';
|
|
683
|
+
button.addEventListener('click', () => {
|
|
684
|
+
const container = button.closest('.code-block');
|
|
685
|
+
const codeEl = container ? container.querySelector('pre > code') : null;
|
|
686
|
+
if (!codeEl) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const text = codeEl.innerText || codeEl.textContent || '';
|
|
690
|
+
const done = () => {
|
|
691
|
+
button.classList.add('is-copied');
|
|
692
|
+
setTimeout(() => button.classList.remove('is-copied'), 1200);
|
|
693
|
+
};
|
|
694
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
695
|
+
navigator.clipboard.writeText(text).then(done).catch(() => {
|
|
696
|
+
const textarea = document.createElement('textarea');
|
|
697
|
+
textarea.value = text;
|
|
698
|
+
textarea.setAttribute('readonly', '');
|
|
699
|
+
textarea.style.position = 'absolute';
|
|
700
|
+
textarea.style.left = '-9999px';
|
|
701
|
+
document.body.appendChild(textarea);
|
|
702
|
+
textarea.select();
|
|
703
|
+
document.execCommand('copy');
|
|
704
|
+
document.body.removeChild(textarea);
|
|
705
|
+
done();
|
|
706
|
+
});
|
|
707
|
+
} else {
|
|
708
|
+
const textarea = document.createElement('textarea');
|
|
709
|
+
textarea.value = text;
|
|
710
|
+
textarea.setAttribute('readonly', '');
|
|
711
|
+
textarea.style.position = 'absolute';
|
|
712
|
+
textarea.style.left = '-9999px';
|
|
713
|
+
document.body.appendChild(textarea);
|
|
714
|
+
textarea.select();
|
|
715
|
+
document.execCommand('copy');
|
|
716
|
+
document.body.removeChild(textarea);
|
|
717
|
+
done();
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function initPostsSearchPersistence(rootElement = document) {
|
|
724
|
+
const input = rootElement.querySelector('.posts-search-block input[type="search"][name="q"]');
|
|
725
|
+
const results = rootElement.querySelector('.posts-search-results');
|
|
726
|
+
if (!input || !results) {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (input.dataset.searchPersistenceBound === 'true') {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
input.dataset.searchPersistenceBound = 'true';
|
|
733
|
+
const termKey = 'bloggy:postsSearchTerm';
|
|
734
|
+
const resultsKey = 'bloggy:postsSearchResults';
|
|
735
|
+
const enhanceGatherLink = () => {
|
|
736
|
+
const gatherLink = results.querySelector('a[href^="/search/gather"]');
|
|
737
|
+
if (!gatherLink) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const href = gatherLink.getAttribute('href');
|
|
741
|
+
if (!href) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
gatherLink.setAttribute('hx_get', href);
|
|
745
|
+
gatherLink.setAttribute('hx_target', '#main-content');
|
|
746
|
+
gatherLink.setAttribute('hx_push_url', 'true');
|
|
747
|
+
gatherLink.setAttribute('hx_swap', 'outerHTML show:window:top settle:0.1s');
|
|
748
|
+
};
|
|
749
|
+
let storedTerm = '';
|
|
750
|
+
let storedResults = null;
|
|
751
|
+
try {
|
|
752
|
+
storedTerm = localStorage.getItem(termKey) || '';
|
|
753
|
+
storedResults = localStorage.getItem(resultsKey);
|
|
754
|
+
} catch (err) {
|
|
755
|
+
storedTerm = '';
|
|
756
|
+
storedResults = null;
|
|
757
|
+
}
|
|
758
|
+
if (storedTerm && !input.value) {
|
|
759
|
+
input.value = storedTerm;
|
|
760
|
+
}
|
|
761
|
+
if (storedResults && input.value) {
|
|
762
|
+
try {
|
|
763
|
+
const payload = JSON.parse(storedResults);
|
|
764
|
+
if (payload && payload.term === input.value && payload.html) {
|
|
765
|
+
results.innerHTML = payload.html;
|
|
766
|
+
enhanceGatherLink();
|
|
767
|
+
}
|
|
768
|
+
} catch (err) {
|
|
769
|
+
// Ignore malformed cached payloads.
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
const persistTerm = () => {
|
|
773
|
+
try {
|
|
774
|
+
if (input.value) {
|
|
775
|
+
localStorage.setItem(termKey, input.value);
|
|
776
|
+
} else {
|
|
777
|
+
localStorage.removeItem(termKey);
|
|
778
|
+
localStorage.removeItem(resultsKey);
|
|
779
|
+
}
|
|
780
|
+
} catch (err) {
|
|
781
|
+
// Ignore storage failures.
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
input.addEventListener('input', persistTerm);
|
|
785
|
+
const fetchResults = (query) => {
|
|
786
|
+
return fetch(`/_sidebar/posts/search?q=${query}`)
|
|
787
|
+
.then((response) => response.text())
|
|
788
|
+
.then((html) => {
|
|
789
|
+
results.innerHTML = html;
|
|
790
|
+
enhanceGatherLink();
|
|
791
|
+
try {
|
|
792
|
+
localStorage.setItem(resultsKey, JSON.stringify({
|
|
793
|
+
term: input.value,
|
|
794
|
+
html: results.innerHTML
|
|
795
|
+
}));
|
|
796
|
+
} catch (err) {
|
|
797
|
+
// Ignore storage failures.
|
|
798
|
+
}
|
|
799
|
+
})
|
|
800
|
+
.catch(() => {});
|
|
801
|
+
};
|
|
802
|
+
document.body.addEventListener('htmx:afterSwap', (event) => {
|
|
803
|
+
if (event.target !== results) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
enhanceGatherLink();
|
|
807
|
+
try {
|
|
808
|
+
localStorage.setItem(resultsKey, JSON.stringify({
|
|
809
|
+
term: input.value,
|
|
810
|
+
html: results.innerHTML
|
|
811
|
+
}));
|
|
812
|
+
} catch (err) {
|
|
813
|
+
// Ignore storage failures.
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
if (input.value) {
|
|
817
|
+
const query = encodeURIComponent(input.value);
|
|
818
|
+
if (window.htmx && typeof window.htmx.ajax === 'function') {
|
|
819
|
+
window.htmx.ajax('GET', `/_sidebar/posts/search?q=${query}`, { target: results, swap: 'innerHTML' });
|
|
820
|
+
} else {
|
|
821
|
+
fetchResults(query);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function initSearchClearButtons(rootElement = document) {
|
|
827
|
+
const blocks = rootElement.querySelectorAll('.posts-search-block');
|
|
828
|
+
blocks.forEach((block) => {
|
|
829
|
+
const input = block.querySelector('input[type="search"][name="q"]');
|
|
830
|
+
const button = block.querySelector('.posts-search-clear-button');
|
|
831
|
+
const results = block.querySelector('.posts-search-results');
|
|
832
|
+
if (!input || !button) {
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (button.dataset.clearBound === 'true') {
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
button.dataset.clearBound = 'true';
|
|
839
|
+
const updateVisibility = () => {
|
|
840
|
+
button.style.opacity = input.value ? '1' : '0';
|
|
841
|
+
button.style.pointerEvents = input.value ? 'auto' : 'none';
|
|
842
|
+
};
|
|
843
|
+
updateVisibility();
|
|
844
|
+
input.addEventListener('input', updateVisibility);
|
|
845
|
+
button.addEventListener('click', () => {
|
|
846
|
+
input.value = '';
|
|
847
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
848
|
+
if (results) {
|
|
849
|
+
results.innerHTML = '';
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
localStorage.removeItem('bloggy:postsSearchTerm');
|
|
853
|
+
localStorage.removeItem('bloggy:postsSearchResults');
|
|
854
|
+
} catch (err) {
|
|
855
|
+
// Ignore storage failures.
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
|
|
413
861
|
document.addEventListener('toggle', (event) => {
|
|
414
862
|
const details = event.target;
|
|
415
863
|
if (!(details instanceof HTMLDetailsElement)) {
|
|
@@ -437,17 +885,27 @@ function updateActivePostLink() {
|
|
|
437
885
|
}
|
|
438
886
|
|
|
439
887
|
// Update active TOC link based on scroll position
|
|
888
|
+
let lastActiveTocAnchor = null;
|
|
440
889
|
function updateActiveTocLink() {
|
|
441
890
|
const headings = document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]');
|
|
442
891
|
const tocLinks = document.querySelectorAll('.toc-link');
|
|
443
892
|
|
|
444
893
|
let activeHeading = null;
|
|
894
|
+
let nearestBelow = null;
|
|
895
|
+
let nearestBelowTop = Infinity;
|
|
896
|
+
const offset = 140;
|
|
445
897
|
headings.forEach(heading => {
|
|
446
898
|
const rect = heading.getBoundingClientRect();
|
|
447
|
-
if (rect.top <=
|
|
899
|
+
if (rect.top <= offset) {
|
|
448
900
|
activeHeading = heading;
|
|
901
|
+
} else if (rect.top < nearestBelowTop) {
|
|
902
|
+
nearestBelowTop = rect.top;
|
|
903
|
+
nearestBelow = heading;
|
|
449
904
|
}
|
|
450
905
|
});
|
|
906
|
+
if (!activeHeading && nearestBelow) {
|
|
907
|
+
activeHeading = nearestBelow;
|
|
908
|
+
}
|
|
451
909
|
|
|
452
910
|
tocLinks.forEach(link => {
|
|
453
911
|
const anchor = link.getAttribute('data-anchor');
|
|
@@ -457,6 +915,14 @@ function updateActiveTocLink() {
|
|
|
457
915
|
link.classList.remove('bg-blue-50', 'dark:bg-blue-900/20', 'text-blue-600', 'dark:text-blue-400', 'font-semibold');
|
|
458
916
|
}
|
|
459
917
|
});
|
|
918
|
+
|
|
919
|
+
const activeId = activeHeading ? activeHeading.id : null;
|
|
920
|
+
if (activeId && activeId !== lastActiveTocAnchor) {
|
|
921
|
+
document.querySelectorAll(`.toc-link[data-anchor="${activeId}"]`).forEach(link => {
|
|
922
|
+
link.scrollIntoView({ block: 'nearest' });
|
|
923
|
+
});
|
|
924
|
+
lastActiveTocAnchor = activeId;
|
|
925
|
+
}
|
|
460
926
|
}
|
|
461
927
|
|
|
462
928
|
// Listen for scroll events to update active TOC link
|
|
@@ -471,16 +937,71 @@ window.addEventListener('scroll', () => {
|
|
|
471
937
|
}
|
|
472
938
|
});
|
|
473
939
|
|
|
940
|
+
// Sync TOC highlight on hash changes and TOC clicks
|
|
941
|
+
window.addEventListener('hashchange', () => {
|
|
942
|
+
requestAnimationFrame(updateActiveTocLink);
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
document.addEventListener('click', (event) => {
|
|
946
|
+
const link = event.target.closest('.toc-link');
|
|
947
|
+
if (!link) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const anchor = link.getAttribute('data-anchor');
|
|
951
|
+
if (!anchor) {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
requestAnimationFrame(() => {
|
|
955
|
+
document.querySelectorAll('.toc-link').forEach(item => {
|
|
956
|
+
item.classList.toggle(
|
|
957
|
+
'bg-blue-50',
|
|
958
|
+
item.getAttribute('data-anchor') === anchor
|
|
959
|
+
);
|
|
960
|
+
item.classList.toggle(
|
|
961
|
+
'dark:bg-blue-900/20',
|
|
962
|
+
item.getAttribute('data-anchor') === anchor
|
|
963
|
+
);
|
|
964
|
+
item.classList.toggle(
|
|
965
|
+
'text-blue-600',
|
|
966
|
+
item.getAttribute('data-anchor') === anchor
|
|
967
|
+
);
|
|
968
|
+
item.classList.toggle(
|
|
969
|
+
'dark:text-blue-400',
|
|
970
|
+
item.getAttribute('data-anchor') === anchor
|
|
971
|
+
);
|
|
972
|
+
item.classList.toggle(
|
|
973
|
+
'font-semibold',
|
|
974
|
+
item.getAttribute('data-anchor') === anchor
|
|
975
|
+
);
|
|
976
|
+
});
|
|
977
|
+
lastActiveTocAnchor = anchor;
|
|
978
|
+
updateActiveTocLink();
|
|
979
|
+
});
|
|
980
|
+
});
|
|
981
|
+
|
|
474
982
|
// Re-run mermaid on HTMX content swaps
|
|
475
|
-
document.body.addEventListener('htmx:afterSwap', function() {
|
|
983
|
+
document.body.addEventListener('htmx:afterSwap', function(event) {
|
|
984
|
+
mermaidDebugSnapshot('before mermaid.run (htmx:afterSwap)');
|
|
985
|
+
document.querySelectorAll('.mermaid-wrapper').forEach(wrapper => {
|
|
986
|
+
if (!wrapper.id) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
// HTMX swaps can trigger a mermaid re-run that replaces SVGs.
|
|
990
|
+
// Clear interaction state so we always re-bind after mermaid.run().
|
|
991
|
+
delete mermaidStates[wrapper.id];
|
|
992
|
+
delete wrapper.dataset.mermaidInteractive;
|
|
993
|
+
});
|
|
476
994
|
mermaid.run().then(() => {
|
|
477
|
-
|
|
995
|
+
mermaidDebugSnapshot('after mermaid.run (htmx:afterSwap)');
|
|
996
|
+
scheduleMermaidInteraction();
|
|
478
997
|
});
|
|
479
998
|
updateActivePostLink();
|
|
480
999
|
updateActiveTocLink();
|
|
481
1000
|
initMobileMenus(); // Reinitialize mobile menu handlers
|
|
482
1001
|
initPostsSidebarAutoReveal();
|
|
483
1002
|
initFolderChevronState();
|
|
1003
|
+
initSearchPlaceholderCycle(event.target || document);
|
|
1004
|
+
initCodeBlockCopyButtons(event.target || document);
|
|
484
1005
|
});
|
|
485
1006
|
|
|
486
1007
|
// Watch for theme changes and re-render mermaid diagrams
|
|
@@ -580,6 +1101,79 @@ function initMobileMenus() {
|
|
|
580
1101
|
}
|
|
581
1102
|
}
|
|
582
1103
|
|
|
1104
|
+
// Keyboard shortcuts for toggling sidebars
|
|
1105
|
+
function initKeyboardShortcuts() {
|
|
1106
|
+
// Prewarm the selectors to avoid lazy compilation delays
|
|
1107
|
+
const postsSidebars = document.querySelectorAll('details[data-sidebar="posts"]');
|
|
1108
|
+
const tocSidebar = document.querySelector('#toc-sidebar details');
|
|
1109
|
+
|
|
1110
|
+
document.addEventListener('keydown', (e) => {
|
|
1111
|
+
// Skip if user is typing in an input field
|
|
1112
|
+
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// Z: Toggle posts panel
|
|
1117
|
+
if (e.key === 'z' || e.key === 'Z') {
|
|
1118
|
+
e.preventDefault();
|
|
1119
|
+
const postsSidebars = document.querySelectorAll('details[data-sidebar="posts"]');
|
|
1120
|
+
postsSidebars.forEach(sidebar => {
|
|
1121
|
+
sidebar.open = !sidebar.open;
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// X: Toggle TOC panel
|
|
1126
|
+
if (e.key === 'x' || e.key === 'X') {
|
|
1127
|
+
e.preventDefault();
|
|
1128
|
+
const tocSidebar = document.querySelector('#toc-sidebar details');
|
|
1129
|
+
if (tocSidebar) {
|
|
1130
|
+
tocSidebar.open = !tocSidebar.open;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function syncPdfFocusButtons(root = document) {
|
|
1137
|
+
const isFocused = document.body.classList.contains('pdf-focus');
|
|
1138
|
+
root.querySelectorAll('[data-pdf-focus-toggle]').forEach((button) => {
|
|
1139
|
+
const focusLabel = button.getAttribute('data-pdf-focus-label') || 'Focus PDF';
|
|
1140
|
+
const exitLabel = button.getAttribute('data-pdf-exit-label') || 'Exit focus';
|
|
1141
|
+
button.textContent = isFocused ? exitLabel : focusLabel;
|
|
1142
|
+
button.setAttribute('aria-pressed', isFocused ? 'true' : 'false');
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function ensurePdfFocusState() {
|
|
1147
|
+
const hasPdfViewer = document.querySelector('.pdf-viewer') || document.querySelector('[data-pdf-focus-toggle]');
|
|
1148
|
+
if (!hasPdfViewer) {
|
|
1149
|
+
document.body.classList.remove('pdf-focus');
|
|
1150
|
+
}
|
|
1151
|
+
syncPdfFocusButtons(document);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function initPdfFocusToggle() {
|
|
1155
|
+
document.addEventListener('click', (event) => {
|
|
1156
|
+
const button = event.target.closest('[data-pdf-focus-toggle]');
|
|
1157
|
+
if (!button) {
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
event.preventDefault();
|
|
1161
|
+
document.body.classList.toggle('pdf-focus');
|
|
1162
|
+
syncPdfFocusButtons(document);
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
document.addEventListener('keydown', (event) => {
|
|
1166
|
+
if (event.key !== 'Escape') {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
if (!document.body.classList.contains('pdf-focus')) {
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
document.body.classList.remove('pdf-focus');
|
|
1173
|
+
syncPdfFocusButtons(document);
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
|
|
583
1177
|
// Initialize on page load
|
|
584
1178
|
document.addEventListener('DOMContentLoaded', () => {
|
|
585
1179
|
updateActivePostLink();
|
|
@@ -587,4 +1181,22 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
587
1181
|
initMobileMenus();
|
|
588
1182
|
initPostsSidebarAutoReveal();
|
|
589
1183
|
initFolderChevronState();
|
|
1184
|
+
initKeyboardShortcuts();
|
|
1185
|
+
initPdfFocusToggle();
|
|
1186
|
+
initSearchPlaceholderCycle(document);
|
|
1187
|
+
initPostsSearchPersistence(document);
|
|
1188
|
+
initCodeBlockCopyButtons(document);
|
|
1189
|
+
initSearchClearButtons(document);
|
|
1190
|
+
ensurePdfFocusState();
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
document.body.addEventListener('htmx:afterSwap', (event) => {
|
|
1194
|
+
if (!event.target) {
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
initSearchPlaceholderCycle(event.target);
|
|
1198
|
+
initPostsSearchPersistence(event.target);
|
|
1199
|
+
initCodeBlockCopyButtons(event.target);
|
|
1200
|
+
initSearchClearButtons(event.target);
|
|
1201
|
+
ensurePdfFocusState();
|
|
590
1202
|
});
|