nodebb-plugin-ezoic-infinite 1.6.28 → 1.6.29

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.28",
3
+ "version": "1.6.29",
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,16 +1511,21 @@ function buildOrdinalMap(items) {
1511
1511
 
1512
1512
 
1513
1513
 
1514
- // ===== V17 minimal pile-fix (no insert hooks) =====
1514
+ // ===== V17.1 upscroll-only top pile-up fixer (keeps injection intact) =====
1515
1515
  (function () {
1516
- // Goal: keep ad injection intact. Only repair when we detect "pile-up" of between wraps.
1517
1516
  var TOPIC_LI_SEL = 'li[component="category/topic"]';
1518
1517
  var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
1519
1518
  var HOST_CLASS = 'nodebb-ezoic-host';
1520
1519
 
1521
1520
  var scheduled = false;
1522
1521
  var lastRun = 0;
1523
- var COOLDOWN = 180;
1522
+ var COOLDOWN = 140;
1523
+
1524
+ var lastY = window.pageYOffset || document.documentElement.scrollTop || 0;
1525
+
1526
+ function getY() {
1527
+ return window.pageYOffset || document.documentElement.scrollTop || 0;
1528
+ }
1524
1529
 
1525
1530
  function getTopicList() {
1526
1531
  try {
@@ -1535,6 +1540,7 @@ function buildOrdinalMap(items) {
1535
1540
  }
1536
1541
 
1537
1542
  function ensureHostForWrap(wrap, ul) {
1543
+ // only wrap invalid ul>div cases; do not interfere with normal injection
1538
1544
  try {
1539
1545
  if (!wrap || wrap.nodeType !== 1) return null;
1540
1546
  if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
@@ -1545,7 +1551,6 @@ function buildOrdinalMap(items) {
1545
1551
  if (!ul) ul = wrap.closest ? wrap.closest('ul,ol') : null;
1546
1552
  if (!ul || !(ul.tagName === 'UL' || ul.tagName === 'OL')) return null;
1547
1553
 
1548
- // Only wrap if direct child of list (invalid / fragile)
1549
1554
  if (wrap.parentElement === ul) {
1550
1555
  host = document.createElement('li');
1551
1556
  host.className = HOST_CLASS;
@@ -1566,67 +1571,59 @@ function buildOrdinalMap(items) {
1566
1571
  var prev = node.previousElementSibling;
1567
1572
  while (prev) {
1568
1573
  if (prev.matches && prev.matches(TOPIC_LI_SEL)) return prev;
1569
- // skip other hosts/wraps
1570
1574
  prev = prev.previousElementSibling;
1571
1575
  }
1572
1576
  } catch (e) {}
1573
1577
  return null;
1574
1578
  }
1575
1579
 
1576
- function detectPileUp(ul) {
1577
- // Pile-up signature: 2+ between wraps/hosts adjacent with no topics between, often near top.
1580
+ function detectTopPileUp(ul) {
1581
+ // Detect a pile-up near the top: within first ~40 children, find 2+ between ads without topics between.
1578
1582
  try {
1579
1583
  var kids = ul.children;
1584
+ var limit = Math.min(kids.length, 40);
1580
1585
  var run = 0;
1581
1586
  var maxRun = 0;
1582
- for (var i = 0; i < kids.length; i++) {
1587
+ for (var i=0;i<limit;i++){
1583
1588
  var el = kids[i];
1584
1589
  var isBetween = false;
1585
- if (isHost(el)) {
1586
- isBetween = !!(el.querySelector && el.querySelector(BETWEEN_WRAP_SEL));
1587
- } else if (el.matches && el.matches(BETWEEN_WRAP_SEL)) {
1588
- isBetween = true;
1589
- }
1590
- if (isBetween) {
1591
- run++;
1592
- if (run > maxRun) maxRun = run;
1593
- } else if (el.matches && el.matches(TOPIC_LI_SEL)) {
1594
- run = 0;
1595
- } else {
1596
- // other nodes reset lightly
1597
- run = 0;
1598
- }
1590
+ if (isHost(el)) isBetween = !!(el.querySelector && el.querySelector(BETWEEN_WRAP_SEL));
1591
+ else if (el.matches && el.matches(BETWEEN_WRAP_SEL)) isBetween = true;
1592
+
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; }
1599
1596
  }
1600
1597
  return maxRun >= 2;
1601
1598
  } catch (e) {}
1602
1599
  return false;
1603
1600
  }
1604
1601
 
1605
- function redistribute(ul) {
1602
+ function redistributeTopPile(ul) {
1606
1603
  try {
1607
1604
  if (!ul) return;
1608
1605
 
1609
- // Step 1: wrap any direct child between DIVs into LI hosts (makes list stable)
1610
- ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
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){}
1611
1608
 
1612
- // Step 2: only act if we see pile-up (avoid touching infinite scroll during normal flow)
1613
- if (!detectPileUp(ul)) return;
1609
+ if (!detectTopPileUp(ul)) return;
1614
1610
 
1615
- // Move each host to immediately after the closest previous topic LI at its current position.
1616
- var hosts = ul.querySelectorAll(':scope > li.' + HOST_CLASS);
1617
- hosts.forEach(function(host){
1618
- try {
1619
- var wrap = host.querySelector && host.querySelector(BETWEEN_WRAP_SEL);
1620
- if (!wrap) return;
1621
-
1622
- var anchor = previousTopicLi(host);
1623
- if (!anchor) return; // if none, don't move (prevents yanking to top/bottom)
1624
-
1625
- if (host.previousElementSibling !== anchor) {
1626
- anchor.insertAdjacentElement('afterend', host);
1627
- }
1628
- } catch (e) {}
1629
- });
1611
+ // Only move hosts that are currently in the top region (first 60 list children).
1612
+ var kids = ul.children;
1613
+ var limit = Math.min(kids.length, 60);
1614
+ for (var i=0;i<limit;i++){
1615
+ 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);
1625
+ }
1626
+ }
1630
1627
  } catch (e) {}
1631
1628
  }
1632
1629
 
@@ -1635,60 +1632,55 @@ function buildOrdinalMap(items) {
1635
1632
  if (now - lastRun < COOLDOWN) return;
1636
1633
  if (scheduled) return;
1637
1634
  scheduled = true;
1638
- requestAnimationFrame(function () {
1635
+ requestAnimationFrame(function(){
1639
1636
  scheduled = false;
1640
1637
  lastRun = Date.now();
1641
1638
  try {
1642
1639
  var ul = getTopicList();
1643
1640
  if (!ul) return;
1644
- redistribute(ul);
1641
+ redistributeTopPile(ul);
1645
1642
  } catch (e) {}
1646
1643
  });
1647
1644
  }
1648
1645
 
1646
+ function onScroll() {
1647
+ var y = getY();
1648
+ var dy = y - lastY;
1649
+ lastY = y;
1650
+
1651
+ // Only act on upscroll (when the pile-up manifests)
1652
+ if (dy < -6) schedule('upscroll');
1653
+ }
1654
+
1649
1655
  function init() {
1656
+ // Initial repair does nothing unless pile-up already present.
1650
1657
  schedule('init');
1651
1658
 
1652
- // Observe only the topic list once available
1659
+ window.addEventListener('scroll', onScroll, { passive: true });
1660
+
1661
+ // MutationObserver on list (guarded; redistribution only runs on upscroll)
1653
1662
  try {
1654
1663
  if (typeof MutationObserver !== 'undefined') {
1655
- var observeList = function(ul){
1656
- if (!ul) return;
1657
- var mo = new MutationObserver(function(muts){
1658
- // schedule on any change; redistribute() itself is guarded by pile-up detection
1659
- schedule('mo');
1660
- });
1661
- mo.observe(ul, { childList: true, subtree: true });
1662
- };
1663
-
1664
1664
  var ul = getTopicList();
1665
- if (ul) observeList(ul);
1666
- else {
1667
- var mo2 = new MutationObserver(function(){
1668
- var u2 = getTopicList();
1669
- if (u2) {
1670
- try { observeList(u2); } catch(e){}
1671
- try { mo2.disconnect(); } catch(e){}
1672
- }
1673
- });
1674
- mo2.observe(document.documentElement || document.body, { childList: true, subtree: true });
1665
+ if (ul) {
1666
+ var mo = new MutationObserver(function(){ /* no immediate redistribution */ });
1667
+ mo.observe(ul, { childList:true, subtree:true });
1675
1668
  }
1676
1669
  }
1677
1670
  } catch (e) {}
1678
1671
 
1679
- // NodeBB events: run after infinite scroll batches
1680
1672
  if (window.jQuery) {
1681
1673
  try {
1682
1674
  window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
1683
- setTimeout(function(){ schedule('event'); }, 50);
1684
- setTimeout(function(){ schedule('event2'); }, 400);
1675
+ // don't touch during downscroll load; just re-evaluate when user scrolls up
1676
+ setTimeout(function(){ schedule('event'); }, 0);
1685
1677
  });
1686
- } catch (e) {}
1678
+ } catch(e){}
1687
1679
  }
1688
1680
  }
1689
1681
 
1690
1682
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
1691
1683
  else init();
1692
1684
  })();
1693
- // ===== /V17 =====
1685
+ // ===== /V17.1 =====
1694
1686
 
package/public/style.css CHANGED
@@ -81,8 +81,8 @@
81
81
  }
82
82
 
83
83
 
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 ===== */
84
+ /* ===== V17.1 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.1 ===== */
88
88