nodebb-plugin-ezoic-infinite 1.5.30 → 1.5.31
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/public/client.js +95 -14
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -10,8 +10,24 @@
|
|
|
10
10
|
// Insert at most N ads per run to keep the UI smooth on infinite scroll
|
|
11
11
|
const MAX_INSERTS_PER_RUN = 3;
|
|
12
12
|
|
|
13
|
-
// Preload before viewport (
|
|
14
|
-
const
|
|
13
|
+
// Preload before viewport (earlier load for smoother scroll)
|
|
14
|
+
const PRELOAD_MARGIN_DESKTOP = '1600px 0px 1600px 0px';
|
|
15
|
+
const PRELOAD_MARGIN_MOBILE = '900px 0px 900px 0px';
|
|
16
|
+
|
|
17
|
+
const MAX_INFLIGHT_DESKTOP = 3;
|
|
18
|
+
const MAX_INFLIGHT_MOBILE = 2;
|
|
19
|
+
|
|
20
|
+
function isMobile() {
|
|
21
|
+
try { return window && window.innerWidth && window.innerWidth < 768; } catch (e) { return false; }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getPreloadRootMargin() {
|
|
25
|
+
return isMobile() ? PRELOAD_MARGIN_MOBILE : PRELOAD_MARGIN_DESKTOP;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getMaxInflight() {
|
|
29
|
+
return isMobile() ? MAX_INFLIGHT_MOBILE : MAX_INFLIGHT_DESKTOP;
|
|
30
|
+
}
|
|
15
31
|
|
|
16
32
|
const SELECTORS = {
|
|
17
33
|
topicItem: 'li[component="category/topic"]',
|
|
@@ -50,22 +66,64 @@
|
|
|
50
66
|
io: null,
|
|
51
67
|
runQueued: false,
|
|
52
68
|
|
|
53
|
-
//
|
|
69
|
+
// preloading budget
|
|
70
|
+
inflight: 0,
|
|
71
|
+
pending: [],
|
|
72
|
+
pendingSet: new Set(),
|
|
73
|
+
|
|
74
|
+
// hero)
|
|
54
75
|
heroDoneForPage: false,
|
|
55
76
|
};
|
|
56
77
|
|
|
57
78
|
const insertingIds = new Set();
|
|
58
79
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
function enqueueShow(id) {
|
|
81
|
+
if (!id || isBlocked()) return;
|
|
82
|
+
const max = getMaxInflight();
|
|
83
|
+
if (state.inflight >= max) {
|
|
84
|
+
if (!state.pendingSet.has(id)) {
|
|
85
|
+
state.pending.push(id);
|
|
86
|
+
state.pendingSet.add(id);
|
|
65
87
|
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
startShow(id);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function drainQueue() {
|
|
94
|
+
if (isBlocked()) return;
|
|
95
|
+
const max = getMaxInflight();
|
|
96
|
+
while (state.inflight < max && state.pending.length) {
|
|
97
|
+
const id = state.pending.shift();
|
|
98
|
+
state.pendingSet.delete(id);
|
|
99
|
+
startShow(id);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function markEmptyWrapper(id) {
|
|
104
|
+
try {
|
|
105
|
+
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
106
|
+
if (!ph || !ph.isConnected) return;
|
|
107
|
+
const wrap = ph.closest ? ph.closest(`.${WRAP_CLASS}`) : null;
|
|
108
|
+
if (!wrap) return;
|
|
109
|
+
// If still empty after a delay, collapse it.
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
try {
|
|
112
|
+
const ph2 = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
113
|
+
if (!ph2 || !ph2.isConnected) return;
|
|
114
|
+
const w2 = ph2.closest ? ph2.closest(`.${WRAP_CLASS}`) : null;
|
|
115
|
+
if (!w2) return;
|
|
116
|
+
// consider empty if only whitespace and no iframes/ins/img
|
|
117
|
+
const hasAd = !!(ph2.querySelector && ph2.querySelector('iframe, ins, img, .ez-ad, .ezoic-ad'));
|
|
118
|
+
if (!hasAd) w2.classList.add('is-empty');
|
|
119
|
+
} catch (e) {}
|
|
120
|
+
}, 3500);
|
|
66
121
|
} catch (e) {}
|
|
67
122
|
}
|
|
68
123
|
|
|
124
|
+
// Production build: debug disabled
|
|
125
|
+
function dbg() {}
|
|
126
|
+
|
|
69
127
|
// ---------- small utils ----------
|
|
70
128
|
|
|
71
129
|
function normalizeBool(v) {
|
|
@@ -408,13 +466,28 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
408
466
|
}
|
|
409
467
|
}
|
|
410
468
|
|
|
411
|
-
function
|
|
469
|
+
function enqueueShow(id) {
|
|
412
470
|
if (!id || isBlocked()) return;
|
|
413
471
|
|
|
414
472
|
const now = Date.now();
|
|
415
473
|
const last = state.lastShowById.get(id) || 0;
|
|
416
474
|
if (now - last < 1500) return; // basic throttle
|
|
417
475
|
|
|
476
|
+
// Defer one frame
|
|
477
|
+
|
|
478
|
+
// Budget concurrent loads
|
|
479
|
+
state.inflight++;
|
|
480
|
+
let released = false;
|
|
481
|
+
const release = () => {
|
|
482
|
+
if (released) return;
|
|
483
|
+
released = true;
|
|
484
|
+
state.inflight = Math.max(0, state.inflight - 1);
|
|
485
|
+
drainQueue();
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
// Safety release in case Ezoic never fills
|
|
489
|
+
const hardTimer = setTimeout(release, 6500);
|
|
490
|
+
|
|
418
491
|
// Defer one frame so the placeholder is definitely in DOM after insertion/recycle
|
|
419
492
|
requestAnimationFrame(() => {
|
|
420
493
|
if (isBlocked()) return;
|
|
@@ -440,6 +513,9 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
440
513
|
} catch (e) {}
|
|
441
514
|
try { ez.showAds(id); } catch (e) {}
|
|
442
515
|
try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
|
|
516
|
+
try { markEmptyWrapper(id); } catch (e) {}
|
|
517
|
+
// allow a short time for DOM fill; then release budget
|
|
518
|
+
setTimeout(() => { clearTimeout(hardTimer); release(); }, 700);
|
|
443
519
|
};
|
|
444
520
|
|
|
445
521
|
// Fast path
|
|
@@ -466,10 +542,12 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
466
542
|
} catch (e) {}
|
|
467
543
|
try { ez2.showAds(id); } catch (e) {}
|
|
468
544
|
try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
|
|
545
|
+
try { markEmptyWrapper(id); } catch (e) {}
|
|
546
|
+
setTimeout(() => { clearTimeout(hardTimer); release(); }, 700);
|
|
469
547
|
} catch (e) {}
|
|
470
548
|
});
|
|
471
549
|
}
|
|
472
|
-
} catch (e) {}
|
|
550
|
+
} catch (e) { try { clearTimeout(hardTimer); release(); } catch (e2) {} }
|
|
473
551
|
});
|
|
474
552
|
}
|
|
475
553
|
|
|
@@ -487,9 +565,9 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
487
565
|
|
|
488
566
|
const idAttr = el && el.getAttribute && el.getAttribute('data-ezoic-id');
|
|
489
567
|
const id = parseInt(idAttr, 10);
|
|
490
|
-
if (Number.isFinite(id) && id > 0)
|
|
568
|
+
if (Number.isFinite(id) && id > 0) enqueueShow(id);
|
|
491
569
|
}
|
|
492
|
-
}, { root: null, rootMargin:
|
|
570
|
+
}, { root: null, rootMargin: getPreloadRootMargin(), threshold: 0 });
|
|
493
571
|
} catch (e) {
|
|
494
572
|
state.io = null;
|
|
495
573
|
}
|
|
@@ -505,7 +583,7 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
505
583
|
// If already above fold, fire immediately
|
|
506
584
|
try {
|
|
507
585
|
const r = ph.getBoundingClientRect();
|
|
508
|
-
if (r.top < window.innerHeight * 1.5 && r.bottom > -200)
|
|
586
|
+
if (r.top < window.innerHeight * 1.5 && r.bottom > -200) enqueueShow(id);
|
|
509
587
|
} catch (e) {}
|
|
510
588
|
}
|
|
511
589
|
|
|
@@ -708,6 +786,9 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
708
786
|
state.curPosts = 0;
|
|
709
787
|
state.curCategories = 0;
|
|
710
788
|
state.lastShowById.clear();
|
|
789
|
+
state.inflight = 0;
|
|
790
|
+
state.pending = [];
|
|
791
|
+
try { state.pendingSet && state.pendingSet.clear(); } catch (e) {}
|
|
711
792
|
try { state.usedOnce && state.usedOnce.clear(); } catch (e) {}
|
|
712
793
|
state.heroDoneForPage = false;
|
|
713
794
|
|