nodebb-plugin-ezoic-infinite 1.6.29 → 1.6.30

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.29",
3
+ "version": "1.6.30",
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
@@ -1511,17 +1511,19 @@ function buildOrdinalMap(items) {
1511
1511
 
1512
1512
 
1513
1513
 
1514
- // ===== V17.1 upscroll-only top pile-up fixer (keeps injection intact) =====
1514
+
1515
+
1516
+
1517
+ // ===== V17.2: Up-scroll "rehome" by data-ezoic-after (keeps injection intact) =====
1515
1518
  (function () {
1516
1519
  var TOPIC_LI_SEL = 'li[component="category/topic"]';
1517
1520
  var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
1518
1521
  var HOST_CLASS = 'nodebb-ezoic-host';
1519
1522
 
1523
+ var lastY = window.pageYOffset || document.documentElement.scrollTop || 0;
1520
1524
  var scheduled = false;
1521
1525
  var lastRun = 0;
1522
- var COOLDOWN = 140;
1523
-
1524
- var lastY = window.pageYOffset || document.documentElement.scrollTop || 0;
1526
+ var COOLDOWN = 160;
1525
1527
 
1526
1528
  function getY() {
1527
1529
  return window.pageYOffset || document.documentElement.scrollTop || 0;
@@ -1535,12 +1537,7 @@ function buildOrdinalMap(items) {
1535
1537
  } catch (e) { return null; }
1536
1538
  }
1537
1539
 
1538
- function isHost(node) {
1539
- return !!(node && node.nodeType === 1 && node.tagName === 'LI' && node.classList && node.classList.contains(HOST_CLASS));
1540
- }
1541
-
1542
1540
  function ensureHostForWrap(wrap, ul) {
1543
- // only wrap invalid ul>div cases; do not interfere with normal injection
1544
1541
  try {
1545
1542
  if (!wrap || wrap.nodeType !== 1) return null;
1546
1543
  if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
@@ -1557,6 +1554,10 @@ function buildOrdinalMap(items) {
1557
1554
  host.setAttribute('role', 'listitem');
1558
1555
  host.style.listStyle = 'none';
1559
1556
  host.style.width = '100%';
1557
+ try {
1558
+ var after = wrap.getAttribute('data-ezoic-after');
1559
+ if (after) host.setAttribute('data-ezoic-after', after);
1560
+ } catch (e) {}
1560
1561
  ul.insertBefore(host, wrap);
1561
1562
  host.appendChild(wrap);
1562
1563
  try { wrap.style.width = '100%'; } catch (e) {}
@@ -1566,68 +1567,108 @@ function buildOrdinalMap(items) {
1566
1567
  return null;
1567
1568
  }
1568
1569
 
1569
- function previousTopicLi(node) {
1570
+ function nthTopic(ul, n) {
1570
1571
  try {
1571
- var prev = node.previousElementSibling;
1572
- while (prev) {
1573
- if (prev.matches && prev.matches(TOPIC_LI_SEL)) return prev;
1574
- prev = prev.previousElementSibling;
1572
+ var topics = ul.querySelectorAll(TOPIC_LI_SEL);
1573
+ if (!topics || !topics.length) return null;
1574
+ if (n < 1) n = 1;
1575
+ if (n > topics.length) n = topics.length;
1576
+ return topics[n-1] || null;
1577
+ } catch (e) {}
1578
+ return null;
1579
+ }
1580
+
1581
+ function getAfterValue(host) {
1582
+ try {
1583
+ var v = host.getAttribute('data-ezoic-after');
1584
+ if (!v) {
1585
+ var wrap = host.querySelector && host.querySelector(BETWEEN_WRAP_SEL);
1586
+ if (wrap) v = wrap.getAttribute('data-ezoic-after');
1575
1587
  }
1588
+ var n = parseInt(v, 10);
1589
+ return isNaN(n) ? null : n;
1576
1590
  } catch (e) {}
1577
1591
  return null;
1578
1592
  }
1579
1593
 
1580
- function detectTopPileUp(ul) {
1581
- // Detect a pile-up near the top: within first ~40 children, find 2+ between ads without topics between.
1594
+ function isTopPiled(ul) {
1582
1595
  try {
1583
1596
  var kids = ul.children;
1584
- var limit = Math.min(kids.length, 40);
1585
- var run = 0;
1586
- var maxRun = 0;
1597
+ var limit = Math.min(kids.length, 50);
1598
+ var run = 0, maxRun = 0;
1587
1599
  for (var i=0;i<limit;i++){
1588
1600
  var el = kids[i];
1589
- var isBetween = false;
1590
- if (isHost(el)) isBetween = !!(el.querySelector && el.querySelector(BETWEEN_WRAP_SEL));
1591
- else if (el.matches && el.matches(BETWEEN_WRAP_SEL)) isBetween = true;
1601
+ var isAd = false;
1602
+ if (el.tagName === 'LI' && el.classList && el.classList.contains(HOST_CLASS)) {
1603
+ isAd = !!(el.querySelector && el.querySelector(BETWEEN_WRAP_SEL));
1604
+ } else if (el.matches && el.matches(BETWEEN_WRAP_SEL)) {
1605
+ isAd = true;
1606
+ }
1592
1607
 
1593
- if (isBetween) { run++; if (run>maxRun) maxRun=run; }
1594
- else if (el.matches && el.matches(TOPIC_LI_SEL)) { run=0; }
1595
- else { run=0; }
1608
+ if (isAd) { run++; if (run>maxRun) maxRun=run; }
1609
+ else if (el.matches && el.matches(TOPIC_LI_SEL)) { run = 0; }
1610
+ else { run = 0; }
1596
1611
  }
1597
1612
  return maxRun >= 2;
1598
1613
  } catch (e) {}
1599
1614
  return false;
1600
1615
  }
1601
1616
 
1602
- function redistributeTopPile(ul) {
1617
+ function rehomeByAfter(ul) {
1603
1618
  try {
1604
1619
  if (!ul) return;
1605
1620
 
1606
- // Step 0: wrap invalid top-level between divs
1607
- try { ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); }); } catch(e){}
1621
+ try {
1622
+ ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1623
+ } catch (e) {}
1608
1624
 
1609
- if (!detectTopPileUp(ul)) return;
1625
+ if (!isTopPiled(ul)) return;
1610
1626
 
1611
- // Only move hosts that are currently in the top region (first 60 list children).
1612
1627
  var kids = ul.children;
1613
- var limit = Math.min(kids.length, 60);
1628
+ var limit = Math.min(kids.length, 80);
1629
+ var topHosts = [];
1614
1630
  for (var i=0;i<limit;i++){
1615
1631
  var el = kids[i];
1616
- if (!isHost(el)) continue;
1617
- var host = el;
1618
- var wrap = host.querySelector && host.querySelector(BETWEEN_WRAP_SEL);
1619
- if (!wrap) continue;
1620
-
1621
- var anchor = previousTopicLi(host);
1622
- if (!anchor) continue;
1623
- if (host.previousElementSibling !== anchor) {
1624
- anchor.insertAdjacentElement('afterend', host);
1632
+ if (el.tagName === 'LI' && el.classList && el.classList.contains(HOST_CLASS)) {
1633
+ var wrap = el.querySelector && el.querySelector(BETWEEN_WRAP_SEL);
1634
+ if (wrap) topHosts.push(el);
1625
1635
  }
1626
1636
  }
1637
+ if (!topHosts.length) return;
1638
+
1639
+ topHosts.sort(function(a,b){
1640
+ var aa = getAfterValue(a);
1641
+ var bb = getAfterValue(b);
1642
+ if (aa == null && bb == null) return 0;
1643
+ if (aa == null) return 1;
1644
+ if (bb == null) return -1;
1645
+ return aa - bb;
1646
+ });
1647
+
1648
+ topHosts.forEach(function(host){
1649
+ try {
1650
+ var after = getAfterValue(host);
1651
+ var anchor = null;
1652
+ if (after != null) anchor = nthTopic(ul, after);
1653
+
1654
+ if (!anchor) {
1655
+ var prev = host.previousElementSibling;
1656
+ while (prev) {
1657
+ if (prev.matches && prev.matches(TOPIC_LI_SEL)) { anchor = prev; break; }
1658
+ prev = prev.previousElementSibling;
1659
+ }
1660
+ }
1661
+ if (!anchor) return;
1662
+
1663
+ if (host.previousElementSibling !== anchor) {
1664
+ anchor.insertAdjacentElement('afterend', host);
1665
+ }
1666
+ } catch (e) {}
1667
+ });
1627
1668
  } catch (e) {}
1628
1669
  }
1629
1670
 
1630
- function schedule(reason) {
1671
+ function schedule() {
1631
1672
  var now = Date.now();
1632
1673
  if (now - lastRun < COOLDOWN) return;
1633
1674
  if (scheduled) return;
@@ -1635,11 +1676,9 @@ function buildOrdinalMap(items) {
1635
1676
  requestAnimationFrame(function(){
1636
1677
  scheduled = false;
1637
1678
  lastRun = Date.now();
1638
- try {
1639
- var ul = getTopicList();
1640
- if (!ul) return;
1641
- redistributeTopPile(ul);
1642
- } catch (e) {}
1679
+ var ul = getTopicList();
1680
+ if (!ul) return;
1681
+ rehomeByAfter(ul);
1643
1682
  });
1644
1683
  }
1645
1684
 
@@ -1647,40 +1686,33 @@ function buildOrdinalMap(items) {
1647
1686
  var y = getY();
1648
1687
  var dy = y - lastY;
1649
1688
  lastY = y;
1650
-
1651
- // Only act on upscroll (when the pile-up manifests)
1652
- if (dy < -6) schedule('upscroll');
1689
+ if (dy < -8 && y < 900) schedule();
1653
1690
  }
1654
1691
 
1655
1692
  function init() {
1656
- // Initial repair does nothing unless pile-up already present.
1657
- schedule('init');
1658
-
1659
- window.addEventListener('scroll', onScroll, { passive: true });
1660
-
1661
- // MutationObserver on list (guarded; redistribution only runs on upscroll)
1662
1693
  try {
1663
- if (typeof MutationObserver !== 'undefined') {
1664
- var ul = getTopicList();
1665
- if (ul) {
1666
- var mo = new MutationObserver(function(){ /* no immediate redistribution */ });
1667
- mo.observe(ul, { childList:true, subtree:true });
1668
- }
1669
- }
1694
+ var ul = getTopicList();
1695
+ if (ul) ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1670
1696
  } catch (e) {}
1671
1697
 
1698
+ window.addEventListener('scroll', onScroll, { passive:true });
1699
+
1672
1700
  if (window.jQuery) {
1673
1701
  try {
1674
1702
  window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
1675
- // don't touch during downscroll load; just re-evaluate when user scrolls up
1676
- setTimeout(function(){ schedule('event'); }, 0);
1703
+ setTimeout(function(){
1704
+ try {
1705
+ var ul = getTopicList();
1706
+ if (ul) ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1707
+ } catch(e){}
1708
+ }, 50);
1677
1709
  });
1678
- } catch(e){}
1710
+ } catch (e) {}
1679
1711
  }
1680
1712
  }
1681
1713
 
1682
1714
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
1683
1715
  else init();
1684
1716
  })();
1685
- // ===== /V17.1 =====
1717
+ // ===== /V17.2 =====
1686
1718
 
package/public/style.css CHANGED
@@ -81,8 +81,15 @@
81
81
  }
82
82
 
83
83
 
84
- /* ===== V17.1 host styling ===== */
84
+ /* ===== V17 host styling ===== */
85
+ li.nodebb-ezoic-host { list-style: none; width: 100%; display: block; }
86
+ li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; display: block; }
87
+ /* ===== /V17 ===== */
88
+
89
+
90
+
91
+ /* ===== V17.2 host styling ===== */
85
92
  li.nodebb-ezoic-host { list-style:none; width:100%; display:block; }
86
93
  li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width:100%; display:block; }
87
- /* ===== /V17.1 ===== */
94
+ /* ===== /V17.2 ===== */
88
95