nodebb-plugin-pdf-secure 1.2.15 → 1.2.16
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 +119 -7
- package/static/viewer.html +38 -10
package/package.json
CHANGED
package/static/lib/main.js
CHANGED
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
});
|
|
23
23
|
})();
|
|
24
24
|
|
|
25
|
+
// Fullscreen API support detection
|
|
26
|
+
// On touch devices (tablets, mobiles), always use CSS simulated fullscreen.
|
|
27
|
+
// Native fullscreen has an unblockable browser "scroll-to-exit" gesture on touch devices.
|
|
28
|
+
// Desktop keeps native fullscreen for the proper OS-level experience.
|
|
29
|
+
var isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
|
|
30
|
+
var fullscreenApiSupported = !isTouchDevice && !!(
|
|
31
|
+
document.documentElement.requestFullscreen ||
|
|
32
|
+
document.documentElement.webkitRequestFullscreen
|
|
33
|
+
);
|
|
34
|
+
var simulatedFullscreenIframe = null;
|
|
35
|
+
var savedBodyOverflow = '';
|
|
36
|
+
|
|
25
37
|
// Loading queue - only load one PDF at a time
|
|
26
38
|
const loadQueue = [];
|
|
27
39
|
let isLoading = false;
|
|
@@ -76,19 +88,30 @@
|
|
|
76
88
|
});
|
|
77
89
|
if (!sourceIframe) return;
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
if (fullscreenApiSupported) {
|
|
92
|
+
// Native fullscreen path
|
|
93
|
+
var fsEl = document.fullscreenElement || document.webkitFullscreenElement;
|
|
94
|
+
if (fsEl) {
|
|
95
|
+
if (document.exitFullscreen) document.exitFullscreen();
|
|
96
|
+
else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
|
|
97
|
+
} else {
|
|
98
|
+
if (sourceIframe.requestFullscreen) sourceIframe.requestFullscreen().catch(function () { });
|
|
99
|
+
else if (sourceIframe.webkitRequestFullscreen) sourceIframe.webkitRequestFullscreen();
|
|
100
|
+
}
|
|
83
101
|
} else {
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
// Simulated fullscreen path (iOS Safari / Chrome)
|
|
103
|
+
if (simulatedFullscreenIframe) {
|
|
104
|
+
exitSimulatedFullscreen();
|
|
105
|
+
} else {
|
|
106
|
+
enterSimulatedFullscreen(sourceIframe);
|
|
107
|
+
}
|
|
86
108
|
}
|
|
87
109
|
}
|
|
88
110
|
|
|
89
111
|
// Fullscreen state query from iframe
|
|
90
112
|
if (event.data && event.data.type === 'pdf-secure-fullscreen-query') {
|
|
91
|
-
var fsActive = !!(document.fullscreenElement || document.webkitFullscreenElement)
|
|
113
|
+
var fsActive = !!(document.fullscreenElement || document.webkitFullscreenElement) ||
|
|
114
|
+
(simulatedFullscreenIframe !== null);
|
|
92
115
|
if (event.source) {
|
|
93
116
|
event.source.postMessage({
|
|
94
117
|
type: 'pdf-secure-fullscreen-state',
|
|
@@ -126,8 +149,16 @@
|
|
|
126
149
|
// Forward fullscreen state changes to all viewer iframes
|
|
127
150
|
function notifyFullscreenChange() {
|
|
128
151
|
var fsActive = !!(document.fullscreenElement || document.webkitFullscreenElement);
|
|
152
|
+
if (fsActive) {
|
|
153
|
+
document.body.style.overscrollBehavior = 'none';
|
|
154
|
+
document.body.style.overflow = 'hidden';
|
|
155
|
+
} else {
|
|
156
|
+
document.body.style.overscrollBehavior = '';
|
|
157
|
+
document.body.style.overflow = '';
|
|
158
|
+
}
|
|
129
159
|
document.querySelectorAll('.pdf-secure-iframe').forEach(function (f) {
|
|
130
160
|
if (f.contentWindow) {
|
|
161
|
+
f.style.overscrollBehavior = 'none';
|
|
131
162
|
f.contentWindow.postMessage({
|
|
132
163
|
type: 'pdf-secure-fullscreen-state',
|
|
133
164
|
isFullscreen: fsActive
|
|
@@ -138,6 +169,85 @@
|
|
|
138
169
|
document.addEventListener('fullscreenchange', notifyFullscreenChange);
|
|
139
170
|
document.addEventListener('webkitfullscreenchange', notifyFullscreenChange);
|
|
140
171
|
|
|
172
|
+
// Touch handler to block parent scroll during simulated fullscreen
|
|
173
|
+
function parentFullscreenTouchHandler(e) {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Enter CSS simulated fullscreen (for iOS Safari/Chrome)
|
|
178
|
+
function enterSimulatedFullscreen(iframe) {
|
|
179
|
+
if (simulatedFullscreenIframe) return; // already in simulated fullscreen
|
|
180
|
+
simulatedFullscreenIframe = iframe;
|
|
181
|
+
|
|
182
|
+
// Save original iframe styles
|
|
183
|
+
iframe._savedStyle = {
|
|
184
|
+
position: iframe.style.position,
|
|
185
|
+
top: iframe.style.top,
|
|
186
|
+
left: iframe.style.left,
|
|
187
|
+
width: iframe.style.width,
|
|
188
|
+
height: iframe.style.height,
|
|
189
|
+
zIndex: iframe.style.zIndex
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Apply fullscreen styles
|
|
193
|
+
iframe.style.position = 'fixed';
|
|
194
|
+
iframe.style.top = '0';
|
|
195
|
+
iframe.style.left = '0';
|
|
196
|
+
iframe.style.width = '100vw';
|
|
197
|
+
iframe.style.height = '100vh';
|
|
198
|
+
iframe.style.zIndex = '2147483647';
|
|
199
|
+
|
|
200
|
+
// Lock body scroll
|
|
201
|
+
savedBodyOverflow = document.body.style.overflow;
|
|
202
|
+
document.body.style.overflow = 'hidden';
|
|
203
|
+
document.body.style.overscrollBehavior = 'none';
|
|
204
|
+
|
|
205
|
+
// Block touch scroll on parent
|
|
206
|
+
document.addEventListener('touchmove', parentFullscreenTouchHandler, { passive: false });
|
|
207
|
+
|
|
208
|
+
// Notify iframe it is now fullscreen
|
|
209
|
+
if (iframe.contentWindow) {
|
|
210
|
+
iframe.contentWindow.postMessage({
|
|
211
|
+
type: 'pdf-secure-fullscreen-state',
|
|
212
|
+
isFullscreen: true
|
|
213
|
+
}, window.location.origin);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Exit CSS simulated fullscreen
|
|
218
|
+
function exitSimulatedFullscreen() {
|
|
219
|
+
if (!simulatedFullscreenIframe) return;
|
|
220
|
+
var iframe = simulatedFullscreenIframe;
|
|
221
|
+
simulatedFullscreenIframe = null;
|
|
222
|
+
|
|
223
|
+
// Restore original iframe styles
|
|
224
|
+
if (iframe._savedStyle) {
|
|
225
|
+
iframe.style.position = iframe._savedStyle.position;
|
|
226
|
+
iframe.style.top = iframe._savedStyle.top;
|
|
227
|
+
iframe.style.left = iframe._savedStyle.left;
|
|
228
|
+
iframe.style.width = iframe._savedStyle.width;
|
|
229
|
+
iframe.style.height = iframe._savedStyle.height;
|
|
230
|
+
iframe.style.zIndex = iframe._savedStyle.zIndex;
|
|
231
|
+
delete iframe._savedStyle;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Restore body scroll
|
|
235
|
+
document.body.style.overflow = savedBodyOverflow;
|
|
236
|
+
document.body.style.overscrollBehavior = '';
|
|
237
|
+
savedBodyOverflow = '';
|
|
238
|
+
|
|
239
|
+
// Remove parent touch block
|
|
240
|
+
document.removeEventListener('touchmove', parentFullscreenTouchHandler);
|
|
241
|
+
|
|
242
|
+
// Notify iframe it is no longer fullscreen
|
|
243
|
+
if (iframe.contentWindow) {
|
|
244
|
+
iframe.contentWindow.postMessage({
|
|
245
|
+
type: 'pdf-secure-fullscreen-state',
|
|
246
|
+
isFullscreen: false
|
|
247
|
+
}, window.location.origin);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
141
251
|
async function processQueue() {
|
|
142
252
|
if (isLoading || loadQueue.length === 0) return;
|
|
143
253
|
|
|
@@ -169,6 +279,8 @@
|
|
|
169
279
|
loadQueue.length = 0;
|
|
170
280
|
isLoading = false;
|
|
171
281
|
currentResolver = null;
|
|
282
|
+
// Exit simulated fullscreen on SPA navigation
|
|
283
|
+
exitSimulatedFullscreen();
|
|
172
284
|
interceptPdfLinks();
|
|
173
285
|
});
|
|
174
286
|
} catch (err) {
|
package/static/viewer.html
CHANGED
|
@@ -2753,7 +2753,7 @@
|
|
|
2753
2753
|
const syntheticStart = {
|
|
2754
2754
|
currentTarget: svg,
|
|
2755
2755
|
touches: [{ clientX: touchStartX, clientY: touchStartYDraw }],
|
|
2756
|
-
preventDefault: () => {}
|
|
2756
|
+
preventDefault: () => { }
|
|
2757
2757
|
};
|
|
2758
2758
|
startDraw(syntheticStart, pageNum);
|
|
2759
2759
|
// Then immediately draw to current position for continuity
|
|
@@ -2768,7 +2768,7 @@
|
|
|
2768
2768
|
const syntheticStart = {
|
|
2769
2769
|
currentTarget: svg,
|
|
2770
2770
|
touches: [{ clientX: touchStartX, clientY: touchStartYDraw }],
|
|
2771
|
-
preventDefault: () => {}
|
|
2771
|
+
preventDefault: () => { }
|
|
2772
2772
|
};
|
|
2773
2773
|
startDraw(syntheticStart, pageNum);
|
|
2774
2774
|
stopDraw(pageNum);
|
|
@@ -4497,11 +4497,23 @@
|
|
|
4497
4497
|
}
|
|
4498
4498
|
}
|
|
4499
4499
|
|
|
4500
|
+
// Apply touch restrictions when entering/exiting fullscreen
|
|
4501
|
+
function applyFullscreenTouchRestrictions() {
|
|
4502
|
+
if (isFullscreen) {
|
|
4503
|
+
document.documentElement.style.touchAction = 'pan-y pinch-zoom';
|
|
4504
|
+
document.documentElement.style.overscrollBehavior = 'none';
|
|
4505
|
+
} else {
|
|
4506
|
+
document.documentElement.style.touchAction = '';
|
|
4507
|
+
document.documentElement.style.overscrollBehavior = '';
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4500
4511
|
// Listen for fullscreen state from parent (iframe mode)
|
|
4501
4512
|
window.addEventListener('message', (event) => {
|
|
4502
4513
|
if (event.data && event.data.type === 'pdf-secure-fullscreen-state') {
|
|
4503
4514
|
isFullscreen = event.data.isFullscreen;
|
|
4504
4515
|
updateFullscreenIcon();
|
|
4516
|
+
applyFullscreenTouchRestrictions();
|
|
4505
4517
|
}
|
|
4506
4518
|
});
|
|
4507
4519
|
|
|
@@ -4509,10 +4521,12 @@
|
|
|
4509
4521
|
document.addEventListener('fullscreenchange', () => {
|
|
4510
4522
|
isFullscreen = !!(document.fullscreenElement || document.webkitFullscreenElement);
|
|
4511
4523
|
updateFullscreenIcon();
|
|
4524
|
+
applyFullscreenTouchRestrictions();
|
|
4512
4525
|
});
|
|
4513
4526
|
document.addEventListener('webkitfullscreenchange', () => {
|
|
4514
4527
|
isFullscreen = !!(document.fullscreenElement || document.webkitFullscreenElement);
|
|
4515
4528
|
updateFullscreenIcon();
|
|
4529
|
+
applyFullscreenTouchRestrictions();
|
|
4516
4530
|
});
|
|
4517
4531
|
|
|
4518
4532
|
// Fullscreen button click
|
|
@@ -4805,11 +4819,21 @@
|
|
|
4805
4819
|
const touchY = e.touches[0].clientY;
|
|
4806
4820
|
const deltaY = touchStartY - touchY; // positive = scrolling down
|
|
4807
4821
|
|
|
4808
|
-
const
|
|
4809
|
-
const
|
|
4822
|
+
const BUFFER = 5; // px buffer to catch near-boundary gestures
|
|
4823
|
+
const atTop = container.scrollTop <= BUFFER;
|
|
4824
|
+
const atBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 1 - BUFFER;
|
|
4810
4825
|
|
|
4811
|
-
|
|
4812
|
-
|
|
4826
|
+
// In fullscreen, use buffer; outside fullscreen, only block at exact boundary
|
|
4827
|
+
if (isFullscreen) {
|
|
4828
|
+
if ((atTop && deltaY < 0) || (atBottom && deltaY > 0)) {
|
|
4829
|
+
e.preventDefault();
|
|
4830
|
+
}
|
|
4831
|
+
} else {
|
|
4832
|
+
const atTopExact = container.scrollTop <= 0;
|
|
4833
|
+
const atBottomExact = container.scrollTop + container.clientHeight >= container.scrollHeight - 1;
|
|
4834
|
+
if ((atTopExact && deltaY < 0) || (atBottomExact && deltaY > 0)) {
|
|
4835
|
+
e.preventDefault();
|
|
4836
|
+
}
|
|
4813
4837
|
}
|
|
4814
4838
|
touchStartY = touchY;
|
|
4815
4839
|
}, { passive: false });
|
|
@@ -4831,11 +4855,15 @@
|
|
|
4831
4855
|
|
|
4832
4856
|
const touchY = e.touches[0].clientY;
|
|
4833
4857
|
const deltaY = docTouchStartY - touchY;
|
|
4834
|
-
const atTop = container.scrollTop <= 0;
|
|
4835
|
-
const atBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 1;
|
|
4836
4858
|
|
|
4837
|
-
|
|
4838
|
-
|
|
4859
|
+
const BUFFER = 5; // px buffer to catch near-boundary gestures
|
|
4860
|
+
const atTop = container.scrollTop <= BUFFER;
|
|
4861
|
+
const atBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 1 - BUFFER;
|
|
4862
|
+
|
|
4863
|
+
// If content fits entirely (no scroll needed), block all overscroll
|
|
4864
|
+
const contentFits = container.scrollHeight <= container.clientHeight + 1;
|
|
4865
|
+
|
|
4866
|
+
if (contentFits || (atTop && deltaY < 0) || (atBottom && deltaY > 0)) {
|
|
4839
4867
|
e.preventDefault();
|
|
4840
4868
|
}
|
|
4841
4869
|
docTouchStartY = touchY;
|