nodebb-plugin-ezoic-infinite 1.6.46 → 1.6.47

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 +105 -80
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.46",
3
+ "version": "1.6.47",
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
@@ -1693,105 +1693,130 @@ function buildOrdinalMap(items) {
1693
1693
  // ===== /V17 =====
1694
1694
 
1695
1695
 
1696
- // ===== V17.17 TOPFIX (minimal, non-invasive) =====
1697
- // Goal: prevent "between" ad wrappers from being inserted at the very top of the topic list
1698
- // (before any real topic LI), without moving or re-wrapping existing GPT containers.
1699
- // We only:
1700
- // 1) If a between wrapper is inserted as the first child and a topic LI exists, move its host
1701
- // to after the first topic LI (one-time).
1702
- // 2) If no topic LI exists yet, mark it with data-ezoic-after="1" and leave it in place.
1703
- // This avoids the heavier "pending pool" logic that can reduce fill.
1704
- (function(){
1705
- 'use strict';
1706
1696
 
1707
- var UL_SEL = 'ul.topics, ul.topic-list, ul.category-list';
1708
- var BETWEEN_WRAP_SEL = '.nodebb-ezoic-wrap.ezoic-ad-between';
1709
- var HOST_CLASS = 'ezoic-ad-host';
1697
+ // ===== V17 empty-refresh (no move) =====
1698
+ // Goal: if a between-ad is visible but still has no ad iframe, gently poke Ezoic/GPT to render.
1699
+ (function () {
1700
+ var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
1701
+ var seen = new WeakMap();
1702
+ var io = null;
1703
+ var showAdsCooldownUntil = 0;
1710
1704
 
1711
- function getUl(){
1712
- return document.querySelector(UL_SEL);
1705
+ function now() { return Date.now ? Date.now() : +new Date(); }
1706
+
1707
+ function getSlotId(wrap) {
1708
+ try {
1709
+ var gpt = wrap.querySelector('[id^="div-gpt-ad-"]');
1710
+ return gpt ? gpt.id : null;
1711
+ } catch (e) { return null; }
1713
1712
  }
1714
1713
 
1715
- function firstTopicLi(ul){
1716
- if (!ul) return null;
1717
- // Consider any LI that is not our ad host as a topic LI
1718
- var lis = ul.querySelectorAll(':scope > li');
1719
- for (var i=0;i<lis.length;i++){
1720
- if (!lis[i].classList.contains(HOST_CLASS)) return lis[i];
1721
- }
1722
- return null;
1714
+ function hasAnyCreative(wrap) {
1715
+ try {
1716
+ // Any iframe (safeframe or normal) inside the wrap counts as rendered.
1717
+ if (wrap.querySelector('iframe')) return true;
1718
+ // Some formats use ins/amp/other nodes.
1719
+ if (wrap.querySelector('amp-ad, amp-embed, ins.adsbygoogle')) return true;
1720
+ } catch (e) {}
1721
+ return false;
1723
1722
  }
1724
1723
 
1725
- function hostFromWrap(wrap){
1726
- if (!wrap) return null;
1727
- // expected structure: li.ezoic-ad-host > (something) > span.nodebb-ezoic-wrap
1728
- var host = wrap.closest('li');
1729
- if (host && host.classList && host.classList.contains(HOST_CLASS)) return host;
1730
- return null;
1724
+ function safeShowAds() {
1725
+ var t = now();
1726
+ if (t < showAdsCooldownUntil) return;
1727
+ showAdsCooldownUntil = t + 1200;
1728
+
1729
+ try {
1730
+ if (window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
1731
+ window.ezstandalone.showAds();
1732
+ return;
1733
+ }
1734
+ } catch (e) {}
1735
+
1736
+ // Fallback: nudge common listeners.
1737
+ try { window.dispatchEvent(new Event('scroll')); } catch (e) {}
1738
+ try { window.dispatchEvent(new Event('resize')); } catch (e) {}
1731
1739
  }
1732
1740
 
1733
- function handleWrapInserted(ul, wrap){
1741
+ function scheduleCheck(wrap) {
1734
1742
  try {
1735
- if (!ul || !wrap) return;
1736
- var host = hostFromWrap(wrap);
1737
- if (!host) return;
1738
-
1739
- // Only act when the host is at the very top (before any topic LI)
1740
- if (host.parentElement !== ul) return;
1741
- if (ul.firstElementChild !== host) return;
1742
-
1743
- var anchor = firstTopicLi(ul);
1744
- if (anchor) {
1745
- // One-time move to just after first topic LI
1746
- anchor.insertAdjacentElement('afterend', host);
1747
- } else {
1748
- // No anchor yet: mark to discourage top placement; do not move later
1749
- try { wrap.setAttribute('data-ezoic-after', '1'); } catch(e) {}
1743
+ if (!wrap || wrap.nodeType !== 1) return;
1744
+ if (hasAnyCreative(wrap)) return;
1745
+
1746
+ var meta = seen.get(wrap);
1747
+ var t = now();
1748
+ if (!meta) {
1749
+ meta = { firstSeen: t, checks: 0 };
1750
+ seen.set(wrap, meta);
1750
1751
  }
1752
+
1753
+ // Give the normal pipeline time to render.
1754
+ if (meta.checks === 0) {
1755
+ setTimeout(function () { scheduleCheck(wrap); }, 700);
1756
+ meta.checks++;
1757
+ return;
1758
+ }
1759
+
1760
+ if (hasAnyCreative(wrap)) return;
1761
+
1762
+ // Still empty -> poke.
1763
+ var id = getSlotId(wrap) || 'none';
1764
+ try { console.log('[EZ EMPTY]', id); } catch (e) {}
1765
+ safeShowAds();
1766
+
1767
+ // One more re-check shortly after the poke.
1768
+ setTimeout(function () {
1769
+ if (!wrap.isConnected) return;
1770
+ if (hasAnyCreative(wrap)) return;
1771
+ // If still empty after a poke, try once more (but do not loop forever).
1772
+ safeShowAds();
1773
+ }, 900);
1751
1774
  } catch (e) {}
1752
1775
  }
1753
1776
 
1754
- function scanExisting(ul){
1777
+ function ensureIO() {
1778
+ if (io) return;
1779
+ if (!('IntersectionObserver' in window)) return;
1780
+
1781
+ io = new IntersectionObserver(function (entries) {
1782
+ for (var i = 0; i < entries.length; i++) {
1783
+ var e = entries[i];
1784
+ if (!e.isIntersecting) continue;
1785
+ scheduleCheck(e.target);
1786
+ }
1787
+ }, { root: null, rootMargin: '900px 0px 900px 0px', threshold: 0.01 });
1788
+
1789
+ // Observe existing wraps.
1755
1790
  try {
1756
- var wraps = ul ? ul.querySelectorAll(BETWEEN_WRAP_SEL) : [];
1757
- wraps.forEach(function(w){ handleWrapInserted(ul, w); });
1758
- } catch(e) {}
1759
- }
1760
-
1761
- function init(){
1762
- var ul = getUl();
1763
- if (!ul) return;
1764
-
1765
- scanExisting(ul);
1766
-
1767
- if (typeof MutationObserver === 'undefined') return;
1768
- var mo = new MutationObserver(function(muts){
1769
- for (var i=0;i<muts.length;i++){
1770
- var m = muts[i];
1771
- if (!m.addedNodes) continue;
1772
- for (var j=0;j<m.addedNodes.length;j++){
1773
- var n = m.addedNodes[j];
1774
- if (!n || n.nodeType !== 1) continue;
1775
-
1776
- // direct wrap
1777
- if (n.matches && n.matches(BETWEEN_WRAP_SEL)) {
1778
- handleWrapInserted(ul, n);
1779
- continue;
1791
+ var wraps = document.querySelectorAll(BETWEEN_WRAP_SEL);
1792
+ for (var j = 0; j < wraps.length; j++) io.observe(wraps[j]);
1793
+ } catch (e) {}
1794
+
1795
+ // Observe new wraps.
1796
+ try {
1797
+ var mo = new MutationObserver(function (muts) {
1798
+ for (var k = 0; k < muts.length; k++) {
1799
+ var m = muts[k];
1800
+ if (!m.addedNodes) continue;
1801
+ for (var n = 0; n < m.addedNodes.length; n++) {
1802
+ var node = m.addedNodes[n];
1803
+ if (!node || node.nodeType !== 1) continue;
1804
+ if (node.matches && node.matches(BETWEEN_WRAP_SEL)) {
1805
+ io.observe(node);
1806
+ } else if (node.querySelectorAll) {
1807
+ var inner = node.querySelectorAll(BETWEEN_WRAP_SEL);
1808
+ for (var q = 0; q < inner.length; q++) io.observe(inner[q]);
1809
+ }
1780
1810
  }
1781
- // wrap somewhere inside
1782
- var inner = n.querySelector ? n.querySelector(BETWEEN_WRAP_SEL) : null;
1783
- if (inner) handleWrapInserted(ul, inner);
1784
1811
  }
1785
- }
1786
- });
1787
-
1788
- mo.observe(ul, { childList: true, subtree: true });
1812
+ });
1813
+ mo.observe(document.body, { childList: true, subtree: true });
1814
+ } catch (e) {}
1789
1815
  }
1790
1816
 
1791
1817
  if (document.readyState === 'loading') {
1792
- document.addEventListener('DOMContentLoaded', init);
1818
+ document.addEventListener('DOMContentLoaded', ensureIO, { once: true });
1793
1819
  } else {
1794
- init();
1820
+ ensureIO();
1795
1821
  }
1796
1822
  })();
1797
- // ===== /V17.17 TOPFIX =====