nodebb-plugin-pdf-secure2 1.4.5 → 1.4.6
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.
- package/package.json +1 -1
- package/static/lib/main.js +113 -1
- package/static/viewer-app.js +65 -10
- package/static/viewer.html +156 -24
package/package.json
CHANGED
package/static/lib/main.js
CHANGED
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
document.documentElement.webkitRequestFullscreen
|
|
34
34
|
);
|
|
35
35
|
var simulatedFullscreenIframe = null;
|
|
36
|
+
var windowedModeIframe = null;
|
|
37
|
+
var windowedBackdrop = null;
|
|
36
38
|
var savedBodyOverflow = '';
|
|
37
39
|
|
|
38
40
|
// Loading queue - only load one PDF at a time
|
|
@@ -103,6 +105,9 @@
|
|
|
103
105
|
});
|
|
104
106
|
if (!sourceIframe) return;
|
|
105
107
|
|
|
108
|
+
// Exit windowed mode first if active
|
|
109
|
+
if (windowedModeIframe) exitWindowedMode();
|
|
110
|
+
|
|
106
111
|
if (fullscreenApiSupported) {
|
|
107
112
|
// Native fullscreen path
|
|
108
113
|
var fsEl = document.fullscreenElement || document.webkitFullscreenElement;
|
|
@@ -123,6 +128,28 @@
|
|
|
123
128
|
}
|
|
124
129
|
}
|
|
125
130
|
|
|
131
|
+
// Windowed mode toggle request from iframe viewer
|
|
132
|
+
if (event.data && event.data.type === 'pdf-secure-windowed-toggle') {
|
|
133
|
+
var sourceIframe = document.querySelector('.pdf-secure-iframe');
|
|
134
|
+
document.querySelectorAll('.pdf-secure-iframe').forEach(function (f) {
|
|
135
|
+
if (f.contentWindow === event.source) sourceIframe = f;
|
|
136
|
+
});
|
|
137
|
+
if (!sourceIframe) return;
|
|
138
|
+
|
|
139
|
+
if (windowedModeIframe) {
|
|
140
|
+
exitWindowedMode();
|
|
141
|
+
} else {
|
|
142
|
+
// Exit fullscreen/simulated fullscreen first if active
|
|
143
|
+
var fsEl = document.fullscreenElement || document.webkitFullscreenElement;
|
|
144
|
+
if (fsEl) {
|
|
145
|
+
if (document.exitFullscreen) document.exitFullscreen();
|
|
146
|
+
else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
|
|
147
|
+
}
|
|
148
|
+
if (simulatedFullscreenIframe) exitSimulatedFullscreen();
|
|
149
|
+
enterWindowedMode(sourceIframe);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
126
153
|
// Fullscreen state query from iframe
|
|
127
154
|
if (event.data && event.data.type === 'pdf-secure-fullscreen-query') {
|
|
128
155
|
var fsActive = !!(document.fullscreenElement || document.webkitFullscreenElement) ||
|
|
@@ -270,6 +297,90 @@
|
|
|
270
297
|
}
|
|
271
298
|
}
|
|
272
299
|
|
|
300
|
+
// Enter windowed overlay mode (large overlay without hiding browser chrome/tabs)
|
|
301
|
+
function enterWindowedMode(iframe) {
|
|
302
|
+
if (windowedModeIframe) return;
|
|
303
|
+
windowedModeIframe = iframe;
|
|
304
|
+
|
|
305
|
+
// Save original styles
|
|
306
|
+
iframe._savedWindowedStyle = {
|
|
307
|
+
position: iframe.style.position,
|
|
308
|
+
top: iframe.style.top,
|
|
309
|
+
left: iframe.style.left,
|
|
310
|
+
width: iframe.style.width,
|
|
311
|
+
height: iframe.style.height,
|
|
312
|
+
zIndex: iframe.style.zIndex,
|
|
313
|
+
borderRadius: iframe.style.borderRadius,
|
|
314
|
+
boxShadow: iframe.style.boxShadow
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// Create backdrop
|
|
318
|
+
windowedBackdrop = document.createElement('div');
|
|
319
|
+
windowedBackdrop.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);z-index:2147483646;transition:opacity 0.2s;';
|
|
320
|
+
windowedBackdrop.onclick = function () { exitWindowedMode(); };
|
|
321
|
+
document.body.appendChild(windowedBackdrop);
|
|
322
|
+
|
|
323
|
+
// Position iframe as overlay with margins (browser tabs stay visible)
|
|
324
|
+
iframe.style.position = 'fixed';
|
|
325
|
+
iframe.style.top = '24px';
|
|
326
|
+
iframe.style.left = '24px';
|
|
327
|
+
iframe.style.width = 'calc(100vw - 48px)';
|
|
328
|
+
iframe.style.height = 'calc(100vh - 48px)';
|
|
329
|
+
iframe.style.zIndex = '2147483647';
|
|
330
|
+
iframe.style.borderRadius = '12px';
|
|
331
|
+
iframe.style.boxShadow = '0 8px 48px rgba(0,0,0,0.5)';
|
|
332
|
+
|
|
333
|
+
// Lock body scroll
|
|
334
|
+
savedBodyOverflow = document.body.style.overflow;
|
|
335
|
+
document.body.style.overflow = 'hidden';
|
|
336
|
+
|
|
337
|
+
// Notify iframe
|
|
338
|
+
if (iframe.contentWindow) {
|
|
339
|
+
iframe.contentWindow.postMessage({
|
|
340
|
+
type: 'pdf-secure-windowed-state',
|
|
341
|
+
isWindowed: true
|
|
342
|
+
}, window.location.origin);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Exit windowed overlay mode
|
|
347
|
+
function exitWindowedMode() {
|
|
348
|
+
if (!windowedModeIframe) return;
|
|
349
|
+
var iframe = windowedModeIframe;
|
|
350
|
+
windowedModeIframe = null;
|
|
351
|
+
|
|
352
|
+
// Restore original styles
|
|
353
|
+
if (iframe._savedWindowedStyle) {
|
|
354
|
+
iframe.style.position = iframe._savedWindowedStyle.position;
|
|
355
|
+
iframe.style.top = iframe._savedWindowedStyle.top;
|
|
356
|
+
iframe.style.left = iframe._savedWindowedStyle.left;
|
|
357
|
+
iframe.style.width = iframe._savedWindowedStyle.width;
|
|
358
|
+
iframe.style.height = iframe._savedWindowedStyle.height;
|
|
359
|
+
iframe.style.zIndex = iframe._savedWindowedStyle.zIndex;
|
|
360
|
+
iframe.style.borderRadius = iframe._savedWindowedStyle.borderRadius;
|
|
361
|
+
iframe.style.boxShadow = iframe._savedWindowedStyle.boxShadow;
|
|
362
|
+
delete iframe._savedWindowedStyle;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Remove backdrop
|
|
366
|
+
if (windowedBackdrop && windowedBackdrop.parentNode) {
|
|
367
|
+
windowedBackdrop.remove();
|
|
368
|
+
windowedBackdrop = null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Restore body scroll
|
|
372
|
+
document.body.style.overflow = savedBodyOverflow;
|
|
373
|
+
savedBodyOverflow = '';
|
|
374
|
+
|
|
375
|
+
// Notify iframe
|
|
376
|
+
if (iframe.contentWindow) {
|
|
377
|
+
iframe.contentWindow.postMessage({
|
|
378
|
+
type: 'pdf-secure-windowed-state',
|
|
379
|
+
isWindowed: false
|
|
380
|
+
}, window.location.origin);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
273
384
|
async function processQueue() {
|
|
274
385
|
if (isLoading || loadQueue.length === 0) return;
|
|
275
386
|
|
|
@@ -302,8 +413,9 @@
|
|
|
302
413
|
currentResolver = null;
|
|
303
414
|
// Clear decrypted PDF buffer cache on navigation
|
|
304
415
|
pdfBufferCache.clear();
|
|
305
|
-
// Exit simulated fullscreen on SPA navigation
|
|
416
|
+
// Exit simulated fullscreen / windowed mode on SPA navigation
|
|
306
417
|
exitSimulatedFullscreen();
|
|
418
|
+
exitWindowedMode();
|
|
307
419
|
interceptPdfLinks();
|
|
308
420
|
});
|
|
309
421
|
} catch (err) {
|
package/static/viewer-app.js
CHANGED
|
@@ -687,12 +687,14 @@
|
|
|
687
687
|
|
|
688
688
|
const dropdownBackdrop = document.getElementById('dropdownBackdrop');
|
|
689
689
|
const overflowDropdown = document.getElementById('overflowDropdown');
|
|
690
|
+
const fullscreenDropdown = document.getElementById('fullscreenDropdown');
|
|
690
691
|
|
|
691
692
|
function closeAllDropdowns() {
|
|
692
693
|
highlightDropdown.classList.remove('visible');
|
|
693
694
|
drawDropdown.classList.remove('visible');
|
|
694
695
|
shapesDropdown.classList.remove('visible');
|
|
695
696
|
overflowDropdown.classList.remove('visible');
|
|
697
|
+
fullscreenDropdown.classList.remove('visible');
|
|
696
698
|
dropdownBackdrop.classList.remove('visible');
|
|
697
699
|
}
|
|
698
700
|
|
|
@@ -726,6 +728,11 @@
|
|
|
726
728
|
document.getElementById('drawArrow').onclick = (e) => toggleDropdown(drawDropdown, e);
|
|
727
729
|
document.getElementById('shapesArrow').onclick = (e) => toggleDropdown(shapesDropdown, e);
|
|
728
730
|
|
|
731
|
+
// Fullscreen dropdown toggle
|
|
732
|
+
document.getElementById('fullscreenArrow').onclick = (e) => toggleDropdown(fullscreenDropdown, e);
|
|
733
|
+
fullscreenDropdown.addEventListener('click', (e) => e.stopPropagation());
|
|
734
|
+
fullscreenDropdown.addEventListener('pointerup', (e) => e.stopPropagation());
|
|
735
|
+
|
|
729
736
|
// Overflow menu toggle
|
|
730
737
|
document.getElementById('overflowBtn').onclick = (e) => toggleDropdown(overflowDropdown, e);
|
|
731
738
|
overflowDropdown.addEventListener('click', (e) => e.stopPropagation());
|
|
@@ -762,9 +769,27 @@
|
|
|
762
769
|
closeAllDropdowns();
|
|
763
770
|
});
|
|
764
771
|
attachOverflowAction('overflowFullscreen', () => {
|
|
772
|
+
if (isWindowed) toggleWindowed();
|
|
773
|
+
toggleFullscreen();
|
|
774
|
+
closeAllDropdowns();
|
|
775
|
+
});
|
|
776
|
+
attachOverflowAction('overflowWindowed', () => {
|
|
777
|
+
if (isFullscreen) toggleFullscreen();
|
|
778
|
+
toggleWindowed();
|
|
779
|
+
closeAllDropdowns();
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
// Fullscreen dropdown options
|
|
783
|
+
attachOverflowAction('fullscreenOptionFull', () => {
|
|
784
|
+
if (isWindowed) toggleWindowed();
|
|
765
785
|
toggleFullscreen();
|
|
766
786
|
closeAllDropdowns();
|
|
767
787
|
});
|
|
788
|
+
attachOverflowAction('fullscreenOptionWindowed', () => {
|
|
789
|
+
if (isFullscreen) toggleFullscreen();
|
|
790
|
+
toggleWindowed();
|
|
791
|
+
closeAllDropdowns();
|
|
792
|
+
});
|
|
768
793
|
|
|
769
794
|
// Close dropdowns when clicking outside
|
|
770
795
|
document.addEventListener('click', (e) => {
|
|
@@ -2487,7 +2512,7 @@
|
|
|
2487
2512
|
const isLiteMode = _cfg && _cfg.isLite;
|
|
2488
2513
|
|
|
2489
2514
|
// Fullscreen shortcut (always allowed)
|
|
2490
|
-
if (key === 'f') { toggleFullscreen(); e.preventDefault(); }
|
|
2515
|
+
if (key === 'f') { if (isWindowed) toggleWindowed(); else toggleFullscreen(); e.preventDefault(); }
|
|
2491
2516
|
|
|
2492
2517
|
// Tool shortcuts (blocked in Lite mode)
|
|
2493
2518
|
if (!isLiteMode) {
|
|
@@ -2577,9 +2602,11 @@
|
|
|
2577
2602
|
return;
|
|
2578
2603
|
}
|
|
2579
2604
|
|
|
2580
|
-
// Escape to deselect tool
|
|
2605
|
+
// Escape to deselect tool / exit windowed mode
|
|
2581
2606
|
if (key === 'escape') {
|
|
2582
|
-
if (
|
|
2607
|
+
if (isWindowed) {
|
|
2608
|
+
toggleWindowed();
|
|
2609
|
+
} else if (currentTool) {
|
|
2583
2610
|
setTool(currentTool); // Toggle off
|
|
2584
2611
|
}
|
|
2585
2612
|
closeAllDropdowns();
|
|
@@ -2669,8 +2696,9 @@
|
|
|
2669
2696
|
// ==========================================
|
|
2670
2697
|
|
|
2671
2698
|
|
|
2672
|
-
// Fullscreen state (tracks
|
|
2699
|
+
// Fullscreen & Windowed state (tracks native, simulated fullscreen, and windowed overlay)
|
|
2673
2700
|
let isFullscreen = false;
|
|
2701
|
+
let isWindowed = false;
|
|
2674
2702
|
|
|
2675
2703
|
// Fullscreen toggle function — delegates to parent iframe handler via postMessage
|
|
2676
2704
|
function toggleFullscreen() {
|
|
@@ -2687,23 +2715,41 @@
|
|
|
2687
2715
|
}
|
|
2688
2716
|
}
|
|
2689
2717
|
|
|
2718
|
+
// Windowed mode toggle — delegates to parent
|
|
2719
|
+
function toggleWindowed() {
|
|
2720
|
+
if (window.self !== window.top) {
|
|
2721
|
+
window.parent.postMessage({ type: 'pdf-secure-windowed-toggle' }, window.location.origin);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2690
2725
|
// Update fullscreen button icon
|
|
2691
2726
|
function updateFullscreenIcon() {
|
|
2692
2727
|
const icon = document.getElementById('fullscreenIcon');
|
|
2693
2728
|
const btn = document.getElementById('fullscreenBtn');
|
|
2729
|
+
const exitPath = '<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>';
|
|
2730
|
+
const enterPath = '<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>';
|
|
2731
|
+
const windowedPath = '<path d="M19 4H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V8h14v12z"/>';
|
|
2694
2732
|
if (isFullscreen) {
|
|
2695
|
-
icon.innerHTML =
|
|
2733
|
+
icon.innerHTML = exitPath;
|
|
2734
|
+
btn.classList.add('active');
|
|
2735
|
+
} else if (isWindowed) {
|
|
2736
|
+
icon.innerHTML = windowedPath;
|
|
2696
2737
|
btn.classList.add('active');
|
|
2697
2738
|
} else {
|
|
2698
|
-
icon.innerHTML =
|
|
2739
|
+
icon.innerHTML = enterPath;
|
|
2699
2740
|
btn.classList.remove('active');
|
|
2700
2741
|
}
|
|
2742
|
+
// Highlight active option in dropdown
|
|
2743
|
+
const optFull = document.getElementById('fullscreenOptionFull');
|
|
2744
|
+
const optWindowed = document.getElementById('fullscreenOptionWindowed');
|
|
2745
|
+
if (optFull) optFull.classList.toggle('active', isFullscreen);
|
|
2746
|
+
if (optWindowed) optWindowed.classList.toggle('active', isWindowed);
|
|
2701
2747
|
}
|
|
2702
2748
|
|
|
2703
2749
|
// Apply fullscreen CSS class and touch restrictions
|
|
2704
2750
|
function applyFullscreenTouchRestrictions() {
|
|
2705
|
-
document.body.classList.toggle('viewer-fullscreen', isFullscreen);
|
|
2706
|
-
if (isFullscreen) {
|
|
2751
|
+
document.body.classList.toggle('viewer-fullscreen', isFullscreen || isWindowed);
|
|
2752
|
+
if (isFullscreen || isWindowed) {
|
|
2707
2753
|
document.documentElement.style.touchAction = 'pan-y pinch-zoom';
|
|
2708
2754
|
document.documentElement.style.overscrollBehavior = 'none';
|
|
2709
2755
|
} else {
|
|
@@ -2712,13 +2758,18 @@
|
|
|
2712
2758
|
}
|
|
2713
2759
|
}
|
|
2714
2760
|
|
|
2715
|
-
// Listen for fullscreen state from parent (
|
|
2761
|
+
// Listen for fullscreen/windowed state from parent (iframe mode)
|
|
2716
2762
|
window.addEventListener('message', (event) => {
|
|
2717
2763
|
if (event.data && event.data.type === 'pdf-secure-fullscreen-state') {
|
|
2718
2764
|
isFullscreen = event.data.isFullscreen;
|
|
2719
2765
|
updateFullscreenIcon();
|
|
2720
2766
|
applyFullscreenTouchRestrictions();
|
|
2721
2767
|
}
|
|
2768
|
+
if (event.data && event.data.type === 'pdf-secure-windowed-state') {
|
|
2769
|
+
isWindowed = event.data.isWindowed;
|
|
2770
|
+
updateFullscreenIcon();
|
|
2771
|
+
applyFullscreenTouchRestrictions();
|
|
2772
|
+
}
|
|
2722
2773
|
});
|
|
2723
2774
|
|
|
2724
2775
|
// Local fullscreen events (standalone mode)
|
|
@@ -2728,7 +2779,11 @@
|
|
|
2728
2779
|
applyFullscreenTouchRestrictions();
|
|
2729
2780
|
});
|
|
2730
2781
|
|
|
2731
|
-
|
|
2782
|
+
// Fullscreen button click: toggle current active mode, or fullscreen by default
|
|
2783
|
+
document.getElementById('fullscreenBtn').onclick = () => {
|
|
2784
|
+
if (isWindowed) toggleWindowed();
|
|
2785
|
+
else toggleFullscreen();
|
|
2786
|
+
};
|
|
2732
2787
|
|
|
2733
2788
|
|
|
2734
2789
|
// Mouse wheel / touchpad pinch zoom with Ctrl (clamped 0.5x-5x)
|
package/static/viewer.html
CHANGED
|
@@ -757,11 +757,24 @@
|
|
|
757
757
|
.chatMsg .tableWrap {
|
|
758
758
|
overflow-x: auto;
|
|
759
759
|
margin: 6px 0;
|
|
760
|
+
-webkit-overflow-scrolling: touch;
|
|
761
|
+
position: relative;
|
|
762
|
+
}
|
|
763
|
+
.chatMsg .tableWrap.scrollable::after {
|
|
764
|
+
content: '';
|
|
765
|
+
position: absolute;
|
|
766
|
+
top: 0;
|
|
767
|
+
right: 0;
|
|
768
|
+
bottom: 0;
|
|
769
|
+
width: 24px;
|
|
770
|
+
background: linear-gradient(to right, transparent, rgba(0,0,0,0.4));
|
|
771
|
+
pointer-events: none;
|
|
760
772
|
}
|
|
761
773
|
.chatMsg table {
|
|
762
|
-
width: 100%;
|
|
763
774
|
border-collapse: collapse;
|
|
764
775
|
font-size: 12px;
|
|
776
|
+
min-width: 100%;
|
|
777
|
+
width: max-content;
|
|
765
778
|
}
|
|
766
779
|
.chatMsg th, .chatMsg td {
|
|
767
780
|
border: 1px solid rgba(255,255,255,0.15);
|
|
@@ -771,6 +784,7 @@
|
|
|
771
784
|
.chatMsg th {
|
|
772
785
|
background: rgba(255,255,255,0.08);
|
|
773
786
|
font-weight: 600;
|
|
787
|
+
white-space: nowrap;
|
|
774
788
|
}
|
|
775
789
|
.chatMsg tbody tr:nth-child(even) {
|
|
776
790
|
background: rgba(255,255,255,0.03);
|
|
@@ -2742,12 +2756,33 @@
|
|
|
2742
2756
|
</svg>
|
|
2743
2757
|
</button>
|
|
2744
2758
|
|
|
2745
|
-
<!-- Fullscreen Toggle -->
|
|
2746
|
-
<
|
|
2747
|
-
<
|
|
2748
|
-
<
|
|
2749
|
-
|
|
2750
|
-
|
|
2759
|
+
<!-- Fullscreen Toggle with Dropdown -->
|
|
2760
|
+
<div class="toolbarBtnWithDropdown" id="fullscreenWrapper">
|
|
2761
|
+
<button class="toolbarBtn" id="fullscreenBtn" data-tooltip="Tam Ekran (F)">
|
|
2762
|
+
<svg viewBox="0 0 24 24" id="fullscreenIcon">
|
|
2763
|
+
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
|
|
2764
|
+
</svg>
|
|
2765
|
+
</button>
|
|
2766
|
+
<button class="dropdownArrow" id="fullscreenArrow">
|
|
2767
|
+
<svg viewBox="0 0 24 24">
|
|
2768
|
+
<path d="M7 10l5 5 5-5z" />
|
|
2769
|
+
</svg>
|
|
2770
|
+
</button>
|
|
2771
|
+
<div class="toolDropdown" id="fullscreenDropdown">
|
|
2772
|
+
<button class="overflowItem" id="fullscreenOptionFull">
|
|
2773
|
+
<svg viewBox="0 0 24 24">
|
|
2774
|
+
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
|
|
2775
|
+
</svg>
|
|
2776
|
+
<span>Tam Ekran</span>
|
|
2777
|
+
</button>
|
|
2778
|
+
<button class="overflowItem" id="fullscreenOptionWindowed">
|
|
2779
|
+
<svg viewBox="0 0 24 24">
|
|
2780
|
+
<path d="M19 4H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V8h14v12z" />
|
|
2781
|
+
</svg>
|
|
2782
|
+
<span>Pencereli Mod</span>
|
|
2783
|
+
</button>
|
|
2784
|
+
</div>
|
|
2785
|
+
</div>
|
|
2751
2786
|
|
|
2752
2787
|
<!-- AI Chat Toggle -->
|
|
2753
2788
|
<button class="toolbarBtn" id="chatBtn" data-tooltip="PDF Chat (C)" style="display:none;">
|
|
@@ -3060,6 +3095,12 @@
|
|
|
3060
3095
|
</svg>
|
|
3061
3096
|
<span>Tam Ekran</span>
|
|
3062
3097
|
</button>
|
|
3098
|
+
<button class="overflowItem" id="overflowWindowed">
|
|
3099
|
+
<svg viewBox="0 0 24 24">
|
|
3100
|
+
<path d="M19 4H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V8h14v12z" />
|
|
3101
|
+
</svg>
|
|
3102
|
+
<span>Pencereli Mod</span>
|
|
3103
|
+
</button>
|
|
3063
3104
|
</div>
|
|
3064
3105
|
</div>
|
|
3065
3106
|
</div>
|
|
@@ -4219,6 +4260,14 @@
|
|
|
4219
4260
|
// Truncate excessively long AI responses
|
|
4220
4261
|
if (text.length > 10000) text = text.slice(0, 10000) + '…';
|
|
4221
4262
|
|
|
4263
|
+
// Fix multi-line inline math: join $...$ that span line breaks
|
|
4264
|
+
// e.g. "$V_o\n= V_i$" → "$V_o = V_i$"
|
|
4265
|
+
text = text.replace(/\$([^$]*?\n[^$]*?)\$/g, function (match, inner) {
|
|
4266
|
+
// Only join if the inner content is short (likely math, not prose)
|
|
4267
|
+
if (inner.length > 80) return match;
|
|
4268
|
+
return '$' + inner.replace(/\n/g, ' ') + '$';
|
|
4269
|
+
});
|
|
4270
|
+
|
|
4222
4271
|
var frag = document.createDocumentFragment();
|
|
4223
4272
|
|
|
4224
4273
|
// Split into code blocks, LaTeX blocks ($$...$$), and normal text
|
|
@@ -4492,15 +4541,19 @@
|
|
|
4492
4541
|
}
|
|
4493
4542
|
|
|
4494
4543
|
// Render LaTeX math expression (inline or block)
|
|
4544
|
+
// Tracks pending elements to re-render once KaTeX loads
|
|
4545
|
+
var pendingMathElements = [];
|
|
4495
4546
|
function renderMath(latex, displayMode) {
|
|
4496
|
-
if (typeof katex === 'undefined') {
|
|
4497
|
-
// KaTeX not loaded, show raw LaTeX
|
|
4498
|
-
var fallback = document.createElement('code');
|
|
4499
|
-
fallback.textContent = (displayMode ? '$$' : '$') + latex + (displayMode ? '$$' : '$');
|
|
4500
|
-
return fallback;
|
|
4501
|
-
}
|
|
4502
4547
|
var container = document.createElement('span');
|
|
4503
4548
|
container.className = displayMode ? 'katex-block' : 'katex-inline';
|
|
4549
|
+
if (typeof katex === 'undefined') {
|
|
4550
|
+
// KaTeX not loaded yet — show placeholder, queue for re-render
|
|
4551
|
+
container.textContent = (displayMode ? '$$' : '$') + latex + (displayMode ? '$$' : '$');
|
|
4552
|
+
container.dataset.latex = latex;
|
|
4553
|
+
container.dataset.displayMode = displayMode ? '1' : '0';
|
|
4554
|
+
pendingMathElements.push(container);
|
|
4555
|
+
return container;
|
|
4556
|
+
}
|
|
4504
4557
|
try {
|
|
4505
4558
|
katex.render(latex, container, { displayMode: displayMode, throwOnError: false });
|
|
4506
4559
|
} catch (e) {
|
|
@@ -4508,6 +4561,27 @@
|
|
|
4508
4561
|
}
|
|
4509
4562
|
return container;
|
|
4510
4563
|
}
|
|
4564
|
+
// Re-render pending math elements once KaTeX loads
|
|
4565
|
+
function flushPendingMath() {
|
|
4566
|
+
if (typeof katex === 'undefined' || pendingMathElements.length === 0) return;
|
|
4567
|
+
pendingMathElements.forEach(function (el) {
|
|
4568
|
+
if (!el.dataset.latex) return;
|
|
4569
|
+
try {
|
|
4570
|
+
var dm = el.dataset.displayMode === '1';
|
|
4571
|
+
katex.render(el.dataset.latex, el, { displayMode: dm, throwOnError: false });
|
|
4572
|
+
delete el.dataset.latex;
|
|
4573
|
+
delete el.dataset.displayMode;
|
|
4574
|
+
} catch (e) { /* keep fallback text */ }
|
|
4575
|
+
});
|
|
4576
|
+
pendingMathElements = [];
|
|
4577
|
+
}
|
|
4578
|
+
// Check periodically if KaTeX has loaded (for deferred loading)
|
|
4579
|
+
var katexCheckInterval = setInterval(function () {
|
|
4580
|
+
if (typeof katex !== 'undefined') {
|
|
4581
|
+
clearInterval(katexCheckInterval);
|
|
4582
|
+
flushPendingMath();
|
|
4583
|
+
}
|
|
4584
|
+
}, 500);
|
|
4511
4585
|
|
|
4512
4586
|
function tokenizeInline(line) {
|
|
4513
4587
|
var nodes = [];
|
|
@@ -5195,6 +5269,7 @@
|
|
|
5195
5269
|
|
|
5196
5270
|
const dropdownBackdrop = document.getElementById('dropdownBackdrop');
|
|
5197
5271
|
const overflowDropdown = document.getElementById('overflowDropdown');
|
|
5272
|
+
const fullscreenDropdown = document.getElementById('fullscreenDropdown');
|
|
5198
5273
|
|
|
5199
5274
|
// Swipe-to-dismiss controller (declared before closeAllDropdowns which references it)
|
|
5200
5275
|
let swipeAbortController = null;
|
|
@@ -5206,7 +5281,7 @@
|
|
|
5206
5281
|
// Clean up swipe listeners
|
|
5207
5282
|
if (swipeAbortController) { swipeAbortController.abort(); swipeAbortController = null; }
|
|
5208
5283
|
// Reset inline styles and move dropdowns back to original parents
|
|
5209
|
-
[highlightDropdown, drawDropdown, shapesDropdown, overflowDropdown].forEach(dd => {
|
|
5284
|
+
[highlightDropdown, drawDropdown, shapesDropdown, overflowDropdown, fullscreenDropdown].forEach(dd => {
|
|
5210
5285
|
dd.style.transform = '';
|
|
5211
5286
|
dd.style.transition = '';
|
|
5212
5287
|
dd.style.position = '';
|
|
@@ -5223,6 +5298,7 @@
|
|
|
5223
5298
|
drawDropdown.classList.remove('visible');
|
|
5224
5299
|
shapesDropdown.classList.remove('visible');
|
|
5225
5300
|
overflowDropdown.classList.remove('visible');
|
|
5301
|
+
fullscreenDropdown.classList.remove('visible');
|
|
5226
5302
|
dropdownBackdrop.classList.remove('visible');
|
|
5227
5303
|
}
|
|
5228
5304
|
|
|
@@ -5313,6 +5389,11 @@
|
|
|
5313
5389
|
document.getElementById('drawArrow').onclick = (e) => toggleDropdown(drawDropdown, e);
|
|
5314
5390
|
document.getElementById('shapesArrow').onclick = (e) => toggleDropdown(shapesDropdown, e);
|
|
5315
5391
|
|
|
5392
|
+
// Fullscreen dropdown toggle
|
|
5393
|
+
document.getElementById('fullscreenArrow').onclick = (e) => toggleDropdown(fullscreenDropdown, e);
|
|
5394
|
+
fullscreenDropdown.addEventListener('click', (e) => e.stopPropagation());
|
|
5395
|
+
fullscreenDropdown.addEventListener('pointerup', (e) => e.stopPropagation());
|
|
5396
|
+
|
|
5316
5397
|
// Overflow menu toggle
|
|
5317
5398
|
document.getElementById('overflowBtn').onclick = (e) => toggleDropdown(overflowDropdown, e);
|
|
5318
5399
|
overflowDropdown.addEventListener('click', (e) => e.stopPropagation());
|
|
@@ -5351,9 +5432,27 @@
|
|
|
5351
5432
|
closeAllDropdowns();
|
|
5352
5433
|
});
|
|
5353
5434
|
attachOverflowAction('overflowFullscreen', () => {
|
|
5435
|
+
if (isWindowed) toggleWindowed();
|
|
5354
5436
|
toggleFullscreen();
|
|
5355
5437
|
closeAllDropdowns();
|
|
5356
5438
|
});
|
|
5439
|
+
attachOverflowAction('overflowWindowed', () => {
|
|
5440
|
+
if (isFullscreen) toggleFullscreen();
|
|
5441
|
+
toggleWindowed();
|
|
5442
|
+
closeAllDropdowns();
|
|
5443
|
+
});
|
|
5444
|
+
|
|
5445
|
+
// Fullscreen dropdown options
|
|
5446
|
+
attachOverflowAction('fullscreenOptionFull', () => {
|
|
5447
|
+
if (isWindowed) toggleWindowed(); // exit windowed first
|
|
5448
|
+
toggleFullscreen();
|
|
5449
|
+
closeAllDropdowns();
|
|
5450
|
+
});
|
|
5451
|
+
attachOverflowAction('fullscreenOptionWindowed', () => {
|
|
5452
|
+
if (isFullscreen) toggleFullscreen(); // exit fullscreen first
|
|
5453
|
+
toggleWindowed();
|
|
5454
|
+
closeAllDropdowns();
|
|
5455
|
+
});
|
|
5357
5456
|
|
|
5358
5457
|
// Close dropdowns when clicking outside
|
|
5359
5458
|
document.addEventListener('click', (e) => {
|
|
@@ -7180,7 +7279,7 @@
|
|
|
7180
7279
|
if (key === 't') { setTool('text'); e.preventDefault(); }
|
|
7181
7280
|
if (key === 'r') { setTool('shape'); e.preventDefault(); }
|
|
7182
7281
|
if (key === 'v') { setTool('select'); e.preventDefault(); }
|
|
7183
|
-
if (key === 'f') { toggleFullscreen(); e.preventDefault(); }
|
|
7282
|
+
if (key === 'f') { if (isWindowed) toggleWindowed(); else toggleFullscreen(); e.preventDefault(); }
|
|
7184
7283
|
|
|
7185
7284
|
// Delete selected annotation(s)
|
|
7186
7285
|
if ((key === 'delete' || key === 'backspace') && (selectedAnnotation || multiSelectedAnnotations.length > 0)) {
|
|
@@ -7271,9 +7370,11 @@
|
|
|
7271
7370
|
return;
|
|
7272
7371
|
}
|
|
7273
7372
|
|
|
7274
|
-
// Escape to deselect tool
|
|
7373
|
+
// Escape to deselect tool / exit windowed mode
|
|
7275
7374
|
if (key === 'escape') {
|
|
7276
|
-
if (
|
|
7375
|
+
if (isWindowed) {
|
|
7376
|
+
toggleWindowed();
|
|
7377
|
+
} else if (currentTool) {
|
|
7277
7378
|
setTool(currentTool); // Toggle off
|
|
7278
7379
|
}
|
|
7279
7380
|
closeAllDropdowns();
|
|
@@ -7362,8 +7463,9 @@
|
|
|
7362
7463
|
// ERGONOMIC FEATURES
|
|
7363
7464
|
// ==========================================
|
|
7364
7465
|
|
|
7365
|
-
// Fullscreen: track state (parent-managed when in iframe)
|
|
7466
|
+
// Fullscreen & Windowed: track state (parent-managed when in iframe)
|
|
7366
7467
|
let isFullscreen = false;
|
|
7468
|
+
let isWindowed = false;
|
|
7367
7469
|
const inIframe = window.self !== window.top;
|
|
7368
7470
|
|
|
7369
7471
|
function toggleFullscreen() {
|
|
@@ -7385,6 +7487,12 @@
|
|
|
7385
7487
|
}
|
|
7386
7488
|
}
|
|
7387
7489
|
|
|
7490
|
+
function toggleWindowed() {
|
|
7491
|
+
if (inIframe) {
|
|
7492
|
+
window.parent.postMessage({ type: 'pdf-secure-windowed-toggle' }, window.location.origin);
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
|
|
7388
7496
|
// Update fullscreen button icon based on state
|
|
7389
7497
|
function updateFullscreenIcon() {
|
|
7390
7498
|
const icon = document.getElementById('fullscreenIcon');
|
|
@@ -7392,6 +7500,7 @@
|
|
|
7392
7500
|
const bottomBtn = document.getElementById('bottomFullscreenBtn');
|
|
7393
7501
|
const exitPath = '<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>';
|
|
7394
7502
|
const enterPath = '<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>';
|
|
7503
|
+
const windowedPath = '<path d="M19 4H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V8h14v12z"/>';
|
|
7395
7504
|
if (isFullscreen) {
|
|
7396
7505
|
icon.innerHTML = exitPath;
|
|
7397
7506
|
btn.classList.add('active');
|
|
@@ -7399,6 +7508,13 @@
|
|
|
7399
7508
|
bottomBtn.querySelector('svg').innerHTML = exitPath;
|
|
7400
7509
|
bottomBtn.classList.add('active');
|
|
7401
7510
|
}
|
|
7511
|
+
} else if (isWindowed) {
|
|
7512
|
+
icon.innerHTML = windowedPath;
|
|
7513
|
+
btn.classList.add('active');
|
|
7514
|
+
if (bottomBtn) {
|
|
7515
|
+
bottomBtn.querySelector('svg').innerHTML = windowedPath;
|
|
7516
|
+
bottomBtn.classList.add('active');
|
|
7517
|
+
}
|
|
7402
7518
|
} else {
|
|
7403
7519
|
icon.innerHTML = enterPath;
|
|
7404
7520
|
btn.classList.remove('active');
|
|
@@ -7407,12 +7523,17 @@
|
|
|
7407
7523
|
bottomBtn.classList.remove('active');
|
|
7408
7524
|
}
|
|
7409
7525
|
}
|
|
7526
|
+
// Highlight active option in dropdown
|
|
7527
|
+
const optFull = document.getElementById('fullscreenOptionFull');
|
|
7528
|
+
const optWindowed = document.getElementById('fullscreenOptionWindowed');
|
|
7529
|
+
if (optFull) optFull.classList.toggle('active', isFullscreen);
|
|
7530
|
+
if (optWindowed) optWindowed.classList.toggle('active', isWindowed);
|
|
7410
7531
|
}
|
|
7411
7532
|
|
|
7412
7533
|
// Apply touch restrictions when entering/exiting fullscreen
|
|
7413
7534
|
function applyFullscreenTouchRestrictions() {
|
|
7414
|
-
document.body.classList.toggle('viewer-fullscreen', isFullscreen);
|
|
7415
|
-
if (isFullscreen) {
|
|
7535
|
+
document.body.classList.toggle('viewer-fullscreen', isFullscreen || isWindowed);
|
|
7536
|
+
if (isFullscreen || isWindowed) {
|
|
7416
7537
|
document.documentElement.style.touchAction = 'pan-y pinch-zoom';
|
|
7417
7538
|
document.documentElement.style.overscrollBehavior = 'none';
|
|
7418
7539
|
} else {
|
|
@@ -7421,13 +7542,18 @@
|
|
|
7421
7542
|
}
|
|
7422
7543
|
}
|
|
7423
7544
|
|
|
7424
|
-
// Listen for fullscreen state from parent (iframe mode)
|
|
7545
|
+
// Listen for fullscreen/windowed state from parent (iframe mode)
|
|
7425
7546
|
window.addEventListener('message', (event) => {
|
|
7426
7547
|
if (event.data && event.data.type === 'pdf-secure-fullscreen-state') {
|
|
7427
7548
|
isFullscreen = event.data.isFullscreen;
|
|
7428
7549
|
updateFullscreenIcon();
|
|
7429
7550
|
applyFullscreenTouchRestrictions();
|
|
7430
7551
|
}
|
|
7552
|
+
if (event.data && event.data.type === 'pdf-secure-windowed-state') {
|
|
7553
|
+
isWindowed = event.data.isWindowed;
|
|
7554
|
+
updateFullscreenIcon();
|
|
7555
|
+
applyFullscreenTouchRestrictions();
|
|
7556
|
+
}
|
|
7431
7557
|
});
|
|
7432
7558
|
|
|
7433
7559
|
// Local fullscreen events (standalone mode)
|
|
@@ -7442,9 +7568,15 @@
|
|
|
7442
7568
|
applyFullscreenTouchRestrictions();
|
|
7443
7569
|
});
|
|
7444
7570
|
|
|
7445
|
-
// Fullscreen button click
|
|
7446
|
-
document.getElementById('fullscreenBtn').onclick = () =>
|
|
7447
|
-
|
|
7571
|
+
// Fullscreen button click: toggle current active mode, or fullscreen by default
|
|
7572
|
+
document.getElementById('fullscreenBtn').onclick = () => {
|
|
7573
|
+
if (isWindowed) toggleWindowed();
|
|
7574
|
+
else toggleFullscreen();
|
|
7575
|
+
};
|
|
7576
|
+
document.getElementById('bottomFullscreenBtn').onclick = () => {
|
|
7577
|
+
if (isWindowed) toggleWindowed();
|
|
7578
|
+
else toggleFullscreen();
|
|
7579
|
+
};
|
|
7448
7580
|
|
|
7449
7581
|
|
|
7450
7582
|
|