nodebb-plugin-ezoic-infinite 1.5.29 → 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 +106 -18
- package/public/style.css +2 -1
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) {
|
|
@@ -286,8 +344,15 @@ function withInternalDomChange(fn) {
|
|
|
286
344
|
let removed = 0;
|
|
287
345
|
|
|
288
346
|
wraps.forEach((wrap) => {
|
|
289
|
-
|
|
290
|
-
|
|
347
|
+
// NodeBB can insert separators/spacers; accept an anchor within a few previous siblings
|
|
348
|
+
let ok = false;
|
|
349
|
+
let prev = wrap.previousElementSibling;
|
|
350
|
+
for (let i = 0; i < 3 && prev; i++) {
|
|
351
|
+
if (itemSet.has(prev)) { ok = true; break; }
|
|
352
|
+
prev = prev.previousElementSibling;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (!ok) {
|
|
291
356
|
const id = getWrapIdFromWrap(wrap);
|
|
292
357
|
withInternalDomChange(() => {
|
|
293
358
|
try {
|
|
@@ -315,12 +380,12 @@ function withInternalDomChange(fn) {
|
|
|
315
380
|
if (hasContent) wrap.classList.remove('is-empty');
|
|
316
381
|
else wrap.classList.add('is-empty');
|
|
317
382
|
} catch (e) {}
|
|
318
|
-
},
|
|
383
|
+
}, 3500);
|
|
319
384
|
}
|
|
320
385
|
|
|
321
386
|
function buildWrap(id, kindClass, afterPos) {
|
|
322
387
|
const wrap = document.createElement('div');
|
|
323
|
-
wrap.className = `${WRAP_CLASS} ${kindClass}
|
|
388
|
+
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
324
389
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
325
390
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
326
391
|
wrap.style.width = '100%';
|
|
@@ -401,13 +466,28 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
401
466
|
}
|
|
402
467
|
}
|
|
403
468
|
|
|
404
|
-
function
|
|
469
|
+
function enqueueShow(id) {
|
|
405
470
|
if (!id || isBlocked()) return;
|
|
406
471
|
|
|
407
472
|
const now = Date.now();
|
|
408
473
|
const last = state.lastShowById.get(id) || 0;
|
|
409
474
|
if (now - last < 1500) return; // basic throttle
|
|
410
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
|
+
|
|
411
491
|
// Defer one frame so the placeholder is definitely in DOM after insertion/recycle
|
|
412
492
|
requestAnimationFrame(() => {
|
|
413
493
|
if (isBlocked()) return;
|
|
@@ -433,6 +513,9 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
433
513
|
} catch (e) {}
|
|
434
514
|
try { ez.showAds(id); } catch (e) {}
|
|
435
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);
|
|
436
519
|
};
|
|
437
520
|
|
|
438
521
|
// Fast path
|
|
@@ -459,10 +542,12 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
459
542
|
} catch (e) {}
|
|
460
543
|
try { ez2.showAds(id); } catch (e) {}
|
|
461
544
|
try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
|
|
545
|
+
try { markEmptyWrapper(id); } catch (e) {}
|
|
546
|
+
setTimeout(() => { clearTimeout(hardTimer); release(); }, 700);
|
|
462
547
|
} catch (e) {}
|
|
463
548
|
});
|
|
464
549
|
}
|
|
465
|
-
} catch (e) {}
|
|
550
|
+
} catch (e) { try { clearTimeout(hardTimer); release(); } catch (e2) {} }
|
|
466
551
|
});
|
|
467
552
|
}
|
|
468
553
|
|
|
@@ -480,9 +565,9 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
480
565
|
|
|
481
566
|
const idAttr = el && el.getAttribute && el.getAttribute('data-ezoic-id');
|
|
482
567
|
const id = parseInt(idAttr, 10);
|
|
483
|
-
if (Number.isFinite(id) && id > 0)
|
|
568
|
+
if (Number.isFinite(id) && id > 0) enqueueShow(id);
|
|
484
569
|
}
|
|
485
|
-
}, { root: null, rootMargin:
|
|
570
|
+
}, { root: null, rootMargin: getPreloadRootMargin(), threshold: 0 });
|
|
486
571
|
} catch (e) {
|
|
487
572
|
state.io = null;
|
|
488
573
|
}
|
|
@@ -498,7 +583,7 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
498
583
|
// If already above fold, fire immediately
|
|
499
584
|
try {
|
|
500
585
|
const r = ph.getBoundingClientRect();
|
|
501
|
-
if (r.top < window.innerHeight * 1.5 && r.bottom > -200)
|
|
586
|
+
if (r.top < window.innerHeight * 1.5 && r.bottom > -200) enqueueShow(id);
|
|
502
587
|
} catch (e) {}
|
|
503
588
|
}
|
|
504
589
|
|
|
@@ -701,6 +786,9 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
701
786
|
state.curPosts = 0;
|
|
702
787
|
state.curCategories = 0;
|
|
703
788
|
state.lastShowById.clear();
|
|
789
|
+
state.inflight = 0;
|
|
790
|
+
state.pending = [];
|
|
791
|
+
try { state.pendingSet && state.pendingSet.clear(); } catch (e) {}
|
|
704
792
|
try { state.usedOnce && state.usedOnce.clear(); } catch (e) {}
|
|
705
793
|
state.heroDoneForPage = false;
|
|
706
794
|
|
package/public/style.css
CHANGED
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
|
|
24
24
|
/* Collapse empty ad blocks (prevents "holes" when an ad doesn't fill or gets destroyed) */
|
|
25
25
|
.ezoic-ad.is-empty {
|
|
26
|
-
display:
|
|
26
|
+
display: block !important;
|
|
27
27
|
margin: 0 !important;
|
|
28
28
|
padding: 0 !important;
|
|
29
29
|
height: 0 !important;
|
|
30
30
|
min-height: 0 !important;
|
|
31
|
+
overflow: hidden !important;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
.ezoic-ad {
|