nodebb-plugin-pdf-secure2 1.2.38 → 1.3.1
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/.claude/settings.local.json +11 -0
- package/lib/controllers.js +105 -0
- package/lib/gemini-chat.js +125 -0
- package/library.js +257 -246
- package/package.json +2 -1
- package/plugin.json +4 -1
- package/static/lib/admin.js +25 -0
- package/static/lib/main.js +468 -468
- package/static/lib/pdf.min.mjs +20 -20
- package/static/lib/pdf.worker.min.mjs +20 -20
- package/static/templates/admin/plugins/pdf-secure.tpl +14 -5
- package/static/viewer-app.js +2 -2
- package/static/viewer.html +6353 -5893
- package/static/lib/pdf-secure (1).pdf +0 -0
- package/static/lib/viewer.js +0 -161
- package/static/viewer-yedek.html +0 -4548
package/static/lib/main.js
CHANGED
|
@@ -1,468 +1,468 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Main plugin logic - PDF links become inline embedded viewers with lazy loading + queue
|
|
4
|
-
(async function () {
|
|
5
|
-
// ============================================
|
|
6
|
-
// PDF.js PRELOAD - Cache CDN assets before iframe loads
|
|
7
|
-
// ============================================
|
|
8
|
-
(function preloadPdfJs() {
|
|
9
|
-
const preloads = [
|
|
10
|
-
{ href: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js', as: 'script' },
|
|
11
|
-
{ href: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css', as: 'style' }
|
|
12
|
-
];
|
|
13
|
-
preloads.forEach(({ href, as }) => {
|
|
14
|
-
if (!document.querySelector(`link[href="${href}"]`)) {
|
|
15
|
-
const link = document.createElement('link');
|
|
16
|
-
link.rel = 'preload';
|
|
17
|
-
link.href = href;
|
|
18
|
-
link.as = as;
|
|
19
|
-
link.crossOrigin = 'anonymous';
|
|
20
|
-
document.head.appendChild(link);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
})();
|
|
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
|
-
|
|
37
|
-
// Loading queue - only load one PDF at a time
|
|
38
|
-
const loadQueue = [];
|
|
39
|
-
let isLoading = false;
|
|
40
|
-
let currentResolver = null;
|
|
41
|
-
|
|
42
|
-
// ============================================
|
|
43
|
-
// SPA MEMORY CACHE - Cache decoded PDF buffers
|
|
44
|
-
// ============================================
|
|
45
|
-
const pdfBufferCache = new Map(); // filename -> ArrayBuffer
|
|
46
|
-
const CACHE_MAX_SIZE = 5; // ~50MB limit (avg 10MB per PDF)
|
|
47
|
-
let currentLoadingFilename = null;
|
|
48
|
-
|
|
49
|
-
function setCachedBuffer(filename, buffer) {
|
|
50
|
-
// Evict oldest if cache is full
|
|
51
|
-
if (pdfBufferCache.size >= CACHE_MAX_SIZE) {
|
|
52
|
-
const firstKey = pdfBufferCache.keys().next().value;
|
|
53
|
-
pdfBufferCache.delete(firstKey);
|
|
54
|
-
}
|
|
55
|
-
pdfBufferCache.set(filename, buffer);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Listen for postMessage from iframe
|
|
59
|
-
window.addEventListener('message', function (event) {
|
|
60
|
-
// Security: Only accept messages from same origin
|
|
61
|
-
if (event.origin !== window.location.origin) return;
|
|
62
|
-
|
|
63
|
-
// PDF ready - resolve queue
|
|
64
|
-
if (event.data && event.data.type === 'pdf-secure-ready') {
|
|
65
|
-
if (currentResolver) {
|
|
66
|
-
currentResolver();
|
|
67
|
-
currentResolver = null;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// PDF buffer from viewer - cache it
|
|
72
|
-
if (event.data && event.data.type === 'pdf-secure-buffer') {
|
|
73
|
-
const { filename, buffer } = event.data;
|
|
74
|
-
if (filename && buffer) {
|
|
75
|
-
setCachedBuffer(filename, buffer);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Fullscreen toggle request from iframe viewer
|
|
80
|
-
if (event.data && event.data.type === 'pdf-secure-fullscreen-toggle') {
|
|
81
|
-
var sourceIframe = document.querySelector('.pdf-secure-iframe');
|
|
82
|
-
// Find the specific iframe that sent the message
|
|
83
|
-
document.querySelectorAll('.pdf-secure-iframe').forEach(function (f) {
|
|
84
|
-
if (f.contentWindow === event.source) sourceIframe = f;
|
|
85
|
-
});
|
|
86
|
-
if (!sourceIframe) return;
|
|
87
|
-
|
|
88
|
-
if (fullscreenApiSupported) {
|
|
89
|
-
// Native fullscreen path
|
|
90
|
-
var fsEl = document.fullscreenElement || document.webkitFullscreenElement;
|
|
91
|
-
if (fsEl) {
|
|
92
|
-
if (document.exitFullscreen) document.exitFullscreen();
|
|
93
|
-
else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
|
|
94
|
-
} else {
|
|
95
|
-
if (sourceIframe.requestFullscreen) sourceIframe.requestFullscreen().catch(function () { });
|
|
96
|
-
else if (sourceIframe.webkitRequestFullscreen) sourceIframe.webkitRequestFullscreen();
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
// Simulated fullscreen path (iOS Safari / Chrome)
|
|
100
|
-
if (simulatedFullscreenIframe) {
|
|
101
|
-
exitSimulatedFullscreen();
|
|
102
|
-
} else {
|
|
103
|
-
enterSimulatedFullscreen(sourceIframe);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Fullscreen state query from iframe
|
|
109
|
-
if (event.data && event.data.type === 'pdf-secure-fullscreen-query') {
|
|
110
|
-
var fsActive = !!(document.fullscreenElement || document.webkitFullscreenElement) ||
|
|
111
|
-
(simulatedFullscreenIframe !== null);
|
|
112
|
-
if (event.source) {
|
|
113
|
-
event.source.postMessage({
|
|
114
|
-
type: 'pdf-secure-fullscreen-state',
|
|
115
|
-
isFullscreen: fsActive
|
|
116
|
-
}, event.origin);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Viewer asking for cached buffer
|
|
121
|
-
if (event.data && event.data.type === 'pdf-secure-cache-request') {
|
|
122
|
-
const { filename } = event.data;
|
|
123
|
-
const cached = pdfBufferCache.get(filename);
|
|
124
|
-
if (cached && event.source) {
|
|
125
|
-
// Send cached buffer to viewer (transferable for 0-copy)
|
|
126
|
-
// Clone once: keep original in cache, transfer the copy
|
|
127
|
-
const copy = cached.slice(0);
|
|
128
|
-
event.source.postMessage({
|
|
129
|
-
type: 'pdf-secure-cache-response',
|
|
130
|
-
filename: filename,
|
|
131
|
-
buffer: copy
|
|
132
|
-
}, event.origin, [copy]);
|
|
133
|
-
} else if (event.source) {
|
|
134
|
-
// No cache, viewer will fetch normally
|
|
135
|
-
event.source.postMessage({
|
|
136
|
-
type: 'pdf-secure-cache-response',
|
|
137
|
-
filename: filename,
|
|
138
|
-
buffer: null
|
|
139
|
-
}, event.origin);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Forward fullscreen state changes to all viewer iframes
|
|
145
|
-
function notifyFullscreenChange() {
|
|
146
|
-
var fsActive = !!(document.fullscreenElement || document.webkitFullscreenElement);
|
|
147
|
-
if (fsActive) {
|
|
148
|
-
document.body.style.overscrollBehavior = 'none';
|
|
149
|
-
document.body.style.overflow = 'hidden';
|
|
150
|
-
} else {
|
|
151
|
-
document.body.style.overscrollBehavior = '';
|
|
152
|
-
document.body.style.overflow = '';
|
|
153
|
-
}
|
|
154
|
-
document.querySelectorAll('.pdf-secure-iframe').forEach(function (f) {
|
|
155
|
-
if (f.contentWindow) {
|
|
156
|
-
f.style.overscrollBehavior = 'none';
|
|
157
|
-
f.contentWindow.postMessage({
|
|
158
|
-
type: 'pdf-secure-fullscreen-state',
|
|
159
|
-
isFullscreen: fsActive
|
|
160
|
-
}, window.location.origin);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
document.addEventListener('fullscreenchange', notifyFullscreenChange);
|
|
165
|
-
document.addEventListener('webkitfullscreenchange', notifyFullscreenChange);
|
|
166
|
-
|
|
167
|
-
// Touch handler to block parent scroll during simulated fullscreen
|
|
168
|
-
function parentFullscreenTouchHandler(e) {
|
|
169
|
-
e.preventDefault();
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Enter CSS simulated fullscreen (for iOS Safari/Chrome)
|
|
173
|
-
function enterSimulatedFullscreen(iframe) {
|
|
174
|
-
if (simulatedFullscreenIframe) return; // already in simulated fullscreen
|
|
175
|
-
simulatedFullscreenIframe = iframe;
|
|
176
|
-
|
|
177
|
-
// Save original iframe styles
|
|
178
|
-
iframe._savedStyle = {
|
|
179
|
-
position: iframe.style.position,
|
|
180
|
-
top: iframe.style.top,
|
|
181
|
-
left: iframe.style.left,
|
|
182
|
-
width: iframe.style.width,
|
|
183
|
-
height: iframe.style.height,
|
|
184
|
-
zIndex: iframe.style.zIndex
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// Apply fullscreen styles
|
|
188
|
-
iframe.style.position = 'fixed';
|
|
189
|
-
iframe.style.top = '0';
|
|
190
|
-
iframe.style.left = '0';
|
|
191
|
-
iframe.style.width = '100vw';
|
|
192
|
-
iframe.style.width = '100dvw'; // Override: dynamic viewport (excludes browser chrome)
|
|
193
|
-
iframe.style.height = '100vh';
|
|
194
|
-
iframe.style.height = '100dvh'; // Override: dynamic viewport (excludes address bar on mobile)
|
|
195
|
-
iframe.style.zIndex = '2147483647';
|
|
196
|
-
|
|
197
|
-
// Lock body scroll
|
|
198
|
-
savedBodyOverflow = document.body.style.overflow;
|
|
199
|
-
document.body.style.overflow = 'hidden';
|
|
200
|
-
document.body.style.overscrollBehavior = 'none';
|
|
201
|
-
|
|
202
|
-
// Block touch scroll on parent
|
|
203
|
-
document.addEventListener('touchmove', parentFullscreenTouchHandler, { passive: false });
|
|
204
|
-
|
|
205
|
-
// Notify iframe it is now fullscreen
|
|
206
|
-
if (iframe.contentWindow) {
|
|
207
|
-
iframe.contentWindow.postMessage({
|
|
208
|
-
type: 'pdf-secure-fullscreen-state',
|
|
209
|
-
isFullscreen: true
|
|
210
|
-
}, window.location.origin);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Exit CSS simulated fullscreen
|
|
215
|
-
function exitSimulatedFullscreen() {
|
|
216
|
-
if (!simulatedFullscreenIframe) return;
|
|
217
|
-
var iframe = simulatedFullscreenIframe;
|
|
218
|
-
simulatedFullscreenIframe = null;
|
|
219
|
-
|
|
220
|
-
// Restore original iframe styles
|
|
221
|
-
if (iframe._savedStyle) {
|
|
222
|
-
iframe.style.position = iframe._savedStyle.position;
|
|
223
|
-
iframe.style.top = iframe._savedStyle.top;
|
|
224
|
-
iframe.style.left = iframe._savedStyle.left;
|
|
225
|
-
iframe.style.width = iframe._savedStyle.width;
|
|
226
|
-
iframe.style.height = iframe._savedStyle.height;
|
|
227
|
-
iframe.style.zIndex = iframe._savedStyle.zIndex;
|
|
228
|
-
delete iframe._savedStyle;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Restore body scroll
|
|
232
|
-
document.body.style.overflow = savedBodyOverflow;
|
|
233
|
-
document.body.style.overscrollBehavior = '';
|
|
234
|
-
savedBodyOverflow = '';
|
|
235
|
-
|
|
236
|
-
// Remove parent touch block
|
|
237
|
-
document.removeEventListener('touchmove', parentFullscreenTouchHandler);
|
|
238
|
-
|
|
239
|
-
// Notify iframe it is no longer fullscreen
|
|
240
|
-
if (iframe.contentWindow) {
|
|
241
|
-
iframe.contentWindow.postMessage({
|
|
242
|
-
type: 'pdf-secure-fullscreen-state',
|
|
243
|
-
isFullscreen: false
|
|
244
|
-
}, window.location.origin);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
async function processQueue() {
|
|
249
|
-
if (isLoading || loadQueue.length === 0) return;
|
|
250
|
-
|
|
251
|
-
isLoading = true;
|
|
252
|
-
const { wrapper, filename, placeholder } = loadQueue.shift();
|
|
253
|
-
|
|
254
|
-
try {
|
|
255
|
-
await loadPdfIframe(wrapper, filename, placeholder);
|
|
256
|
-
} catch (err) {
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
isLoading = false;
|
|
260
|
-
|
|
261
|
-
// Small delay between loads
|
|
262
|
-
setTimeout(processQueue, 200);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function queuePdfLoad(wrapper, filename, placeholder) {
|
|
266
|
-
loadQueue.push({ wrapper, filename, placeholder });
|
|
267
|
-
processQueue();
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
try {
|
|
271
|
-
var hooks = await app.require('hooks');
|
|
272
|
-
|
|
273
|
-
hooks.on('action:ajaxify.end', function () {
|
|
274
|
-
// Clear queue on page change
|
|
275
|
-
loadQueue.length = 0;
|
|
276
|
-
isLoading = false;
|
|
277
|
-
currentResolver = null;
|
|
278
|
-
// Exit simulated fullscreen on SPA navigation
|
|
279
|
-
exitSimulatedFullscreen();
|
|
280
|
-
interceptPdfLinks();
|
|
281
|
-
});
|
|
282
|
-
} catch (err) {
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function interceptPdfLinks() {
|
|
286
|
-
var postContents = document.querySelectorAll('[component="post/content"]');
|
|
287
|
-
|
|
288
|
-
postContents.forEach(function (content) {
|
|
289
|
-
// NEW: Detect server-rendered secure placeholders (hides URL from source)
|
|
290
|
-
var placeholders = content.querySelectorAll('.pdf-secure-placeholder');
|
|
291
|
-
placeholders.forEach(function (placeholder) {
|
|
292
|
-
if (placeholder.dataset.pdfSecureProcessed) return;
|
|
293
|
-
placeholder.dataset.pdfSecureProcessed = 'true';
|
|
294
|
-
|
|
295
|
-
var filename = placeholder.dataset.filename;
|
|
296
|
-
var displayName = placeholder.querySelector('span')?.textContent || filename;
|
|
297
|
-
|
|
298
|
-
createPdfViewer(placeholder, filename, displayName);
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
// FALLBACK: Detect old-style PDF links (for backwards compatibility)
|
|
302
|
-
var pdfLinks = content.querySelectorAll('a[href$=".pdf"], a[href$=".PDF"]');
|
|
303
|
-
pdfLinks.forEach(function (link) {
|
|
304
|
-
if (link.dataset.pdfSecure) return;
|
|
305
|
-
link.dataset.pdfSecure = 'true';
|
|
306
|
-
|
|
307
|
-
var href = link.getAttribute('href');
|
|
308
|
-
var parts = href.split('/');
|
|
309
|
-
var filename = parts[parts.length - 1];
|
|
310
|
-
var displayName = link.textContent || filename;
|
|
311
|
-
|
|
312
|
-
createPdfViewer(link, filename, displayName);
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function createPdfViewer(targetElement, filename, displayName) {
|
|
318
|
-
|
|
319
|
-
// Create container
|
|
320
|
-
var container = document.createElement('div');
|
|
321
|
-
container.className = 'pdf-secure-embed';
|
|
322
|
-
container.style.cssText = 'margin:16px 0;border-radius:12px;overflow:hidden;background:#1f1f1f;border:1px solid rgba(255,255,255,0.1);box-shadow:0 4px 20px rgba(0,0,0,0.25);';
|
|
323
|
-
|
|
324
|
-
// Header
|
|
325
|
-
var header = document.createElement('div');
|
|
326
|
-
header.className = 'pdf-secure-embed-header';
|
|
327
|
-
header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background:linear-gradient(135deg,#2d2d2d 0%,#252525 100%);border-bottom:1px solid rgba(255,255,255,0.08);';
|
|
328
|
-
|
|
329
|
-
var title = document.createElement('div');
|
|
330
|
-
title.className = 'pdf-secure-embed-title';
|
|
331
|
-
title.style.cssText = 'display:flex;align-items:center;gap:10px;color:#fff;font-size:14px;font-weight:500;';
|
|
332
|
-
|
|
333
|
-
var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
334
|
-
icon.setAttribute('viewBox', '0 0 24 24');
|
|
335
|
-
icon.style.cssText = 'width:20px;height:20px;min-width:20px;max-width:20px;fill:#e81224;flex-shrink:0;';
|
|
336
|
-
icon.innerHTML = '<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/>';
|
|
337
|
-
|
|
338
|
-
var nameSpan = document.createElement('span');
|
|
339
|
-
nameSpan.style.cssText = 'white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:400px;';
|
|
340
|
-
try { nameSpan.textContent = decodeURIComponent(displayName); }
|
|
341
|
-
catch (e) { nameSpan.textContent = displayName; }
|
|
342
|
-
|
|
343
|
-
title.appendChild(icon);
|
|
344
|
-
title.appendChild(nameSpan);
|
|
345
|
-
|
|
346
|
-
header.appendChild(title);
|
|
347
|
-
container.appendChild(header);
|
|
348
|
-
|
|
349
|
-
// Body with loading placeholder
|
|
350
|
-
var iframeWrapper = document.createElement('div');
|
|
351
|
-
iframeWrapper.className = 'pdf-secure-embed-body';
|
|
352
|
-
iframeWrapper.style.cssText = 'position:relative;width:100%;height:600px;background:#525659;';
|
|
353
|
-
|
|
354
|
-
// Loading placeholder - ALWAYS VISIBLE until PDF ready (z-index: 10)
|
|
355
|
-
var loadingPlaceholder = document.createElement('div');
|
|
356
|
-
loadingPlaceholder.className = 'pdf-loading-placeholder';
|
|
357
|
-
loadingPlaceholder.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#2d2d2d;color:#fff;gap:16px;z-index:10;transition:opacity 0.3s;';
|
|
358
|
-
loadingPlaceholder.innerHTML = `
|
|
359
|
-
<svg viewBox="0 0 24 24" style="width:48px;height:48px;fill:#555;">
|
|
360
|
-
<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/>
|
|
361
|
-
</svg>
|
|
362
|
-
<div class="pdf-loading-text" style="font-size:14px;color:#a0a0a0;">Sırada bekliyor...</div>
|
|
363
|
-
<style>@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}</style>
|
|
364
|
-
`;
|
|
365
|
-
iframeWrapper.appendChild(loadingPlaceholder);
|
|
366
|
-
|
|
367
|
-
container.appendChild(iframeWrapper);
|
|
368
|
-
|
|
369
|
-
targetElement.replaceWith(container);
|
|
370
|
-
|
|
371
|
-
// LAZY LOADING with Intersection Observer + Queue
|
|
372
|
-
// Smart loading: only loads PDFs that are actually visible
|
|
373
|
-
var queueEntry = null; // Track if this PDF is in queue
|
|
374
|
-
var observer = new IntersectionObserver(function (entries) {
|
|
375
|
-
entries.forEach(function (entry) {
|
|
376
|
-
if (entry.isIntersecting) {
|
|
377
|
-
// Update placeholder to show loading state
|
|
378
|
-
var textEl = loadingPlaceholder.querySelector('.pdf-loading-text');
|
|
379
|
-
if (textEl) textEl.textContent = 'PDF Yükleniyor...';
|
|
380
|
-
|
|
381
|
-
var svgEl = loadingPlaceholder.querySelector('svg');
|
|
382
|
-
if (svgEl) {
|
|
383
|
-
svgEl.style.fill = '#0078d4';
|
|
384
|
-
svgEl.style.animation = 'spin 1s linear infinite';
|
|
385
|
-
svgEl.innerHTML = '<path d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8z"/>';
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Add to queue (if not already)
|
|
389
|
-
if (!queueEntry) {
|
|
390
|
-
queueEntry = { wrapper: iframeWrapper, filename, placeholder: loadingPlaceholder };
|
|
391
|
-
loadQueue.push(queueEntry);
|
|
392
|
-
processQueue();
|
|
393
|
-
}
|
|
394
|
-
} else {
|
|
395
|
-
// LEFT viewport - remove from queue if waiting
|
|
396
|
-
if (queueEntry && loadQueue.includes(queueEntry)) {
|
|
397
|
-
var idx = loadQueue.indexOf(queueEntry);
|
|
398
|
-
if (idx > -1) {
|
|
399
|
-
loadQueue.splice(idx, 1);
|
|
400
|
-
|
|
401
|
-
// Reset placeholder to waiting state
|
|
402
|
-
var textEl = loadingPlaceholder.querySelector('.pdf-loading-text');
|
|
403
|
-
if (textEl) textEl.textContent = 'Sırada bekliyor...';
|
|
404
|
-
var svgEl = loadingPlaceholder.querySelector('svg');
|
|
405
|
-
if (svgEl) {
|
|
406
|
-
svgEl.style.fill = '#555';
|
|
407
|
-
svgEl.style.animation = 'none';
|
|
408
|
-
svgEl.innerHTML = '<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/>';
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
queueEntry = null;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
});
|
|
415
|
-
}, {
|
|
416
|
-
rootMargin: '0px', // Only trigger when actually visible
|
|
417
|
-
threshold: 0
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
observer.observe(container);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function loadPdfIframe(wrapper, filename, placeholder) {
|
|
424
|
-
return new Promise((resolve, reject) => {
|
|
425
|
-
// Create iframe HIDDEN (z-index: 1, under placeholder)
|
|
426
|
-
var iframe = document.createElement('iframe');
|
|
427
|
-
iframe.className = 'pdf-secure-iframe';
|
|
428
|
-
iframe.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;border:none;z-index:1;';
|
|
429
|
-
iframe.src = config.relative_path + '/plugins/pdf-secure/viewer?file=' + encodeURIComponent(filename);
|
|
430
|
-
iframe.setAttribute('frameborder', '0');
|
|
431
|
-
iframe.setAttribute('allowfullscreen', 'true');
|
|
432
|
-
iframe.setAttribute('allow', 'fullscreen');
|
|
433
|
-
|
|
434
|
-
// Store resolver for postMessage callback
|
|
435
|
-
currentResolver = function () {
|
|
436
|
-
// Fade out placeholder, show iframe
|
|
437
|
-
if (placeholder) {
|
|
438
|
-
placeholder.style.opacity = '0';
|
|
439
|
-
setTimeout(function () {
|
|
440
|
-
if (placeholder.parentNode) {
|
|
441
|
-
placeholder.remove();
|
|
442
|
-
}
|
|
443
|
-
}, 300);
|
|
444
|
-
}
|
|
445
|
-
resolve();
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
iframe.onerror = function () {
|
|
449
|
-
currentResolver = null;
|
|
450
|
-
if (placeholder) {
|
|
451
|
-
var textEl = placeholder.querySelector('.pdf-loading-text');
|
|
452
|
-
if (textEl) textEl.textContent = 'Yükleme hatası!';
|
|
453
|
-
}
|
|
454
|
-
reject(new Error('Failed to load iframe'));
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
wrapper.appendChild(iframe);
|
|
458
|
-
|
|
459
|
-
// Timeout fallback (60 seconds for large PDFs)
|
|
460
|
-
setTimeout(function () {
|
|
461
|
-
if (currentResolver) {
|
|
462
|
-
currentResolver();
|
|
463
|
-
currentResolver = null;
|
|
464
|
-
}
|
|
465
|
-
}, 60000);
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
})();
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Main plugin logic - PDF links become inline embedded viewers with lazy loading + queue
|
|
4
|
+
(async function () {
|
|
5
|
+
// ============================================
|
|
6
|
+
// PDF.js PRELOAD - Cache CDN assets before iframe loads
|
|
7
|
+
// ============================================
|
|
8
|
+
(function preloadPdfJs() {
|
|
9
|
+
const preloads = [
|
|
10
|
+
{ href: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js', as: 'script' },
|
|
11
|
+
{ href: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css', as: 'style' }
|
|
12
|
+
];
|
|
13
|
+
preloads.forEach(({ href, as }) => {
|
|
14
|
+
if (!document.querySelector(`link[href="${href}"]`)) {
|
|
15
|
+
const link = document.createElement('link');
|
|
16
|
+
link.rel = 'preload';
|
|
17
|
+
link.href = href;
|
|
18
|
+
link.as = as;
|
|
19
|
+
link.crossOrigin = 'anonymous';
|
|
20
|
+
document.head.appendChild(link);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
})();
|
|
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
|
+
|
|
37
|
+
// Loading queue - only load one PDF at a time
|
|
38
|
+
const loadQueue = [];
|
|
39
|
+
let isLoading = false;
|
|
40
|
+
let currentResolver = null;
|
|
41
|
+
|
|
42
|
+
// ============================================
|
|
43
|
+
// SPA MEMORY CACHE - Cache decoded PDF buffers
|
|
44
|
+
// ============================================
|
|
45
|
+
const pdfBufferCache = new Map(); // filename -> ArrayBuffer
|
|
46
|
+
const CACHE_MAX_SIZE = 5; // ~50MB limit (avg 10MB per PDF)
|
|
47
|
+
let currentLoadingFilename = null;
|
|
48
|
+
|
|
49
|
+
function setCachedBuffer(filename, buffer) {
|
|
50
|
+
// Evict oldest if cache is full
|
|
51
|
+
if (pdfBufferCache.size >= CACHE_MAX_SIZE) {
|
|
52
|
+
const firstKey = pdfBufferCache.keys().next().value;
|
|
53
|
+
pdfBufferCache.delete(firstKey);
|
|
54
|
+
}
|
|
55
|
+
pdfBufferCache.set(filename, buffer);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Listen for postMessage from iframe
|
|
59
|
+
window.addEventListener('message', function (event) {
|
|
60
|
+
// Security: Only accept messages from same origin
|
|
61
|
+
if (event.origin !== window.location.origin) return;
|
|
62
|
+
|
|
63
|
+
// PDF ready - resolve queue
|
|
64
|
+
if (event.data && event.data.type === 'pdf-secure-ready') {
|
|
65
|
+
if (currentResolver) {
|
|
66
|
+
currentResolver();
|
|
67
|
+
currentResolver = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// PDF buffer from viewer - cache it
|
|
72
|
+
if (event.data && event.data.type === 'pdf-secure-buffer') {
|
|
73
|
+
const { filename, buffer } = event.data;
|
|
74
|
+
if (filename && buffer) {
|
|
75
|
+
setCachedBuffer(filename, buffer);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Fullscreen toggle request from iframe viewer
|
|
80
|
+
if (event.data && event.data.type === 'pdf-secure-fullscreen-toggle') {
|
|
81
|
+
var sourceIframe = document.querySelector('.pdf-secure-iframe');
|
|
82
|
+
// Find the specific iframe that sent the message
|
|
83
|
+
document.querySelectorAll('.pdf-secure-iframe').forEach(function (f) {
|
|
84
|
+
if (f.contentWindow === event.source) sourceIframe = f;
|
|
85
|
+
});
|
|
86
|
+
if (!sourceIframe) return;
|
|
87
|
+
|
|
88
|
+
if (fullscreenApiSupported) {
|
|
89
|
+
// Native fullscreen path
|
|
90
|
+
var fsEl = document.fullscreenElement || document.webkitFullscreenElement;
|
|
91
|
+
if (fsEl) {
|
|
92
|
+
if (document.exitFullscreen) document.exitFullscreen();
|
|
93
|
+
else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
|
|
94
|
+
} else {
|
|
95
|
+
if (sourceIframe.requestFullscreen) sourceIframe.requestFullscreen().catch(function () { });
|
|
96
|
+
else if (sourceIframe.webkitRequestFullscreen) sourceIframe.webkitRequestFullscreen();
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
// Simulated fullscreen path (iOS Safari / Chrome)
|
|
100
|
+
if (simulatedFullscreenIframe) {
|
|
101
|
+
exitSimulatedFullscreen();
|
|
102
|
+
} else {
|
|
103
|
+
enterSimulatedFullscreen(sourceIframe);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Fullscreen state query from iframe
|
|
109
|
+
if (event.data && event.data.type === 'pdf-secure-fullscreen-query') {
|
|
110
|
+
var fsActive = !!(document.fullscreenElement || document.webkitFullscreenElement) ||
|
|
111
|
+
(simulatedFullscreenIframe !== null);
|
|
112
|
+
if (event.source) {
|
|
113
|
+
event.source.postMessage({
|
|
114
|
+
type: 'pdf-secure-fullscreen-state',
|
|
115
|
+
isFullscreen: fsActive
|
|
116
|
+
}, event.origin);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Viewer asking for cached buffer
|
|
121
|
+
if (event.data && event.data.type === 'pdf-secure-cache-request') {
|
|
122
|
+
const { filename } = event.data;
|
|
123
|
+
const cached = pdfBufferCache.get(filename);
|
|
124
|
+
if (cached && event.source) {
|
|
125
|
+
// Send cached buffer to viewer (transferable for 0-copy)
|
|
126
|
+
// Clone once: keep original in cache, transfer the copy
|
|
127
|
+
const copy = cached.slice(0);
|
|
128
|
+
event.source.postMessage({
|
|
129
|
+
type: 'pdf-secure-cache-response',
|
|
130
|
+
filename: filename,
|
|
131
|
+
buffer: copy
|
|
132
|
+
}, event.origin, [copy]);
|
|
133
|
+
} else if (event.source) {
|
|
134
|
+
// No cache, viewer will fetch normally
|
|
135
|
+
event.source.postMessage({
|
|
136
|
+
type: 'pdf-secure-cache-response',
|
|
137
|
+
filename: filename,
|
|
138
|
+
buffer: null
|
|
139
|
+
}, event.origin);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Forward fullscreen state changes to all viewer iframes
|
|
145
|
+
function notifyFullscreenChange() {
|
|
146
|
+
var fsActive = !!(document.fullscreenElement || document.webkitFullscreenElement);
|
|
147
|
+
if (fsActive) {
|
|
148
|
+
document.body.style.overscrollBehavior = 'none';
|
|
149
|
+
document.body.style.overflow = 'hidden';
|
|
150
|
+
} else {
|
|
151
|
+
document.body.style.overscrollBehavior = '';
|
|
152
|
+
document.body.style.overflow = '';
|
|
153
|
+
}
|
|
154
|
+
document.querySelectorAll('.pdf-secure-iframe').forEach(function (f) {
|
|
155
|
+
if (f.contentWindow) {
|
|
156
|
+
f.style.overscrollBehavior = 'none';
|
|
157
|
+
f.contentWindow.postMessage({
|
|
158
|
+
type: 'pdf-secure-fullscreen-state',
|
|
159
|
+
isFullscreen: fsActive
|
|
160
|
+
}, window.location.origin);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
document.addEventListener('fullscreenchange', notifyFullscreenChange);
|
|
165
|
+
document.addEventListener('webkitfullscreenchange', notifyFullscreenChange);
|
|
166
|
+
|
|
167
|
+
// Touch handler to block parent scroll during simulated fullscreen
|
|
168
|
+
function parentFullscreenTouchHandler(e) {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Enter CSS simulated fullscreen (for iOS Safari/Chrome)
|
|
173
|
+
function enterSimulatedFullscreen(iframe) {
|
|
174
|
+
if (simulatedFullscreenIframe) return; // already in simulated fullscreen
|
|
175
|
+
simulatedFullscreenIframe = iframe;
|
|
176
|
+
|
|
177
|
+
// Save original iframe styles
|
|
178
|
+
iframe._savedStyle = {
|
|
179
|
+
position: iframe.style.position,
|
|
180
|
+
top: iframe.style.top,
|
|
181
|
+
left: iframe.style.left,
|
|
182
|
+
width: iframe.style.width,
|
|
183
|
+
height: iframe.style.height,
|
|
184
|
+
zIndex: iframe.style.zIndex
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Apply fullscreen styles
|
|
188
|
+
iframe.style.position = 'fixed';
|
|
189
|
+
iframe.style.top = '0';
|
|
190
|
+
iframe.style.left = '0';
|
|
191
|
+
iframe.style.width = '100vw';
|
|
192
|
+
iframe.style.width = '100dvw'; // Override: dynamic viewport (excludes browser chrome)
|
|
193
|
+
iframe.style.height = '100vh';
|
|
194
|
+
iframe.style.height = '100dvh'; // Override: dynamic viewport (excludes address bar on mobile)
|
|
195
|
+
iframe.style.zIndex = '2147483647';
|
|
196
|
+
|
|
197
|
+
// Lock body scroll
|
|
198
|
+
savedBodyOverflow = document.body.style.overflow;
|
|
199
|
+
document.body.style.overflow = 'hidden';
|
|
200
|
+
document.body.style.overscrollBehavior = 'none';
|
|
201
|
+
|
|
202
|
+
// Block touch scroll on parent
|
|
203
|
+
document.addEventListener('touchmove', parentFullscreenTouchHandler, { passive: false });
|
|
204
|
+
|
|
205
|
+
// Notify iframe it is now fullscreen
|
|
206
|
+
if (iframe.contentWindow) {
|
|
207
|
+
iframe.contentWindow.postMessage({
|
|
208
|
+
type: 'pdf-secure-fullscreen-state',
|
|
209
|
+
isFullscreen: true
|
|
210
|
+
}, window.location.origin);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Exit CSS simulated fullscreen
|
|
215
|
+
function exitSimulatedFullscreen() {
|
|
216
|
+
if (!simulatedFullscreenIframe) return;
|
|
217
|
+
var iframe = simulatedFullscreenIframe;
|
|
218
|
+
simulatedFullscreenIframe = null;
|
|
219
|
+
|
|
220
|
+
// Restore original iframe styles
|
|
221
|
+
if (iframe._savedStyle) {
|
|
222
|
+
iframe.style.position = iframe._savedStyle.position;
|
|
223
|
+
iframe.style.top = iframe._savedStyle.top;
|
|
224
|
+
iframe.style.left = iframe._savedStyle.left;
|
|
225
|
+
iframe.style.width = iframe._savedStyle.width;
|
|
226
|
+
iframe.style.height = iframe._savedStyle.height;
|
|
227
|
+
iframe.style.zIndex = iframe._savedStyle.zIndex;
|
|
228
|
+
delete iframe._savedStyle;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Restore body scroll
|
|
232
|
+
document.body.style.overflow = savedBodyOverflow;
|
|
233
|
+
document.body.style.overscrollBehavior = '';
|
|
234
|
+
savedBodyOverflow = '';
|
|
235
|
+
|
|
236
|
+
// Remove parent touch block
|
|
237
|
+
document.removeEventListener('touchmove', parentFullscreenTouchHandler);
|
|
238
|
+
|
|
239
|
+
// Notify iframe it is no longer fullscreen
|
|
240
|
+
if (iframe.contentWindow) {
|
|
241
|
+
iframe.contentWindow.postMessage({
|
|
242
|
+
type: 'pdf-secure-fullscreen-state',
|
|
243
|
+
isFullscreen: false
|
|
244
|
+
}, window.location.origin);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function processQueue() {
|
|
249
|
+
if (isLoading || loadQueue.length === 0) return;
|
|
250
|
+
|
|
251
|
+
isLoading = true;
|
|
252
|
+
const { wrapper, filename, placeholder } = loadQueue.shift();
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
await loadPdfIframe(wrapper, filename, placeholder);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
isLoading = false;
|
|
260
|
+
|
|
261
|
+
// Small delay between loads
|
|
262
|
+
setTimeout(processQueue, 200);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function queuePdfLoad(wrapper, filename, placeholder) {
|
|
266
|
+
loadQueue.push({ wrapper, filename, placeholder });
|
|
267
|
+
processQueue();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
var hooks = await app.require('hooks');
|
|
272
|
+
|
|
273
|
+
hooks.on('action:ajaxify.end', function () {
|
|
274
|
+
// Clear queue on page change
|
|
275
|
+
loadQueue.length = 0;
|
|
276
|
+
isLoading = false;
|
|
277
|
+
currentResolver = null;
|
|
278
|
+
// Exit simulated fullscreen on SPA navigation
|
|
279
|
+
exitSimulatedFullscreen();
|
|
280
|
+
interceptPdfLinks();
|
|
281
|
+
});
|
|
282
|
+
} catch (err) {
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function interceptPdfLinks() {
|
|
286
|
+
var postContents = document.querySelectorAll('[component="post/content"]');
|
|
287
|
+
|
|
288
|
+
postContents.forEach(function (content) {
|
|
289
|
+
// NEW: Detect server-rendered secure placeholders (hides URL from source)
|
|
290
|
+
var placeholders = content.querySelectorAll('.pdf-secure-placeholder');
|
|
291
|
+
placeholders.forEach(function (placeholder) {
|
|
292
|
+
if (placeholder.dataset.pdfSecureProcessed) return;
|
|
293
|
+
placeholder.dataset.pdfSecureProcessed = 'true';
|
|
294
|
+
|
|
295
|
+
var filename = placeholder.dataset.filename;
|
|
296
|
+
var displayName = placeholder.querySelector('span')?.textContent || filename;
|
|
297
|
+
|
|
298
|
+
createPdfViewer(placeholder, filename, displayName);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// FALLBACK: Detect old-style PDF links (for backwards compatibility)
|
|
302
|
+
var pdfLinks = content.querySelectorAll('a[href$=".pdf"], a[href$=".PDF"]');
|
|
303
|
+
pdfLinks.forEach(function (link) {
|
|
304
|
+
if (link.dataset.pdfSecure) return;
|
|
305
|
+
link.dataset.pdfSecure = 'true';
|
|
306
|
+
|
|
307
|
+
var href = link.getAttribute('href');
|
|
308
|
+
var parts = href.split('/');
|
|
309
|
+
var filename = parts[parts.length - 1];
|
|
310
|
+
var displayName = link.textContent || filename;
|
|
311
|
+
|
|
312
|
+
createPdfViewer(link, filename, displayName);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function createPdfViewer(targetElement, filename, displayName) {
|
|
318
|
+
|
|
319
|
+
// Create container
|
|
320
|
+
var container = document.createElement('div');
|
|
321
|
+
container.className = 'pdf-secure-embed';
|
|
322
|
+
container.style.cssText = 'margin:16px 0;border-radius:12px;overflow:hidden;background:#1f1f1f;border:1px solid rgba(255,255,255,0.1);box-shadow:0 4px 20px rgba(0,0,0,0.25);';
|
|
323
|
+
|
|
324
|
+
// Header
|
|
325
|
+
var header = document.createElement('div');
|
|
326
|
+
header.className = 'pdf-secure-embed-header';
|
|
327
|
+
header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background:linear-gradient(135deg,#2d2d2d 0%,#252525 100%);border-bottom:1px solid rgba(255,255,255,0.08);';
|
|
328
|
+
|
|
329
|
+
var title = document.createElement('div');
|
|
330
|
+
title.className = 'pdf-secure-embed-title';
|
|
331
|
+
title.style.cssText = 'display:flex;align-items:center;gap:10px;color:#fff;font-size:14px;font-weight:500;';
|
|
332
|
+
|
|
333
|
+
var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
334
|
+
icon.setAttribute('viewBox', '0 0 24 24');
|
|
335
|
+
icon.style.cssText = 'width:20px;height:20px;min-width:20px;max-width:20px;fill:#e81224;flex-shrink:0;';
|
|
336
|
+
icon.innerHTML = '<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/>';
|
|
337
|
+
|
|
338
|
+
var nameSpan = document.createElement('span');
|
|
339
|
+
nameSpan.style.cssText = 'white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:400px;';
|
|
340
|
+
try { nameSpan.textContent = decodeURIComponent(displayName); }
|
|
341
|
+
catch (e) { nameSpan.textContent = displayName; }
|
|
342
|
+
|
|
343
|
+
title.appendChild(icon);
|
|
344
|
+
title.appendChild(nameSpan);
|
|
345
|
+
|
|
346
|
+
header.appendChild(title);
|
|
347
|
+
container.appendChild(header);
|
|
348
|
+
|
|
349
|
+
// Body with loading placeholder
|
|
350
|
+
var iframeWrapper = document.createElement('div');
|
|
351
|
+
iframeWrapper.className = 'pdf-secure-embed-body';
|
|
352
|
+
iframeWrapper.style.cssText = 'position:relative;width:100%;height:600px;background:#525659;';
|
|
353
|
+
|
|
354
|
+
// Loading placeholder - ALWAYS VISIBLE until PDF ready (z-index: 10)
|
|
355
|
+
var loadingPlaceholder = document.createElement('div');
|
|
356
|
+
loadingPlaceholder.className = 'pdf-loading-placeholder';
|
|
357
|
+
loadingPlaceholder.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#2d2d2d;color:#fff;gap:16px;z-index:10;transition:opacity 0.3s;';
|
|
358
|
+
loadingPlaceholder.innerHTML = `
|
|
359
|
+
<svg viewBox="0 0 24 24" style="width:48px;height:48px;fill:#555;">
|
|
360
|
+
<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/>
|
|
361
|
+
</svg>
|
|
362
|
+
<div class="pdf-loading-text" style="font-size:14px;color:#a0a0a0;">Sırada bekliyor...</div>
|
|
363
|
+
<style>@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}</style>
|
|
364
|
+
`;
|
|
365
|
+
iframeWrapper.appendChild(loadingPlaceholder);
|
|
366
|
+
|
|
367
|
+
container.appendChild(iframeWrapper);
|
|
368
|
+
|
|
369
|
+
targetElement.replaceWith(container);
|
|
370
|
+
|
|
371
|
+
// LAZY LOADING with Intersection Observer + Queue
|
|
372
|
+
// Smart loading: only loads PDFs that are actually visible
|
|
373
|
+
var queueEntry = null; // Track if this PDF is in queue
|
|
374
|
+
var observer = new IntersectionObserver(function (entries) {
|
|
375
|
+
entries.forEach(function (entry) {
|
|
376
|
+
if (entry.isIntersecting) {
|
|
377
|
+
// Update placeholder to show loading state
|
|
378
|
+
var textEl = loadingPlaceholder.querySelector('.pdf-loading-text');
|
|
379
|
+
if (textEl) textEl.textContent = 'PDF Yükleniyor...';
|
|
380
|
+
|
|
381
|
+
var svgEl = loadingPlaceholder.querySelector('svg');
|
|
382
|
+
if (svgEl) {
|
|
383
|
+
svgEl.style.fill = '#0078d4';
|
|
384
|
+
svgEl.style.animation = 'spin 1s linear infinite';
|
|
385
|
+
svgEl.innerHTML = '<path d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8z"/>';
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Add to queue (if not already)
|
|
389
|
+
if (!queueEntry) {
|
|
390
|
+
queueEntry = { wrapper: iframeWrapper, filename, placeholder: loadingPlaceholder };
|
|
391
|
+
loadQueue.push(queueEntry);
|
|
392
|
+
processQueue();
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
// LEFT viewport - remove from queue if waiting
|
|
396
|
+
if (queueEntry && loadQueue.includes(queueEntry)) {
|
|
397
|
+
var idx = loadQueue.indexOf(queueEntry);
|
|
398
|
+
if (idx > -1) {
|
|
399
|
+
loadQueue.splice(idx, 1);
|
|
400
|
+
|
|
401
|
+
// Reset placeholder to waiting state
|
|
402
|
+
var textEl = loadingPlaceholder.querySelector('.pdf-loading-text');
|
|
403
|
+
if (textEl) textEl.textContent = 'Sırada bekliyor...';
|
|
404
|
+
var svgEl = loadingPlaceholder.querySelector('svg');
|
|
405
|
+
if (svgEl) {
|
|
406
|
+
svgEl.style.fill = '#555';
|
|
407
|
+
svgEl.style.animation = 'none';
|
|
408
|
+
svgEl.innerHTML = '<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/>';
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
queueEntry = null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
}, {
|
|
416
|
+
rootMargin: '0px', // Only trigger when actually visible
|
|
417
|
+
threshold: 0
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
observer.observe(container);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function loadPdfIframe(wrapper, filename, placeholder) {
|
|
424
|
+
return new Promise((resolve, reject) => {
|
|
425
|
+
// Create iframe HIDDEN (z-index: 1, under placeholder)
|
|
426
|
+
var iframe = document.createElement('iframe');
|
|
427
|
+
iframe.className = 'pdf-secure-iframe';
|
|
428
|
+
iframe.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;border:none;z-index:1;';
|
|
429
|
+
iframe.src = config.relative_path + '/plugins/pdf-secure/viewer?file=' + encodeURIComponent(filename);
|
|
430
|
+
iframe.setAttribute('frameborder', '0');
|
|
431
|
+
iframe.setAttribute('allowfullscreen', 'true');
|
|
432
|
+
iframe.setAttribute('allow', 'fullscreen');
|
|
433
|
+
|
|
434
|
+
// Store resolver for postMessage callback
|
|
435
|
+
currentResolver = function () {
|
|
436
|
+
// Fade out placeholder, show iframe
|
|
437
|
+
if (placeholder) {
|
|
438
|
+
placeholder.style.opacity = '0';
|
|
439
|
+
setTimeout(function () {
|
|
440
|
+
if (placeholder.parentNode) {
|
|
441
|
+
placeholder.remove();
|
|
442
|
+
}
|
|
443
|
+
}, 300);
|
|
444
|
+
}
|
|
445
|
+
resolve();
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
iframe.onerror = function () {
|
|
449
|
+
currentResolver = null;
|
|
450
|
+
if (placeholder) {
|
|
451
|
+
var textEl = placeholder.querySelector('.pdf-loading-text');
|
|
452
|
+
if (textEl) textEl.textContent = 'Yükleme hatası!';
|
|
453
|
+
}
|
|
454
|
+
reject(new Error('Failed to load iframe'));
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
wrapper.appendChild(iframe);
|
|
458
|
+
|
|
459
|
+
// Timeout fallback (60 seconds for large PDFs)
|
|
460
|
+
setTimeout(function () {
|
|
461
|
+
if (currentResolver) {
|
|
462
|
+
currentResolver();
|
|
463
|
+
currentResolver = null;
|
|
464
|
+
}
|
|
465
|
+
}, 60000);
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
})();
|