nodebb-plugin-ezoic-infinite 1.6.38 → 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 +136 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.38",
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
@@ -501,20 +501,43 @@ function globalGapFixInit() {
501
501
  // Best-effort refresh for Ezoic standalone when we defer placement.
502
502
  // Some stacks won't fill ads if a placeholder is rendered while collapsed/0px.
503
503
  let _refreshTimer = null;
504
+
505
+ function collectEzoicIds(root) {
506
+ const ids = new Set();
507
+ if (!root || !root.querySelectorAll) return [];
508
+ // Common Ezoic placeholder ids
509
+ root.querySelectorAll('[id^="ezoic-pub-ad-placeholder-"]').forEach(el => ids.add(el.id));
510
+ // Some integrations keep ids on wrappers
511
+ root.querySelectorAll('[data-ezoic-id]').forEach(el => {
512
+ if (el.id) ids.add(el.id);
513
+ });
514
+ return Array.from(ids);
515
+ }
516
+
504
517
  function refreshAds(ids) {
505
- if (!ids || !ids.length) return;
506
518
  clearTimeout(_refreshTimer);
507
519
  _refreshTimer = setTimeout(() => {
508
520
  try {
509
521
  if (window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
510
- window.ezstandalone.showAds(ids);
522
+ // Prefer a full rescan; fall back to ids if supported.
523
+ try {
524
+ window.ezstandalone.showAds();
525
+ } catch (e) {
526
+ if (ids && ids.length) window.ezstandalone.showAds(ids);
527
+ }
528
+ if (window.__EZ_DEBUG) console.debug('[ezoic-infinite] refreshAds (ezstandalone)', { ids });
511
529
  return;
512
530
  }
513
531
  if (window.ez && typeof window.ez.showAds === 'function') {
514
- window.ez.showAds(ids);
532
+ try {
533
+ window.ez.showAds();
534
+ } catch (e) {
535
+ if (ids && ids.length) window.ez.showAds(ids);
536
+ }
537
+ if (window.__EZ_DEBUG) console.debug('[ezoic-infinite] refreshAds (ez)', { ids });
515
538
  }
516
539
  } catch (e) {}
517
- }, 50);
540
+ }, 75);
518
541
  }
519
542
 
520
543
  function primePlaceholderPool(allIds) {
@@ -1699,12 +1722,116 @@ function buildOrdinalMap(items) {
1699
1722
  });
1700
1723
  }
1701
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
+
1702
1816
  function initObserver() {
1703
1817
  var ulEl = ensureUL();
1704
1818
  if (!ulEl || mo) return;
1705
1819
 
1706
- mo = new MutationObserver(function () {
1820
+ mo = new MutationObserver(function (mutations) {
1707
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) {}
1708
1835
  });
1709
1836
 
1710
1837
  mo.observe(ulEl, { childList: true, subtree: true });
@@ -1713,6 +1840,8 @@ function buildOrdinalMap(items) {
1713
1840
  function init() {
1714
1841
  initObserver();
1715
1842
  scheduleReconcile();
1843
+ // initial scan for placeholders already in the DOM
1844
+ scanPlaceholders(document);
1716
1845
 
1717
1846
  if (window.jQuery) {
1718
1847
  try {
@@ -1724,6 +1853,8 @@ function buildOrdinalMap(items) {
1724
1853
  scheduleReconcile();
1725
1854
  setTimeout(scheduleReconcile, 120);
1726
1855
  setTimeout(scheduleReconcile, 500);
1856
+ // re-scan after ajaxify/infinite load
1857
+ setTimeout(function () { scanPlaceholders(document); }, 50);
1727
1858
  });
1728
1859
  } catch (e) {}
1729
1860
  }