nodebb-plugin-ezoic-infinite 1.6.39 → 1.6.40

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 +109 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.39",
3
+ "version": "1.6.40",
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
@@ -1722,12 +1722,116 @@ function buildOrdinalMap(items) {
1722
1722
  });
1723
1723
  }
1724
1724
 
1725
+ // -------- Viewport "poke" for empty Ezoic placeholders --------
1726
+ // Some Ezoic placeholders can be placed into the DOM already in-view, but
1727
+ // the ad stack only reacts on the next scroll tick. This keeps blocks from
1728
+ // staying blank until the user nudges the page.
1729
+ var io = null;
1730
+ var observedPlaceholders = typeof WeakSet !== 'undefined' ? new WeakSet() : null;
1731
+
1732
+ function isEzoicPlaceholder(el) {
1733
+ return !!(el && el.id && el.id.indexOf('ezoic-pub-ad-placeholder-') === 0);
1734
+ }
1735
+
1736
+ function iframeCount(el) {
1737
+ try {
1738
+ return (el && el.querySelectorAll) ? el.querySelectorAll('iframe').length : 0;
1739
+ } catch (e) {
1740
+ return 0;
1741
+ }
1742
+ }
1743
+
1744
+ function pokePlaceholder(ph, reason) {
1745
+ if (!ph || !ph.id) return;
1746
+ // throttle per placeholder
1747
+ var now = Date.now();
1748
+ var last = 0;
1749
+ try { last = parseInt(ph.getAttribute('data-ez-poke-at') || '0', 10) || 0; } catch (e) {}
1750
+ if (now - last < 1200) return;
1751
+ try { ph.setAttribute('data-ez-poke-at', String(now)); } catch (e) {}
1752
+
1753
+ // Only poke if still empty
1754
+ if (iframeCount(ph) > 0) return;
1755
+
1756
+ if (DEBUG) {
1757
+ try { console.log('[EZ POKE]', reason || 'io', ph.id); } catch (e) {}
1758
+ }
1759
+
1760
+ // Ask Ezoic to fill that specific placeholder
1761
+ refreshAds([ph.id]);
1762
+
1763
+ // And also emit a lightweight scroll/resize tick to trigger any lazy logic
1764
+ try { window.dispatchEvent(new Event('scroll')); } catch (e) {}
1765
+ try { window.dispatchEvent(new Event('resize')); } catch (e) {}
1766
+
1767
+ // One re-try shortly after, in case the first request was dropped.
1768
+ setTimeout(function () {
1769
+ try {
1770
+ if (iframeCount(ph) === 0) {
1771
+ refreshAds([ph.id]);
1772
+ try { window.dispatchEvent(new Event('scroll')); } catch (e2) {}
1773
+ }
1774
+ } catch (e3) {}
1775
+ }, 450);
1776
+ }
1777
+
1778
+ function ensureIO() {
1779
+ if (io) return;
1780
+ if (!('IntersectionObserver' in window)) return;
1781
+ io = new IntersectionObserver(function (entries) {
1782
+ for (var i = 0; i < entries.length; i++) {
1783
+ var e = entries[i];
1784
+ if (!e || !e.isIntersecting) continue;
1785
+ var ph = e.target;
1786
+ if (!isEzoicPlaceholder(ph)) continue;
1787
+ if (iframeCount(ph) === 0) {
1788
+ pokePlaceholder(ph, 'in-view');
1789
+ }
1790
+ }
1791
+ }, { root: null, rootMargin: '200px 0px', threshold: 0.01 });
1792
+ }
1793
+
1794
+ function observePlaceholder(ph) {
1795
+ if (!ph) return;
1796
+ ensureIO();
1797
+ if (!io) return;
1798
+ if (observedPlaceholders) {
1799
+ if (observedPlaceholders.has(ph)) return;
1800
+ observedPlaceholders.add(ph);
1801
+ } else {
1802
+ // fallback: mark directly
1803
+ if (ph.getAttribute && ph.getAttribute('data-ez-observed') === '1') return;
1804
+ try { ph.setAttribute('data-ez-observed', '1'); } catch (e) {}
1805
+ }
1806
+ try { io.observe(ph); } catch (e) {}
1807
+ }
1808
+
1809
+ function scanPlaceholders(root) {
1810
+ if (!root || !root.querySelectorAll) return;
1811
+ var list = [];
1812
+ try { list = root.querySelectorAll('div[id^="ezoic-pub-ad-placeholder-"]'); } catch (e) { list = []; }
1813
+ for (var i = 0; i < list.length; i++) observePlaceholder(list[i]);
1814
+ }
1815
+
1725
1816
  function initObserver() {
1726
1817
  var ulEl = ensureUL();
1727
1818
  if (!ulEl || mo) return;
1728
1819
 
1729
- mo = new MutationObserver(function () {
1820
+ mo = new MutationObserver(function (mutations) {
1730
1821
  scheduleReconcile();
1822
+ // Also observe any newly injected placeholders so in-view blanks get poked.
1823
+ try {
1824
+ for (var i = 0; i < (mutations || []).length; i++) {
1825
+ var m = mutations[i];
1826
+ if (!m || !m.addedNodes) continue;
1827
+ for (var j = 0; j < m.addedNodes.length; j++) {
1828
+ var n = m.addedNodes[j];
1829
+ if (!n) continue;
1830
+ if (isEzoicPlaceholder(n)) observePlaceholder(n);
1831
+ else scanPlaceholders(n);
1832
+ }
1833
+ }
1834
+ } catch (e) {}
1731
1835
  });
1732
1836
 
1733
1837
  mo.observe(ulEl, { childList: true, subtree: true });
@@ -1736,6 +1840,8 @@ function buildOrdinalMap(items) {
1736
1840
  function init() {
1737
1841
  initObserver();
1738
1842
  scheduleReconcile();
1843
+ // initial scan for placeholders already in the DOM
1844
+ scanPlaceholders(document);
1739
1845
 
1740
1846
  if (window.jQuery) {
1741
1847
  try {
@@ -1747,6 +1853,8 @@ function buildOrdinalMap(items) {
1747
1853
  scheduleReconcile();
1748
1854
  setTimeout(scheduleReconcile, 120);
1749
1855
  setTimeout(scheduleReconcile, 500);
1856
+ // re-scan after ajaxify/infinite load
1857
+ setTimeout(function () { scanPlaceholders(document); }, 50);
1750
1858
  });
1751
1859
  } catch (e) {}
1752
1860
  }