nodebb-plugin-pdf-secure 1.2.16 → 1.2.18
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 +2 -0
- package/static/viewer-app.js +41 -27
- package/static/viewer.css +12 -0
- package/static/viewer.html +34 -46
package/package.json
CHANGED
package/static/lib/main.js
CHANGED
|
@@ -194,7 +194,9 @@
|
|
|
194
194
|
iframe.style.top = '0';
|
|
195
195
|
iframe.style.left = '0';
|
|
196
196
|
iframe.style.width = '100vw';
|
|
197
|
+
iframe.style.width = '100dvw'; // Override: dynamic viewport (excludes browser chrome)
|
|
197
198
|
iframe.style.height = '100vh';
|
|
199
|
+
iframe.style.height = '100dvh'; // Override: dynamic viewport (excludes address bar on mobile)
|
|
198
200
|
iframe.style.zIndex = '2147483647';
|
|
199
201
|
|
|
200
202
|
// Lock body scroll
|
package/static/viewer-app.js
CHANGED
|
@@ -2417,12 +2417,22 @@
|
|
|
2417
2417
|
// ERGONOMIC FEATURES
|
|
2418
2418
|
// ==========================================
|
|
2419
2419
|
|
|
2420
|
-
|
|
2420
|
+
|
|
2421
|
+
// Fullscreen state (tracks both native and simulated fullscreen)
|
|
2422
|
+
let isFullscreen = false;
|
|
2423
|
+
|
|
2424
|
+
// Fullscreen toggle function — delegates to parent iframe handler via postMessage
|
|
2421
2425
|
function toggleFullscreen() {
|
|
2422
|
-
if (
|
|
2423
|
-
|
|
2426
|
+
if (window.self !== window.top) {
|
|
2427
|
+
// Inside iframe: ask parent to handle fullscreen
|
|
2428
|
+
window.parent.postMessage({ type: 'pdf-secure-fullscreen-toggle' }, '*');
|
|
2424
2429
|
} else {
|
|
2425
|
-
|
|
2430
|
+
// Standalone mode: use native fullscreen
|
|
2431
|
+
if (document.fullscreenElement) {
|
|
2432
|
+
document.exitFullscreen();
|
|
2433
|
+
} else {
|
|
2434
|
+
document.documentElement.requestFullscreen().catch(() => { });
|
|
2435
|
+
}
|
|
2426
2436
|
}
|
|
2427
2437
|
}
|
|
2428
2438
|
|
|
@@ -2430,7 +2440,7 @@
|
|
|
2430
2440
|
function updateFullscreenIcon() {
|
|
2431
2441
|
const icon = document.getElementById('fullscreenIcon');
|
|
2432
2442
|
const btn = document.getElementById('fullscreenBtn');
|
|
2433
|
-
if (
|
|
2443
|
+
if (isFullscreen) {
|
|
2434
2444
|
icon.innerHTML = '<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>';
|
|
2435
2445
|
btn.classList.add('active');
|
|
2436
2446
|
} else {
|
|
@@ -2439,32 +2449,36 @@
|
|
|
2439
2449
|
}
|
|
2440
2450
|
}
|
|
2441
2451
|
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2452
|
+
// Apply fullscreen CSS class and touch restrictions
|
|
2453
|
+
function applyFullscreenTouchRestrictions() {
|
|
2454
|
+
document.body.classList.toggle('viewer-fullscreen', isFullscreen);
|
|
2455
|
+
if (isFullscreen) {
|
|
2456
|
+
document.documentElement.style.touchAction = 'pan-y pinch-zoom';
|
|
2457
|
+
document.documentElement.style.overscrollBehavior = 'none';
|
|
2458
|
+
} else {
|
|
2459
|
+
document.documentElement.style.touchAction = '';
|
|
2460
|
+
document.documentElement.style.overscrollBehavior = '';
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2446
2463
|
|
|
2447
|
-
//
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2464
|
+
// Listen for fullscreen state from parent (simulated fullscreen)
|
|
2465
|
+
window.addEventListener('message', (event) => {
|
|
2466
|
+
if (event.data && event.data.type === 'pdf-secure-fullscreen-state') {
|
|
2467
|
+
isFullscreen = event.data.isFullscreen;
|
|
2468
|
+
updateFullscreenIcon();
|
|
2469
|
+
applyFullscreenTouchRestrictions();
|
|
2453
2470
|
}
|
|
2454
|
-
lastClickTime = now;
|
|
2455
2471
|
});
|
|
2456
2472
|
|
|
2457
|
-
//
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
container.addEventListener('touchstart', autoFullscreen, { once: true });
|
|
2467
|
-
}
|
|
2473
|
+
// Local fullscreen events (standalone mode)
|
|
2474
|
+
document.addEventListener('fullscreenchange', () => {
|
|
2475
|
+
isFullscreen = !!(document.fullscreenElement || document.webkitFullscreenElement);
|
|
2476
|
+
updateFullscreenIcon();
|
|
2477
|
+
applyFullscreenTouchRestrictions();
|
|
2478
|
+
});
|
|
2479
|
+
|
|
2480
|
+
document.getElementById('fullscreenBtn').onclick = () => toggleFullscreen();
|
|
2481
|
+
|
|
2468
2482
|
|
|
2469
2483
|
// Mouse wheel zoom with Ctrl (debounced, clamped 0.5x-5x)
|
|
2470
2484
|
let zoomTimeout;
|
package/static/viewer.css
CHANGED
|
@@ -1482,3 +1482,15 @@ body {
|
|
|
1482
1482
|
height: 56px;
|
|
1483
1483
|
}
|
|
1484
1484
|
}
|
|
1485
|
+
|
|
1486
|
+
/* ==========================================
|
|
1487
|
+
FULLSCREEN STATE (simulated fullscreen on mobile)
|
|
1488
|
+
========================================== */
|
|
1489
|
+
/* Ensure bottom toolbar is visible and viewer container makes room for it */
|
|
1490
|
+
body.viewer-fullscreen #bottomToolbar {
|
|
1491
|
+
display: block;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
body.viewer-fullscreen #viewerContainer {
|
|
1495
|
+
bottom: calc(var(--bottom-bar-height) + var(--safe-area-bottom));
|
|
1496
|
+
}
|
package/static/viewer.html
CHANGED
|
@@ -1353,6 +1353,18 @@
|
|
|
1353
1353
|
}
|
|
1354
1354
|
}
|
|
1355
1355
|
|
|
1356
|
+
/* ==========================================
|
|
1357
|
+
FULLSCREEN STATE (simulated fullscreen on mobile)
|
|
1358
|
+
========================================== */
|
|
1359
|
+
/* Ensure bottom toolbar is visible and viewer container makes room for it */
|
|
1360
|
+
body.viewer-fullscreen #bottomToolbar {
|
|
1361
|
+
display: block;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
body.viewer-fullscreen #viewerContainer {
|
|
1365
|
+
bottom: calc(var(--bottom-bar-height) + var(--safe-area-bottom));
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1356
1368
|
/* ==========================================
|
|
1357
1369
|
TABLET BREAKPOINT (600px - 1024px)
|
|
1358
1370
|
========================================== */
|
|
@@ -2708,63 +2720,29 @@
|
|
|
2708
2720
|
|
|
2709
2721
|
svg.addEventListener('touchstart', (e) => {
|
|
2710
2722
|
if (!currentTool || e.touches.length !== 1) return;
|
|
2711
|
-
|
|
2712
|
-
//
|
|
2713
|
-
|
|
2714
|
-
e.preventDefault();
|
|
2715
|
-
startDraw(e, pageNum);
|
|
2716
|
-
touchDrawing = true;
|
|
2717
|
-
touchDrawDecided = true;
|
|
2718
|
-
return;
|
|
2719
|
-
}
|
|
2720
|
-
|
|
2721
|
-
// For pen/highlight/shape: wait to decide scroll vs draw
|
|
2722
|
-
touchDrawDecided = false;
|
|
2723
|
-
touchDrawing = false;
|
|
2723
|
+
// In annotation mode, always draw immediately — no direction detection.
|
|
2724
|
+
// Pan and pinch-zoom are blocked at the container level when a tool is active.
|
|
2725
|
+
e.preventDefault();
|
|
2724
2726
|
touchStartX = e.touches[0].clientX;
|
|
2725
2727
|
touchStartYDraw = e.touches[0].clientY;
|
|
2728
|
+
startDraw(e, pageNum);
|
|
2729
|
+
touchDrawing = true;
|
|
2730
|
+
touchDrawDecided = true;
|
|
2726
2731
|
}, { passive: false, signal });
|
|
2727
2732
|
|
|
2728
2733
|
svg.addEventListener('touchmove', (e) => {
|
|
2729
2734
|
if (!currentTool || e.touches.length !== 1) return;
|
|
2730
|
-
|
|
2731
2735
|
if (touchDrawing) {
|
|
2732
|
-
// Already decided: drawing
|
|
2733
2736
|
e.preventDefault();
|
|
2734
2737
|
draw(e);
|
|
2735
|
-
return;
|
|
2736
|
-
}
|
|
2737
|
-
|
|
2738
|
-
if (touchDrawDecided) return; // decided scroll, let it through
|
|
2739
|
-
|
|
2740
|
-
const dx = e.touches[0].clientX - touchStartX;
|
|
2741
|
-
const dy = e.touches[0].clientY - touchStartYDraw;
|
|
2742
|
-
|
|
2743
|
-
if (Math.abs(dx) + Math.abs(dy) > 10) {
|
|
2744
|
-
touchDrawDecided = true;
|
|
2745
|
-
|
|
2746
|
-
if (Math.abs(dy) > Math.abs(dx) * 2) {
|
|
2747
|
-
// Predominantly vertical → scroll (don't prevent default)
|
|
2748
|
-
return;
|
|
2749
|
-
}
|
|
2750
|
-
// Horizontal or diagonal → drawing
|
|
2751
|
-
e.preventDefault();
|
|
2752
|
-
// Start draw from ORIGINAL touch position (not current 10px-offset position)
|
|
2753
|
-
const syntheticStart = {
|
|
2754
|
-
currentTarget: svg,
|
|
2755
|
-
touches: [{ clientX: touchStartX, clientY: touchStartYDraw }],
|
|
2756
|
-
preventDefault: () => { }
|
|
2757
|
-
};
|
|
2758
|
-
startDraw(syntheticStart, pageNum);
|
|
2759
|
-
// Then immediately draw to current position for continuity
|
|
2760
|
-
draw(e);
|
|
2761
|
-
touchDrawing = true;
|
|
2762
2738
|
}
|
|
2763
2739
|
}, { passive: false, signal });
|
|
2764
2740
|
|
|
2765
2741
|
svg.addEventListener('touchend', (e) => {
|
|
2766
|
-
if (
|
|
2767
|
-
|
|
2742
|
+
if (touchDrawing) {
|
|
2743
|
+
stopDraw(pageNum);
|
|
2744
|
+
} else if (currentTool && currentTool !== 'eraser' && currentTool !== 'select') {
|
|
2745
|
+
// Tap without moving → draw a dot at touch position
|
|
2768
2746
|
const syntheticStart = {
|
|
2769
2747
|
currentTarget: svg,
|
|
2770
2748
|
touches: [{ clientX: touchStartX, clientY: touchStartYDraw }],
|
|
@@ -2772,8 +2750,6 @@
|
|
|
2772
2750
|
};
|
|
2773
2751
|
startDraw(syntheticStart, pageNum);
|
|
2774
2752
|
stopDraw(pageNum);
|
|
2775
|
-
} else if (touchDrawing) {
|
|
2776
|
-
stopDraw(pageNum);
|
|
2777
2753
|
}
|
|
2778
2754
|
touchDrawDecided = false;
|
|
2779
2755
|
touchDrawing = false;
|
|
@@ -4499,6 +4475,7 @@
|
|
|
4499
4475
|
|
|
4500
4476
|
// Apply touch restrictions when entering/exiting fullscreen
|
|
4501
4477
|
function applyFullscreenTouchRestrictions() {
|
|
4478
|
+
document.body.classList.toggle('viewer-fullscreen', isFullscreen);
|
|
4502
4479
|
if (isFullscreen) {
|
|
4503
4480
|
document.documentElement.style.touchAction = 'pan-y pinch-zoom';
|
|
4504
4481
|
document.documentElement.style.overscrollBehavior = 'none';
|
|
@@ -4651,6 +4628,11 @@
|
|
|
4651
4628
|
|
|
4652
4629
|
container.addEventListener('touchstart', (e) => {
|
|
4653
4630
|
if (e.touches.length === 2) {
|
|
4631
|
+
// Block pinch-zoom when an annotation tool is active
|
|
4632
|
+
if (annotationMode && currentTool) {
|
|
4633
|
+
e.preventDefault();
|
|
4634
|
+
return;
|
|
4635
|
+
}
|
|
4654
4636
|
// Cancel any active drawing and clean up
|
|
4655
4637
|
if (isDrawing && currentDrawingPage) {
|
|
4656
4638
|
const savePage = currentDrawingPage;
|
|
@@ -4816,6 +4798,12 @@
|
|
|
4816
4798
|
container.addEventListener('touchmove', (e) => {
|
|
4817
4799
|
if (isPinching || e.touches.length !== 1) return;
|
|
4818
4800
|
|
|
4801
|
+
// Block all container scroll when an annotation tool is active
|
|
4802
|
+
if (annotationMode && currentTool) {
|
|
4803
|
+
e.preventDefault();
|
|
4804
|
+
return;
|
|
4805
|
+
}
|
|
4806
|
+
|
|
4819
4807
|
const touchY = e.touches[0].clientY;
|
|
4820
4808
|
const deltaY = touchStartY - touchY; // positive = scrolling down
|
|
4821
4809
|
|