nodebb-plugin-pdf-secure 1.2.12 → 1.2.13
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 +4 -2
- package/static/viewer.html +100 -5
package/package.json
CHANGED
package/static/lib/main.js
CHANGED
|
@@ -73,11 +73,13 @@
|
|
|
73
73
|
const cached = pdfBufferCache.get(filename);
|
|
74
74
|
if (cached && event.source) {
|
|
75
75
|
// Send cached buffer to viewer (transferable for 0-copy)
|
|
76
|
+
// Clone once: keep original in cache, transfer the copy
|
|
77
|
+
const copy = cached.slice(0);
|
|
76
78
|
event.source.postMessage({
|
|
77
79
|
type: 'pdf-secure-cache-response',
|
|
78
80
|
filename: filename,
|
|
79
|
-
buffer:
|
|
80
|
-
}, event.origin, [
|
|
81
|
+
buffer: copy
|
|
82
|
+
}, event.origin, [copy]);
|
|
81
83
|
console.log('[PDF-Secure] Cache: Hit -', filename);
|
|
82
84
|
} else if (event.source) {
|
|
83
85
|
// No cache, viewer will fetch normally
|
package/static/viewer.html
CHANGED
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
-moz-user-select: none;
|
|
53
53
|
-ms-user-select: none;
|
|
54
54
|
user-select: none;
|
|
55
|
+
/* Prevent scroll chaining out of iframe */
|
|
56
|
+
overscroll-behavior: contain;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/* Print Protection - hide everything when printing */
|
|
@@ -624,6 +626,9 @@
|
|
|
624
626
|
overflow: auto;
|
|
625
627
|
background: #525659;
|
|
626
628
|
z-index: 1;
|
|
629
|
+
/* Prevent scroll chaining to parent/iframe on touch devices */
|
|
630
|
+
overscroll-behavior: contain;
|
|
631
|
+
-webkit-overflow-scrolling: touch;
|
|
627
632
|
}
|
|
628
633
|
|
|
629
634
|
#viewerContainer.withSidebar {
|
|
@@ -1524,6 +1529,11 @@
|
|
|
1524
1529
|
width: 56px;
|
|
1525
1530
|
height: 56px;
|
|
1526
1531
|
}
|
|
1532
|
+
|
|
1533
|
+
/* Let our JS handle pinch-to-zoom, but allow browser pan-y for scroll */
|
|
1534
|
+
#viewerContainer {
|
|
1535
|
+
touch-action: pan-x pan-y;
|
|
1536
|
+
}
|
|
1527
1537
|
}
|
|
1528
1538
|
</style>
|
|
1529
1539
|
</head>
|
|
@@ -4502,11 +4512,17 @@
|
|
|
4502
4512
|
});
|
|
4503
4513
|
|
|
4504
4514
|
// ==========================================
|
|
4505
|
-
// PINCH-TO-ZOOM (Touch devices)
|
|
4515
|
+
// PINCH-TO-ZOOM (Touch devices) - Smooth CSS Transform
|
|
4506
4516
|
// ==========================================
|
|
4507
4517
|
let pinchStartDistance = 0;
|
|
4508
4518
|
let pinchStartScale = 1;
|
|
4509
4519
|
let isPinching = false;
|
|
4520
|
+
let pinchMidX = 0;
|
|
4521
|
+
let pinchMidY = 0;
|
|
4522
|
+
let pinchVisualRatio = 1;
|
|
4523
|
+
|
|
4524
|
+
// The viewer div that holds PDF pages
|
|
4525
|
+
const viewerDiv = document.getElementById('viewer');
|
|
4510
4526
|
|
|
4511
4527
|
function getTouchDistance(touches) {
|
|
4512
4528
|
const dx = touches[0].clientX - touches[1].clientX;
|
|
@@ -4514,11 +4530,17 @@
|
|
|
4514
4530
|
return Math.sqrt(dx * dx + dy * dy);
|
|
4515
4531
|
}
|
|
4516
4532
|
|
|
4533
|
+
function getTouchMidpoint(touches) {
|
|
4534
|
+
return {
|
|
4535
|
+
x: (touches[0].clientX + touches[1].clientX) / 2,
|
|
4536
|
+
y: (touches[0].clientY + touches[1].clientY) / 2
|
|
4537
|
+
};
|
|
4538
|
+
}
|
|
4539
|
+
|
|
4517
4540
|
container.addEventListener('touchstart', (e) => {
|
|
4518
4541
|
if (e.touches.length === 2) {
|
|
4519
4542
|
// Cancel any active drawing and clean up
|
|
4520
4543
|
if (isDrawing && currentDrawingPage) {
|
|
4521
|
-
// Remove incomplete path
|
|
4522
4544
|
if (currentPath && currentPath.parentNode) currentPath.remove();
|
|
4523
4545
|
currentPath = null;
|
|
4524
4546
|
pathSegments = [];
|
|
@@ -4530,6 +4552,18 @@
|
|
|
4530
4552
|
isPinching = true;
|
|
4531
4553
|
pinchStartDistance = getTouchDistance(e.touches);
|
|
4532
4554
|
pinchStartScale = pdfViewer.currentScale;
|
|
4555
|
+
pinchVisualRatio = 1;
|
|
4556
|
+
|
|
4557
|
+
// Get midpoint relative to container
|
|
4558
|
+
const mid = getTouchMidpoint(e.touches);
|
|
4559
|
+
const rect = container.getBoundingClientRect();
|
|
4560
|
+
pinchMidX = mid.x - rect.left + container.scrollLeft;
|
|
4561
|
+
pinchMidY = mid.y - rect.top + container.scrollTop;
|
|
4562
|
+
|
|
4563
|
+
// Prepare for CSS transform - use will-change for GPU acceleration
|
|
4564
|
+
viewerDiv.style.willChange = 'transform';
|
|
4565
|
+
viewerDiv.style.transformOrigin = pinchMidX + 'px ' + pinchMidY + 'px';
|
|
4566
|
+
|
|
4533
4567
|
e.preventDefault();
|
|
4534
4568
|
}
|
|
4535
4569
|
}, { passive: false });
|
|
@@ -4538,18 +4572,79 @@
|
|
|
4538
4572
|
if (isPinching && e.touches.length === 2) {
|
|
4539
4573
|
const dist = getTouchDistance(e.touches);
|
|
4540
4574
|
const ratio = dist / pinchStartDistance;
|
|
4541
|
-
|
|
4542
|
-
|
|
4575
|
+
// Clamp the target scale
|
|
4576
|
+
const targetScale = pinchStartScale * ratio;
|
|
4577
|
+
const clampedScale = Math.min(Math.max(targetScale, 0.5), 5.0);
|
|
4578
|
+
pinchVisualRatio = clampedScale / pinchStartScale;
|
|
4579
|
+
|
|
4580
|
+
// Apply CSS transform for instant smooth visual feedback (no re-render)
|
|
4581
|
+
viewerDiv.style.transform = 'scale(' + pinchVisualRatio + ')';
|
|
4582
|
+
|
|
4543
4583
|
e.preventDefault();
|
|
4544
4584
|
}
|
|
4545
4585
|
}, { passive: false });
|
|
4546
4586
|
|
|
4547
4587
|
container.addEventListener('touchend', (e) => {
|
|
4548
|
-
if (e.touches.length < 2) {
|
|
4588
|
+
if (isPinching && e.touches.length < 2) {
|
|
4549
4589
|
isPinching = false;
|
|
4590
|
+
|
|
4591
|
+
// Remove CSS transform
|
|
4592
|
+
viewerDiv.style.transform = '';
|
|
4593
|
+
viewerDiv.style.willChange = '';
|
|
4594
|
+
viewerDiv.style.transformOrigin = '';
|
|
4595
|
+
|
|
4596
|
+
// Apply actual scale (triggers PDF re-render only once)
|
|
4597
|
+
const finalScale = Math.min(Math.max(pinchStartScale * pinchVisualRatio, 0.5), 5.0);
|
|
4598
|
+
if (Math.abs(finalScale - pdfViewer.currentScale) > 0.01) {
|
|
4599
|
+
pdfViewer.currentScale = finalScale;
|
|
4600
|
+
}
|
|
4601
|
+
pinchVisualRatio = 1;
|
|
4550
4602
|
}
|
|
4551
4603
|
});
|
|
4552
4604
|
|
|
4605
|
+
container.addEventListener('touchcancel', (e) => {
|
|
4606
|
+
if (isPinching) {
|
|
4607
|
+
isPinching = false;
|
|
4608
|
+
viewerDiv.style.transform = '';
|
|
4609
|
+
viewerDiv.style.willChange = '';
|
|
4610
|
+
viewerDiv.style.transformOrigin = '';
|
|
4611
|
+
pinchVisualRatio = 1;
|
|
4612
|
+
}
|
|
4613
|
+
});
|
|
4614
|
+
|
|
4615
|
+
// ==========================================
|
|
4616
|
+
// TOUCH SCROLL BOUNDARY (Prevent exit on tablet)
|
|
4617
|
+
// ==========================================
|
|
4618
|
+
// On touch devices, prevent scroll from escaping the container
|
|
4619
|
+
// when at the top/bottom boundary. Only fullscreen button should exit.
|
|
4620
|
+
(function initTouchScrollBoundary() {
|
|
4621
|
+
if (!isTouch()) return;
|
|
4622
|
+
|
|
4623
|
+
let touchStartY = 0;
|
|
4624
|
+
|
|
4625
|
+
container.addEventListener('touchstart', (e) => {
|
|
4626
|
+
if (e.touches.length === 1) {
|
|
4627
|
+
touchStartY = e.touches[0].clientY;
|
|
4628
|
+
}
|
|
4629
|
+
}, { passive: true });
|
|
4630
|
+
|
|
4631
|
+
container.addEventListener('touchmove', (e) => {
|
|
4632
|
+
// Skip if pinching
|
|
4633
|
+
if (isPinching || e.touches.length !== 1) return;
|
|
4634
|
+
|
|
4635
|
+
const touchY = e.touches[0].clientY;
|
|
4636
|
+
const deltaY = touchStartY - touchY; // positive = scrolling down
|
|
4637
|
+
|
|
4638
|
+
const atTop = container.scrollTop <= 0;
|
|
4639
|
+
const atBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 1;
|
|
4640
|
+
|
|
4641
|
+
// Prevent overscroll at boundaries
|
|
4642
|
+
if ((atTop && deltaY < 0) || (atBottom && deltaY > 0)) {
|
|
4643
|
+
e.preventDefault();
|
|
4644
|
+
}
|
|
4645
|
+
}, { passive: false });
|
|
4646
|
+
})();
|
|
4647
|
+
|
|
4553
4648
|
// ==========================================
|
|
4554
4649
|
// CONTEXT MENU TOUCH HANDLING
|
|
4555
4650
|
// ==========================================
|