nodebb-plugin-ezoic-infinite 1.8.43 → 1.8.45
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 +52 -16
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* NodeBB Ezoic Infinite Ads — client.js v3.0.
|
|
2
|
+
* NodeBB Ezoic Infinite Ads — client.js v3.0.1
|
|
3
3
|
*
|
|
4
4
|
* Ezoic API usage per official docs:
|
|
5
5
|
* https://docs.ezoic.com/docs/ezoicads/dynamic-content/
|
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
SHOW_THROTTLE_MS: 900,
|
|
34
34
|
BURST_COOLDOWN_MS: 200,
|
|
35
35
|
BLOCK_DURATION_MS: 1_500,
|
|
36
|
-
|
|
36
|
+
// Batch Ezoic API calls a bit more aggressively on SPA transitions.
|
|
37
|
+
// This reduces early collector calls (e.g. samo.go) during ajaxify route swaps.
|
|
38
|
+
BATCH_FLUSH_MS: 500,
|
|
37
39
|
RECYCLE_DESTROY_MS: 300,
|
|
38
40
|
RECYCLE_SHOW_MS: 300,
|
|
39
41
|
};
|
|
@@ -63,7 +65,8 @@
|
|
|
63
65
|
|
|
64
66
|
const FILL_SEL = 'iframe, ins, img, video, [data-google-container-id], div[id$="__container__"]';
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
// Reduce "ad popping" from aggressive ID recycling.
|
|
69
|
+
const RECYCLE_MIN_AGE_MS = 20_000;
|
|
67
70
|
|
|
68
71
|
// ── Utility ────────────────────────────────────────────────────────────────
|
|
69
72
|
|
|
@@ -346,16 +349,27 @@
|
|
|
346
349
|
|
|
347
350
|
function scheduleEmptyCheck(id) {
|
|
348
351
|
const showTs = now();
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
352
|
+
// Check at 30s, then again at 60s — very conservative to avoid
|
|
353
|
+
// collapsing slow-loading ads
|
|
354
|
+
for (const delay of [30_000, 60_000]) {
|
|
355
|
+
setTimeout(() => {
|
|
356
|
+
try {
|
|
357
|
+
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
358
|
+
const wrap = ph?.closest(`.${WRAP_CLASS}`);
|
|
359
|
+
if (!wrap || !ph?.isConnected) return;
|
|
360
|
+
// Don't collapse if a newer show happened
|
|
361
|
+
if (parseInt(wrap.getAttribute(ATTR.SHOWN) || '0', 10) > showTs) return;
|
|
362
|
+
// If already collapsed or uncollapsed, skip
|
|
363
|
+
if (clearEmptyIfFilled(wrap)) return;
|
|
364
|
+
// Don't collapse if there's any GPT slot, even unfilled
|
|
365
|
+
// (GPT may still be processing the ad request)
|
|
366
|
+
if (ph.querySelector('[id^="div-gpt-ad"]')) return;
|
|
367
|
+
// Don't collapse if there's any child with meaningful height
|
|
368
|
+
if (ph.offsetHeight > 10) return;
|
|
369
|
+
wrap.classList.add('is-empty');
|
|
370
|
+
} catch (_) {}
|
|
371
|
+
}, delay);
|
|
372
|
+
}
|
|
359
373
|
}
|
|
360
374
|
|
|
361
375
|
// ── Recycling ──────────────────────────────────────────────────────────────
|
|
@@ -370,7 +384,8 @@
|
|
|
370
384
|
typeof ez?.showAds !== 'function') return null;
|
|
371
385
|
|
|
372
386
|
const vh = window.innerHeight || 800;
|
|
373
|
-
|
|
387
|
+
// Recycle only when the wrap is well above the viewport to avoid visible jumps.
|
|
388
|
+
const threshold = -(6 * vh);
|
|
374
389
|
const t = now();
|
|
375
390
|
let bestEmpty = null, bestEmptyY = Infinity;
|
|
376
391
|
let bestFull = null, bestFullY = Infinity;
|
|
@@ -677,7 +692,10 @@
|
|
|
677
692
|
step();
|
|
678
693
|
}
|
|
679
694
|
|
|
680
|
-
// ── Cleanup
|
|
695
|
+
// ── Cleanup on navigation ────────────────────────────────────────────────
|
|
696
|
+
//
|
|
697
|
+
// Only runs on actual page transitions (pageKey changes).
|
|
698
|
+
// Uses destroyAll() to properly clean up Ezoic state before removing DOM.
|
|
681
699
|
|
|
682
700
|
function cleanup() {
|
|
683
701
|
state.blockedUntil = now() + TIMING.BLOCK_DURATION_MS;
|
|
@@ -686,6 +704,14 @@
|
|
|
686
704
|
if (state.ezFlushTimer) { clearTimeout(state.ezFlushTimer); state.ezFlushTimer = null; }
|
|
687
705
|
state.ezBatch.clear();
|
|
688
706
|
|
|
707
|
+
// Tell Ezoic to destroy all its placeholders BEFORE we remove DOM
|
|
708
|
+
try {
|
|
709
|
+
const ez = window.ezstandalone;
|
|
710
|
+
if (typeof ez?.destroyAll === 'function') {
|
|
711
|
+
ez.destroyAll();
|
|
712
|
+
}
|
|
713
|
+
} catch (_) {}
|
|
714
|
+
|
|
689
715
|
mutate(() => {
|
|
690
716
|
for (const w of document.querySelectorAll(`.${WRAP_CLASS}`)) dropWrap(w);
|
|
691
717
|
});
|
|
@@ -882,7 +908,17 @@
|
|
|
882
908
|
const $ = window.jQuery;
|
|
883
909
|
if (!$) return;
|
|
884
910
|
$(window).off('.nbbEzoic');
|
|
885
|
-
$(window).on('action:ajaxify.start.nbbEzoic',
|
|
911
|
+
$(window).on('action:ajaxify.start.nbbEzoic', (ev, data) => {
|
|
912
|
+
// Only cleanup if navigating to a different page
|
|
913
|
+
// NodeBB fires ajaxify.start for pagination/sorting on the same page
|
|
914
|
+
const targetUrl = data?.url || data?.tpl_url || '';
|
|
915
|
+
const currentPath = location.pathname.replace(/^\//, '');
|
|
916
|
+
// If the URL is basically the same (ignoring query/hash), skip cleanup
|
|
917
|
+
if (targetUrl && targetUrl.replace(/[?#].*$/, '') === currentPath.replace(/[?#].*$/, '')) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
cleanup();
|
|
921
|
+
});
|
|
886
922
|
$(window).on('action:ajaxify.end.nbbEzoic', () => {
|
|
887
923
|
state.pageKey = pageKey();
|
|
888
924
|
state.kind = null;
|