nodebb-plugin-ezoic-infinite 1.6.45 → 1.6.46

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 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.45",
3
+ "version": "1.6.46",
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
@@ -1515,11 +1515,7 @@ function buildOrdinalMap(items) {
1515
1515
  (function () {
1516
1516
  // Goal: keep ad injection intact. Only repair when we detect "pile-up" of between wraps.
1517
1517
  var TOPIC_LI_SEL = 'li[component="category/topic"]';
1518
- // Between-ad wrapper selector.
1519
- // Note: Ezoic can inject <span class="ezoic-ad ...">; we normalize those into our wraps.
1520
- var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between, span.nodebb-ezoic-wrap.ezoic-ad-between, div.ezoic-ad-between, span.ezoic-ad-between';
1521
- var BETWEEN_WRAP_CHILD_SEL = ':scope > div.nodebb-ezoic-wrap.ezoic-ad-between, :scope > span.nodebb-ezoic-wrap.ezoic-ad-between, :scope > div.ezoic-ad-between, :scope > span.ezoic-ad-between';
1522
- var ORPHAN_EZOIC_CHILD_SEL = ':scope > span.ezoic-ad:not(.nodebb-ezoic-wrap), :scope > div.ezoic-ad:not(.nodebb-ezoic-wrap)';
1518
+ var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
1523
1519
  var HOST_CLASS = 'nodebb-ezoic-host';
1524
1520
 
1525
1521
  var scheduled = false;
@@ -1611,19 +1607,7 @@ function buildOrdinalMap(items) {
1611
1607
  if (!ul) return;
1612
1608
 
1613
1609
  // Step 1: wrap any direct child between DIVs into LI hosts (makes list stable)
1614
- // First: any explicit between wrappers
1615
- ul.querySelectorAll(BETWEEN_WRAP_CHILD_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1616
-
1617
- // Second: native injected Ezoic containers directly under the list.
1618
- // They don't have our marker classes, so we convert them.
1619
- ul.querySelectorAll(ORPHAN_EZOIC_CHILD_SEL).forEach(function(w){
1620
- try { w.classList.add('nodebb-ezoic-wrap'); } catch(e) {}
1621
- try { w.classList.add('ezoic-ad-between'); } catch(e) {}
1622
- if (!w.getAttribute('data-ezoic-after')) {
1623
- w.setAttribute('data-ezoic-after', '1');
1624
- }
1625
- ensureHostForWrap(w, ul);
1626
- });
1610
+ ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1627
1611
 
1628
1612
  // Step 2: only act if we see pile-up (avoid touching infinite scroll during normal flow)
1629
1613
  if (!detectPileUp(ul)) return;
@@ -1708,3 +1692,106 @@ function buildOrdinalMap(items) {
1708
1692
  })();
1709
1693
  // ===== /V17 =====
1710
1694
 
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
+
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';
1710
+
1711
+ function getUl(){
1712
+ return document.querySelector(UL_SEL);
1713
+ }
1714
+
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;
1723
+ }
1724
+
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;
1731
+ }
1732
+
1733
+ function handleWrapInserted(ul, wrap){
1734
+ 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) {}
1750
+ }
1751
+ } catch (e) {}
1752
+ }
1753
+
1754
+ function scanExisting(ul){
1755
+ 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;
1780
+ }
1781
+ // wrap somewhere inside
1782
+ var inner = n.querySelector ? n.querySelector(BETWEEN_WRAP_SEL) : null;
1783
+ if (inner) handleWrapInserted(ul, inner);
1784
+ }
1785
+ }
1786
+ });
1787
+
1788
+ mo.observe(ul, { childList: true, subtree: true });
1789
+ }
1790
+
1791
+ if (document.readyState === 'loading') {
1792
+ document.addEventListener('DOMContentLoaded', init);
1793
+ } else {
1794
+ init();
1795
+ }
1796
+ })();
1797
+ // ===== /V17.17 TOPFIX =====