nodebb-plugin-ezoic-infinite 1.8.61 → 1.8.62

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/client.js +58 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.8.61",
3
+ "version": "1.8.62",
4
4
  "description": "Production-ready Ezoic infinite ads integration for NodeBB 4.x",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -32,12 +32,14 @@
32
32
  BLOCK_DURATION_MS: 1_500,
33
33
  SHOW_TIMEOUT_MS: 7_000,
34
34
  SHOW_RELEASE_MS: 700,
35
+ BATCH_FLUSH_MS: 80,
35
36
  RECYCLE_DELAY_MS: 450,
36
37
  };
37
38
 
38
39
  const LIMITS = {
39
40
  MAX_INSERTS_RUN: 6,
40
41
  MAX_INFLIGHT: 4,
42
+ BATCH_SIZE: 3,
41
43
  MAX_BURST_STEPS: 8,
42
44
  BURST_WINDOW_MS: 2_000,
43
45
  };
@@ -696,9 +698,9 @@
696
698
 
697
699
  // ── Patch Ezoic showAds ────────────────────────────────────────────────────
698
700
  //
699
- // Intercepts ez.showAds() to filter disconnected placeholders and
700
- // block calls during navigation. Matches v50 behavior: individual calls,
701
- // no batching.
701
+ // Intercepts ez.showAds() to batch calls and filter disconnected placeholders.
702
+ // IMPORTANT: no-arg showAds() calls (used by Ezoic for page transitions)
703
+ // are passed through unmodified.
702
704
 
703
705
  function patchShowAds() {
704
706
  const apply = () => {
@@ -709,18 +711,46 @@
709
711
  window.__nbbEzPatched = true;
710
712
 
711
713
  const orig = ez.showAds.bind(ez);
714
+ const queue = new Set();
715
+ let flushTimer = null;
716
+
717
+ const flush = () => {
718
+ flushTimer = null;
719
+ if (isBlocked() || !queue.size) return;
720
+ const ids = Array.from(queue).sort((a, b) => a - b);
721
+ queue.clear();
722
+ const valid = ids.filter(id => {
723
+ const ph = document.getElementById(`${PH_PREFIX}${id}`);
724
+ if (!ph?.isConnected) { state.phState.delete(id); return false; }
725
+ if (isPlaceholderUsed(ph)) { state.phState.set(id, 'shown'); return false; }
726
+ return true;
727
+ });
728
+ for (let i = 0; i < valid.length; i += LIMITS.BATCH_SIZE) {
729
+ const chunk = valid.slice(i, i + LIMITS.BATCH_SIZE);
730
+ try { orig(...chunk); } catch (_) {
731
+ for (const cid of chunk) { try { orig(cid); } catch (_) {} }
732
+ }
733
+ }
734
+ };
735
+
712
736
  ez.showAds = function (...args) {
713
- // No-arg call = Ezoic internal page refresh — pass through
714
- if (args.length === 0) return orig();
737
+ // No-arg call = Ezoic page refresh — pass through unmodified
738
+ if (args.length === 0) {
739
+ return orig();
740
+ }
715
741
  if (isBlocked()) return;
716
742
  const ids = args.length === 1 && Array.isArray(args[0]) ? args[0] : args;
717
- const seen = new Set();
718
743
  for (const v of ids) {
719
744
  const id = parseInt(v, 10);
720
- if (!Number.isFinite(id) || id <= 0 || seen.has(id)) continue;
721
- if (!document.getElementById(`${PH_PREFIX}${id}`)?.isConnected) continue;
722
- seen.add(id);
723
- try { orig(id); } catch (_) {}
745
+ if (!Number.isFinite(id) || id <= 0) continue;
746
+ const ph = document.getElementById(`${PH_PREFIX}${id}`);
747
+ if (!ph?.isConnected) continue;
748
+ if (isPlaceholderUsed(ph)) { state.phState.set(id, 'shown'); continue; }
749
+ state.phState.set(id, 'show-queued');
750
+ queue.add(id);
751
+ }
752
+ if (queue.size && !flushTimer) {
753
+ flushTimer = setTimeout(flush, TIMING.BATCH_FLUSH_MS);
724
754
  }
725
755
  };
726
756
  } catch (_) {}
@@ -814,6 +844,15 @@
814
844
  function cleanup() {
815
845
  state.blockedUntil = now() + TIMING.BLOCK_DURATION_MS;
816
846
 
847
+ // Tell Ezoic to destroy all placeholders BEFORE we remove DOM elements.
848
+ // This prevents GPT slotDestroyed events and Ezoic 400 errors.
849
+ try {
850
+ const ez = window.ezstandalone;
851
+ if (typeof ez?.destroyAll === 'function') {
852
+ ez.destroyAll();
853
+ }
854
+ } catch (_) {}
855
+
817
856
  mutate(() => {
818
857
  for (const w of document.querySelectorAll(`.${WRAP_CLASS}`)) dropWrap(w);
819
858
  });
@@ -1081,8 +1120,15 @@
1081
1120
 
1082
1121
  $(window).off('.nbbEzoic');
1083
1122
 
1084
- // Cleanup on every navigation, same as v50
1085
- $(window).on('action:ajaxify.start.nbbEzoic', cleanup);
1123
+ // Only cleanup on actual page change, not same-page pagination
1124
+ $(window).on('action:ajaxify.start.nbbEzoic', (ev, data) => {
1125
+ const targetUrl = data?.url || data?.tpl_url || '';
1126
+ const currentPath = location.pathname.replace(/^\//, '');
1127
+ if (targetUrl && targetUrl.replace(/[?#].*$/, '') === currentPath.replace(/[?#].*$/, '')) {
1128
+ return; // Same page — skip cleanup
1129
+ }
1130
+ cleanup();
1131
+ });
1086
1132
 
1087
1133
  $(window).on('action:ajaxify.end.nbbEzoic', () => {
1088
1134
  state.pageKey = pageKey();