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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-pdf-secure",
3
- "version": "1.2.16",
3
+ "version": "1.2.18",
4
4
  "description": "Secure PDF viewer plugin for NodeBB - prevents downloading, enables canvas-only rendering with Premium group support",
5
5
  "main": "library.js",
6
6
  "repository": {
@@ -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
@@ -2417,12 +2417,22 @@
2417
2417
  // ERGONOMIC FEATURES
2418
2418
  // ==========================================
2419
2419
 
2420
- // Fullscreen toggle function
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 (document.fullscreenElement) {
2423
- document.exitFullscreen();
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
- document.documentElement.requestFullscreen().catch(() => { });
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 (document.fullscreenElement) {
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
- document.addEventListener('fullscreenchange', updateFullscreenIcon);
2443
-
2444
- // Fullscreen button click
2445
- document.getElementById('fullscreenBtn').onclick = () => toggleFullscreen();
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
- // Double-click on page for fullscreen
2448
- let lastClickTime = 0;
2449
- container.addEventListener('click', (e) => {
2450
- const now = Date.now();
2451
- if (now - lastClickTime < 300) {
2452
- toggleFullscreen();
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
- // Auto-fullscreen when viewer loads inside iframe
2458
- if (window.self !== window.top && window.PDF_SECURE_CONFIG) {
2459
- // We're inside an iframe - request fullscreen on first user interaction
2460
- const autoFullscreen = () => {
2461
- document.documentElement.requestFullscreen().catch(() => { });
2462
- container.removeEventListener('click', autoFullscreen);
2463
- container.removeEventListener('touchstart', autoFullscreen);
2464
- };
2465
- container.addEventListener('click', autoFullscreen, { once: true });
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
+ }
@@ -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
- // Eraser and select need immediate response
2713
- if (currentTool === 'eraser' || currentTool === 'select') {
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 (!touchDrawDecided && currentTool && currentTool !== 'eraser' && currentTool !== 'select') {
2767
- // Tap without moving > 10px → draw a dot at touch position
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