nodebb-plugin-ezoic-infinite 1.5.37 → 1.5.38

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 +80 -48
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.5.37",
3
+ "version": "1.5.38",
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
@@ -8,24 +8,24 @@
8
8
  const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
9
9
 
10
10
  // Insert at most N ads per run to keep the UI smooth on infinite scroll
11
- const MAX_INSERTS_PER_RUN = 3;
11
+ const MAX_INSERTS_PER_RUN = 6;
12
12
 
13
13
  // Preload before viewport (earlier load for smoother scroll)
14
14
  // Preload far enough ahead that fast scroll doesn't outrun ad loading.
15
15
  // Slightly more aggressive margins to reduce “I scrolled past it before it loaded”.
16
- const PRELOAD_MARGIN_DESKTOP = '4200px 0px 4200px 0px';
17
- const PRELOAD_MARGIN_MOBILE = '2400px 0px 2400px 0px';
16
+ const PRELOAD_MARGIN_DESKTOP = '6000px 0px 6000px 0px';
17
+ const PRELOAD_MARGIN_MOBILE = '3500px 0px 3500px 0px';
18
18
 
19
19
  // When the user scrolls very fast, temporarily preload more aggressively.
20
20
  // This helps ensure ads are already in-flight before the user reaches them.
21
- const PRELOAD_MARGIN_DESKTOP_BOOST = '6500px 0px 6500px 0px';
22
- const PRELOAD_MARGIN_MOBILE_BOOST = '4200px 0px 4200px 0px';
21
+ const PRELOAD_MARGIN_DESKTOP_BOOST = '8500px 0px 8500px 0px';
22
+ const PRELOAD_MARGIN_MOBILE_BOOST = '5500px 0px 5500px 0px';
23
23
  const BOOST_DURATION_MS = 2500;
24
24
  const BOOST_SPEED_PX_PER_MS = 2.2; // ~2200px/s
25
25
 
26
26
  // Allow a bit more parallelism; the perf profile can still dial this down on low-end devices.
27
- const MAX_INFLIGHT_DESKTOP = 5;
28
- const MAX_INFLIGHT_MOBILE = 4;
27
+ const MAX_INFLIGHT_DESKTOP = 8;
28
+ const MAX_INFLIGHT_MOBILE = 6;
29
29
 
30
30
 
31
31
  // Adaptive performance profile (device/network aware)
@@ -575,50 +575,53 @@ function startShow(id) {
575
575
  drainQueue();
576
576
  };
577
577
 
578
+ // Safety release even if the ad pipeline stalls
578
579
  const hardTimer = setTimeout(release, 6500);
579
580
 
580
- requestAnimationFrame(() => {
581
- try {
582
- if (isBlocked()) return;
581
+ try {
582
+ if (isBlocked()) return;
583
583
 
584
- const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
585
- if (!ph || !ph.isConnected) return;
584
+ const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
585
+ if (!ph || !ph.isConnected) return;
586
586
 
587
- const now2 = Date.now();
588
- const last2 = state.lastShowById.get(id) || 0;
589
- if (now2 - last2 < 900) return;
590
- state.lastShowById.set(id, now2);
587
+ // Guard against rapid duplicate calls for the same id
588
+ const now2 = Date.now();
589
+ const last2 = state.lastShowById.get(id) || 0;
590
+ if (now2 - last2 < 250) return;
591
+ state.lastShowById.set(id, now2);
591
592
 
592
- window.ezstandalone = window.ezstandalone || {};
593
- const ez = window.ezstandalone;
593
+ window.ezstandalone = window.ezstandalone || {};
594
+ const ez = window.ezstandalone;
594
595
 
595
- const doShow = () => {
596
- try {
597
- if (state.usedOnce && state.usedOnce.has(id)) {
598
- safeDestroyById(id);
599
- }
600
- } catch (e) {}
596
+ const doShow = () => {
597
+ try {
598
+ if (state.usedOnce && state.usedOnce.has(id)) {
599
+ safeDestroyById(id);
600
+ }
601
+ } catch (e) {}
601
602
 
602
- try { ez.showAds(id); } catch (e) {}
603
- try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
603
+ try { ez.showAds(id); } catch (e) {}
604
+ try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
604
605
 
605
- // Tighten any oversized reserved height once the creative is in the DOM.
606
- try { scheduleTighten(id); } catch (e) {}
606
+ // Tighten any oversized reserved height once the creative is in the DOM.
607
+ try { scheduleTighten(id); } catch (e) {}
607
608
 
608
- setTimeout(() => { clearTimeout(hardTimer); release(); }, 650);
609
- };
609
+ // Release budget quickly; the creative can keep loading independently.
610
+ setTimeout(() => { clearTimeout(hardTimer); release(); }, 300);
611
+ };
610
612
 
611
- if (Array.isArray(ez.cmd)) {
612
- try { ez.cmd.push(doShow); } catch (e) { doShow(); }
613
- } else {
614
- doShow();
615
- }
616
- } finally {
617
- // If we returned early, hardTimer will release.
613
+ // Use cmd queue if present, but don't delay to next frame.
614
+ if (Array.isArray(ez.cmd)) {
615
+ try { ez.cmd.push(doShow); } catch (e) { doShow(); }
616
+ } else {
617
+ doShow();
618
618
  }
619
- });
619
+ } finally {
620
+ // If we returned early, hardTimer will release.
621
+ }
620
622
  }
621
623
 
624
+
622
625
  // ---------- height normalization (reduce empty space) ----------
623
626
  // Some Ezoic / GPT wrappers reserve a larger min-height (e.g., 400px) than the rendered creative (e.g., 250px),
624
627
  // leaving visible empty space. We “tighten” the reserved min-height to the actual iframe/container height after load.
@@ -676,16 +679,43 @@ function ensureHeightObserver(id) {
676
679
  tightenEzoicHeightFor(id);
677
680
  }) : null;
678
681
 
679
- if (ro) {
680
- const ad = ph.querySelector('span.ezoic-ad');
681
- const container = ad && ad.querySelector && ad.querySelector('div[id$="__container__"]');
682
- const iframe = ad && ad.querySelector && ad.querySelector('iframe');
683
- try { if (container) ro.observe(container); } catch (e) {}
684
- try { if (iframe) ro.observe(iframe); } catch (e) {}
685
- try { if (ad) ro.observe(ad); } catch (e) {}
682
+ // Also watch for Ezoic re-applying an oversized inline min-height (style attribute changes).
683
+ const mo = (typeof MutationObserver === 'function') ? new MutationObserver(() => {
684
+ tightenEzoicHeightFor(id);
685
+ }) : null;
686
+
687
+ const attach = () => {
688
+ const ad = ph.querySelector('span.ezoic-ad[data-ez-name], span.ezoic-ad');
689
+ if (!ad) return false;
690
+
691
+ if (ro) {
692
+ const container = ad.querySelector && ad.querySelector('div[id$="__container__"]');
693
+ const iframe = ad.querySelector && ad.querySelector('iframe');
694
+ try { if (container) ro.observe(container); } catch (e) {}
695
+ try { if (iframe) ro.observe(iframe); } catch (e) {}
696
+ try { ro.observe(ad); } catch (e) {}
697
+ }
698
+ if (mo) {
699
+ try { mo.observe(ad, { attributes: true, attributeFilter: ['style', 'class'] }); } catch (e) {}
700
+ }
701
+ return true;
702
+ };
703
+
704
+ // If the ad wrapper is injected slightly later, observe the placeholder briefly for children.
705
+ if (!attach() && typeof MutationObserver === 'function') {
706
+ try {
707
+ const tmp = new MutationObserver(() => {
708
+ if (attach()) {
709
+ try { tmp.disconnect(); } catch (e) {}
710
+ tightenEzoicHeightFor(id);
711
+ }
712
+ });
713
+ tmp.observe(ph, { childList: true, subtree: true });
714
+ setTimeout(() => { try { tmp.disconnect(); } catch (e) {} }, 3000);
715
+ } catch (e) {}
686
716
  }
687
717
 
688
- _heightObsByPlaceholder.set(id, ro || true);
718
+ _heightObsByPlaceholder.set(id, { ro: ro || null, mo: mo || null });
689
719
  } catch (e) {}
690
720
  }
691
721
 
@@ -777,7 +807,7 @@ function scheduleTighten(id) {
777
807
  const targets = computeTargets(items.length, interval, showFirst);
778
808
  let inserted = 0;
779
809
  const perf = getPerfProfile();
780
- const maxInserts = perf.maxInsertsPerRun + (isBoosted() ? 1 : 0);
810
+ const maxInserts = perf.maxInsertsPerRun + (isBoosted() ? 2 : 0);
781
811
 
782
812
  for (const afterPos of targets) {
783
813
  if (inserted >= maxInserts) break;
@@ -959,7 +989,9 @@ function scheduleTighten(id) {
959
989
  // disconnect any ResizeObservers used for height tightening
960
990
  try {
961
991
  for (const v of _heightObsByPlaceholder.values()) {
962
- try { v && v.disconnect && v.disconnect(); } catch (e) {}
992
+ try { if (v && v.ro && v.ro.disconnect) v.ro.disconnect(); } catch (e) {}
993
+ try { if (v && v.mo && v.mo.disconnect) v.mo.disconnect(); } catch (e) {}
994
+ try { if (v && v.disconnect) v.disconnect(); } catch (e) {} // backward compat
963
995
  }
964
996
  _heightObsByPlaceholder.clear();
965
997
  } catch (e) {}