nodebb-plugin-pdf-secure 1.2.20 → 1.2.22
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/image.png +0 -0
- package/static/viewer.html +127 -78
package/package.json
CHANGED
package/static/image.png
ADDED
|
Binary file
|
package/static/viewer.html
CHANGED
|
@@ -1573,47 +1573,46 @@
|
|
|
1573
1573
|
}
|
|
1574
1574
|
}
|
|
1575
1575
|
|
|
1576
|
-
/*
|
|
1577
|
-
|
|
1576
|
+
/* Per-page lock overlay - her kilitli sayfa icin */
|
|
1577
|
+
.page-lock-overlay {
|
|
1578
|
+
position: absolute;
|
|
1579
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
1578
1580
|
display: flex;
|
|
1579
1581
|
flex-direction: column;
|
|
1580
1582
|
align-items: center;
|
|
1581
1583
|
justify-content: center;
|
|
1582
|
-
padding: 60px 20px;
|
|
1583
|
-
text-align: center;
|
|
1584
1584
|
background: linear-gradient(180deg, rgba(31,31,31,0.95) 0%, rgba(20,20,20,0.98) 100%);
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
max-width: 100%;
|
|
1585
|
+
z-index: 15;
|
|
1586
|
+
text-align: center;
|
|
1587
|
+
padding: 20px;
|
|
1589
1588
|
}
|
|
1590
1589
|
|
|
1591
|
-
.
|
|
1592
|
-
margin-bottom:
|
|
1590
|
+
.page-lock-icon {
|
|
1591
|
+
margin-bottom: 16px;
|
|
1593
1592
|
opacity: 0.9;
|
|
1594
1593
|
}
|
|
1595
1594
|
|
|
1596
|
-
.
|
|
1597
|
-
font-size:
|
|
1595
|
+
.page-lock-title {
|
|
1596
|
+
font-size: 20px;
|
|
1598
1597
|
font-weight: 600;
|
|
1599
1598
|
color: #ffd700;
|
|
1600
|
-
margin-bottom:
|
|
1599
|
+
margin-bottom: 10px;
|
|
1601
1600
|
}
|
|
1602
1601
|
|
|
1603
|
-
.
|
|
1604
|
-
font-size:
|
|
1602
|
+
.page-lock-message {
|
|
1603
|
+
font-size: 14px;
|
|
1605
1604
|
color: #a0a0a0;
|
|
1606
|
-
margin-bottom:
|
|
1607
|
-
max-width:
|
|
1605
|
+
margin-bottom: 24px;
|
|
1606
|
+
max-width: 320px;
|
|
1608
1607
|
line-height: 1.5;
|
|
1609
1608
|
}
|
|
1610
1609
|
|
|
1611
|
-
.
|
|
1610
|
+
.page-lock-button {
|
|
1612
1611
|
display: inline-block;
|
|
1613
|
-
padding:
|
|
1612
|
+
padding: 12px 36px;
|
|
1614
1613
|
background: linear-gradient(135deg, #ffd700 0%, #ffaa00 100%);
|
|
1615
1614
|
color: #1a1a1a;
|
|
1616
|
-
font-size:
|
|
1615
|
+
font-size: 15px;
|
|
1617
1616
|
font-weight: 700;
|
|
1618
1617
|
border-radius: 8px;
|
|
1619
1618
|
text-decoration: none;
|
|
@@ -1621,22 +1620,29 @@
|
|
|
1621
1620
|
box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3);
|
|
1622
1621
|
}
|
|
1623
1622
|
|
|
1624
|
-
.
|
|
1623
|
+
.page-lock-button:hover {
|
|
1625
1624
|
transform: translateY(-2px);
|
|
1626
1625
|
box-shadow: 0 6px 20px rgba(255, 215, 0, 0.4);
|
|
1627
1626
|
}
|
|
1628
1627
|
|
|
1629
|
-
.
|
|
1630
|
-
margin-top:
|
|
1631
|
-
font-size:
|
|
1628
|
+
.page-lock-secondary {
|
|
1629
|
+
margin-top: 16px;
|
|
1630
|
+
font-size: 13px;
|
|
1632
1631
|
color: #888;
|
|
1633
1632
|
font-style: italic;
|
|
1634
1633
|
}
|
|
1635
1634
|
|
|
1635
|
+
/* Blur locked page content - even if overlay removed, content unreadable */
|
|
1636
|
+
.page-locked-blur canvas,
|
|
1637
|
+
.page-locked-blur .textLayer,
|
|
1638
|
+
.page-locked-blur .annotationLayer {
|
|
1639
|
+
filter: blur(8px) !important;
|
|
1640
|
+
-webkit-filter: blur(8px) !important;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1636
1643
|
/* Locked Thumbnails */
|
|
1637
1644
|
.thumbnail.locked {
|
|
1638
|
-
opacity: 0.
|
|
1639
|
-
cursor: not-allowed;
|
|
1645
|
+
opacity: 0.5;
|
|
1640
1646
|
position: relative;
|
|
1641
1647
|
}
|
|
1642
1648
|
|
|
@@ -1654,22 +1660,17 @@
|
|
|
1654
1660
|
}
|
|
1655
1661
|
|
|
1656
1662
|
@media (max-width: 768px) {
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
min-height: 300px;
|
|
1663
|
+
.page-lock-title {
|
|
1664
|
+
font-size: 17px;
|
|
1660
1665
|
}
|
|
1661
1666
|
|
|
1662
|
-
.
|
|
1663
|
-
font-size:
|
|
1667
|
+
.page-lock-message {
|
|
1668
|
+
font-size: 13px;
|
|
1664
1669
|
}
|
|
1665
1670
|
|
|
1666
|
-
.
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
.premium-lock-button {
|
|
1671
|
-
padding: 12px 32px;
|
|
1672
|
-
font-size: 14px;
|
|
1671
|
+
.page-lock-button {
|
|
1672
|
+
padding: 10px 28px;
|
|
1673
|
+
font-size: 13px;
|
|
1673
1674
|
}
|
|
1674
1675
|
}
|
|
1675
1676
|
</style>
|
|
@@ -2224,24 +2225,61 @@
|
|
|
2224
2225
|
return data.buffer;
|
|
2225
2226
|
}
|
|
2226
2227
|
|
|
2227
|
-
function
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2228
|
+
function injectPageLock(pageEl) {
|
|
2229
|
+
if (!pageEl) return;
|
|
2230
|
+
// Add blur class to page content
|
|
2231
|
+
if (!pageEl.classList.contains('page-locked-blur')) {
|
|
2232
|
+
pageEl.classList.add('page-locked-blur');
|
|
2233
|
+
}
|
|
2234
|
+
// Don't duplicate overlay
|
|
2235
|
+
if (pageEl.querySelector('.page-lock-overlay')) return;
|
|
2231
2236
|
var overlay = document.createElement('div');
|
|
2232
|
-
overlay.
|
|
2237
|
+
overlay.className = 'page-lock-overlay';
|
|
2233
2238
|
overlay.innerHTML = '\
|
|
2234
|
-
<div class="
|
|
2235
|
-
<svg viewBox="0 0 24 24" width="
|
|
2239
|
+
<div class="page-lock-icon">\
|
|
2240
|
+
<svg viewBox="0 0 24 24" width="48" height="48" fill="#ffd700">\
|
|
2236
2241
|
<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2z"/>\
|
|
2237
2242
|
</svg>\
|
|
2238
2243
|
</div>\
|
|
2239
|
-
<div class="
|
|
2240
|
-
<div class="
|
|
2241
|
-
<a href="https://forumtest.ieu.app/premium" target="_blank" class="
|
|
2242
|
-
<div class="
|
|
2244
|
+
<div class="page-lock-title">Kilitli</div>\
|
|
2245
|
+
<div class="page-lock-message">Bu sayfayi goruntulemeye devam etmek icin Premium uyelik gereklidir.</div>\
|
|
2246
|
+
<a href="https://forumtest.ieu.app/premium" target="_blank" class="page-lock-button">Premium Satin Al</a>\
|
|
2247
|
+
<div class="page-lock-secondary">Materyal yukleyerek de Premium olabilirsiniz!</div>';
|
|
2248
|
+
pageEl.style.position = 'relative';
|
|
2249
|
+
pageEl.appendChild(overlay);
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
function isOverlayTampered(overlay) {
|
|
2253
|
+
var s = overlay.style;
|
|
2254
|
+
return s.position !== 'absolute' || s.display !== 'flex' ||
|
|
2255
|
+
s.zIndex !== '15' || s.opacity !== '1' ||
|
|
2256
|
+
s.visibility !== 'visible' || s.pointerEvents !== 'auto';
|
|
2257
|
+
}
|
|
2243
2258
|
|
|
2244
|
-
|
|
2259
|
+
function resetOverlayCSS(overlay) {
|
|
2260
|
+
if (!isOverlayTampered(overlay)) return; // Skip if already correct (prevents observer loop)
|
|
2261
|
+
overlay.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:0;display:flex;z-index:15;opacity:1;visibility:visible;pointer-events:auto;';
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
function applyPageLocks() {
|
|
2265
|
+
var pages = document.querySelectorAll('#viewer .page');
|
|
2266
|
+
pages.forEach(function (page) {
|
|
2267
|
+
var pageNum = parseInt(page.dataset.pageNumber || '0', 10);
|
|
2268
|
+
if (pageNum > 1) {
|
|
2269
|
+
// Ensure page is visible (undo any old display:none)
|
|
2270
|
+
if (page.style.display === 'none') {
|
|
2271
|
+
page.style.display = '';
|
|
2272
|
+
}
|
|
2273
|
+
injectPageLock(page);
|
|
2274
|
+
// CSS integrity: reset overlay styles in case of tampering
|
|
2275
|
+
var existing = page.querySelector('.page-lock-overlay');
|
|
2276
|
+
if (existing) resetOverlayCSS(existing);
|
|
2277
|
+
// Ensure blur class is present
|
|
2278
|
+
if (!page.classList.contains('page-locked-blur')) {
|
|
2279
|
+
page.classList.add('page-locked-blur');
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2245
2283
|
}
|
|
2246
2284
|
|
|
2247
2285
|
// ============================================
|
|
@@ -2250,18 +2288,7 @@
|
|
|
2250
2288
|
function startPeriodicCheck() {
|
|
2251
2289
|
setInterval(function () {
|
|
2252
2290
|
if (!premiumInfo || premiumInfo.isPremium) return;
|
|
2253
|
-
|
|
2254
|
-
pages.forEach(function (page, idx) {
|
|
2255
|
-
if (idx > 0 && page.style.display !== 'none') {
|
|
2256
|
-
page.style.display = 'none';
|
|
2257
|
-
}
|
|
2258
|
-
});
|
|
2259
|
-
if (!document.getElementById('premiumLockOverlay')) {
|
|
2260
|
-
showPremiumLockOverlay(premiumInfo.totalPages);
|
|
2261
|
-
}
|
|
2262
|
-
if (pdfViewer && pdfViewer.currentPageNumber > 1) {
|
|
2263
|
-
pdfViewer.currentPageNumber = 1;
|
|
2264
|
-
}
|
|
2291
|
+
applyPageLocks();
|
|
2265
2292
|
}, 2000);
|
|
2266
2293
|
}
|
|
2267
2294
|
|
|
@@ -2272,32 +2299,43 @@
|
|
|
2272
2299
|
var viewerEl = document.getElementById('viewer');
|
|
2273
2300
|
if (!viewerEl) return;
|
|
2274
2301
|
|
|
2302
|
+
// Observer 1: Watch for lock overlay removal from any page
|
|
2275
2303
|
new MutationObserver(function (mutations) {
|
|
2304
|
+
if (!premiumInfo || premiumInfo.isPremium) return;
|
|
2276
2305
|
for (var i = 0; i < mutations.length; i++) {
|
|
2277
2306
|
var removed = mutations[i].removedNodes;
|
|
2278
2307
|
for (var j = 0; j < removed.length; j++) {
|
|
2279
|
-
if (removed[j].
|
|
2280
|
-
|
|
2281
|
-
|
|
2308
|
+
if (removed[j].classList && removed[j].classList.contains('page-lock-overlay')) {
|
|
2309
|
+
var pageEl = mutations[i].target;
|
|
2310
|
+
if (pageEl && pageEl.classList && pageEl.classList.contains('page')) {
|
|
2311
|
+
injectPageLock(pageEl);
|
|
2312
|
+
}
|
|
2282
2313
|
}
|
|
2283
2314
|
}
|
|
2284
2315
|
}
|
|
2285
|
-
}).observe(viewerEl, { childList: true });
|
|
2316
|
+
}).observe(viewerEl, { childList: true, subtree: true });
|
|
2286
2317
|
|
|
2318
|
+
// Observer 2: Watch for style/class tampering on overlays and pages
|
|
2287
2319
|
new MutationObserver(function (mutations) {
|
|
2320
|
+
if (!premiumInfo || premiumInfo.isPremium) return;
|
|
2288
2321
|
for (var i = 0; i < mutations.length; i++) {
|
|
2289
|
-
var
|
|
2290
|
-
if (
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2322
|
+
var target = mutations[i].target;
|
|
2323
|
+
if (!target || !target.classList) continue;
|
|
2324
|
+
// Overlay CSS tampered - reset only if values actually differ
|
|
2325
|
+
if (target.classList.contains('page-lock-overlay')) {
|
|
2326
|
+
if (isOverlayTampered(target)) {
|
|
2327
|
+
resetOverlayCSS(target);
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
// Blur class removed from locked page - re-add it
|
|
2331
|
+
if (target.classList.contains('page') && !target.classList.contains('page-locked-blur')) {
|
|
2332
|
+
var pageNum = parseInt(target.dataset.pageNumber || '0', 10);
|
|
2333
|
+
if (pageNum > 1) {
|
|
2334
|
+
target.classList.add('page-locked-blur');
|
|
2297
2335
|
}
|
|
2298
2336
|
}
|
|
2299
2337
|
}
|
|
2300
|
-
}).observe(viewerEl, {
|
|
2338
|
+
}).observe(viewerEl, { subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
|
|
2301
2339
|
}
|
|
2302
2340
|
|
|
2303
2341
|
// Auto-load PDF if config is present (injected by NodeBB plugin)
|
|
@@ -2399,7 +2437,7 @@
|
|
|
2399
2437
|
// Premium Gate: Client-side page restriction for non-premium users
|
|
2400
2438
|
if (config.isPremium === false && pdfDoc && pdfDoc.numPages > 1) {
|
|
2401
2439
|
premiumInfo = Object.freeze({ isPremium: false, totalPages: pdfDoc.numPages });
|
|
2402
|
-
|
|
2440
|
+
applyPageLocks();
|
|
2403
2441
|
startPeriodicCheck();
|
|
2404
2442
|
setupAntiTampering();
|
|
2405
2443
|
} else {
|
|
@@ -2477,9 +2515,11 @@
|
|
|
2477
2515
|
}).promise;
|
|
2478
2516
|
|
|
2479
2517
|
const thumb = document.createElement('div');
|
|
2480
|
-
|
|
2518
|
+
const isLocked = premiumInfo && !premiumInfo.isPremium && i > 1;
|
|
2519
|
+
thumb.className = 'thumbnail' + (i === 1 ? ' active' : '') + (isLocked ? ' locked' : '');
|
|
2481
2520
|
thumb.dataset.page = i;
|
|
2482
|
-
thumb.innerHTML = `<div class="thumbnailNum">${i}</div
|
|
2521
|
+
thumb.innerHTML = `<div class="thumbnailNum">${i}</div>` +
|
|
2522
|
+
(isLocked ? '<div class="thumbnail-lock">\u{1F512}</div>' : '');
|
|
2483
2523
|
thumb.insertBefore(canvas, thumb.firstChild);
|
|
2484
2524
|
|
|
2485
2525
|
thumb.onclick = () => {
|
|
@@ -2525,6 +2565,12 @@
|
|
|
2525
2565
|
eventBus.on('pagerendered', (evt) => {
|
|
2526
2566
|
injectAnnotationLayer(evt.pageNumber);
|
|
2527
2567
|
|
|
2568
|
+
// Re-inject lock overlay if this is a locked page (PDF.js re-render removes it)
|
|
2569
|
+
if (premiumInfo && !premiumInfo.isPremium && evt.pageNumber > 1) {
|
|
2570
|
+
var pageEl = document.querySelector('#viewer .page[data-page-number="' + evt.pageNumber + '"]');
|
|
2571
|
+
if (pageEl) injectPageLock(pageEl);
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2528
2574
|
// Rotation is handled natively by PDF.js via pagesRotation
|
|
2529
2575
|
});
|
|
2530
2576
|
|
|
@@ -2871,6 +2917,9 @@
|
|
|
2871
2917
|
|
|
2872
2918
|
// Annotation Layer with Persistence
|
|
2873
2919
|
async function injectAnnotationLayer(pageNum) {
|
|
2920
|
+
// Skip annotation injection on locked pages
|
|
2921
|
+
if (premiumInfo && !premiumInfo.isPremium && pageNum > 1) return;
|
|
2922
|
+
|
|
2874
2923
|
const pageView = pdfViewer.getPageView(pageNum - 1);
|
|
2875
2924
|
if (!pageView?.div) return;
|
|
2876
2925
|
|