nodebb-plugin-ezoic-infinite 1.6.32 → 1.6.34

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.32",
3
+ "version": "1.6.34",
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
@@ -1514,7 +1514,7 @@ function buildOrdinalMap(items) {
1514
1514
 
1515
1515
 
1516
1516
 
1517
- // ===== V17.4: Hide top pile (supports ul>div OR li hosts), no moving, keeps injection intact =====
1517
+ // ===== V17.3: keep injection; hide top pile on upscroll; rehome EMPTY hosts on downscroll =====
1518
1518
  (function(){
1519
1519
  var TOPIC_LI_SEL = 'li[component="category/topic"]';
1520
1520
  var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
@@ -1524,7 +1524,7 @@ function buildOrdinalMap(items) {
1524
1524
  var lastY = window.pageYOffset || document.documentElement.scrollTop || 0;
1525
1525
  var scheduled = false;
1526
1526
  var lastRun = 0;
1527
- var COOLDOWN = 90;
1527
+ var COOLDOWN = 120;
1528
1528
 
1529
1529
  function getY(){ return window.pageYOffset || document.documentElement.scrollTop || 0; }
1530
1530
 
@@ -1536,54 +1536,113 @@ function buildOrdinalMap(items) {
1536
1536
  } catch(e){ return null; }
1537
1537
  }
1538
1538
 
1539
- function isBetweenNode(el){
1540
- try {
1541
- if (!el || el.nodeType !== 1) return false;
1542
- if (el.matches && el.matches(BETWEEN_WRAP_SEL)) return true; // ul>div case
1543
- if (el.tagName === 'LI' && el.classList && el.classList.contains(HOST_CLASS)) {
1544
- return !!(el.querySelector && el.querySelector(BETWEEN_WRAP_SEL));
1539
+ function ensureHostForWrap(wrap, ul){
1540
+ try{
1541
+ if (!wrap || wrap.nodeType!==1) return null;
1542
+ if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
1543
+
1544
+ var host = wrap.closest ? wrap.closest('li.'+HOST_CLASS) : null;
1545
+ if (host) return host;
1546
+
1547
+ if (!ul) ul = wrap.closest ? wrap.closest('ul,ol') : null;
1548
+ if (!ul || !(ul.tagName==='UL' || ul.tagName==='OL')) return null;
1549
+
1550
+ if (wrap.parentElement === ul){
1551
+ host = document.createElement('li');
1552
+ host.className = HOST_CLASS;
1553
+ host.setAttribute('role','listitem');
1554
+ host.style.listStyle='none';
1555
+ host.style.width='100%';
1556
+ // preserve after
1557
+ try {
1558
+ var after = wrap.getAttribute('data-ezoic-after');
1559
+ if (after) host.setAttribute('data-ezoic-after', after);
1560
+ } catch(e){}
1561
+ ul.insertBefore(host, wrap);
1562
+ host.appendChild(wrap);
1563
+ try { wrap.style.width='100%'; } catch(e){}
1564
+ return host;
1545
1565
  }
1546
- } catch(e){}
1547
- return false;
1566
+ }catch(e){}
1567
+ return null;
1548
1568
  }
1549
1569
 
1550
- function isTopicNode(el){
1551
- try { return !!(el && el.nodeType===1 && el.matches && el.matches(TOPIC_LI_SEL)); } catch(e){ return false; }
1570
+ function hostHasAdContent(host){
1571
+ // iframes/amp ads mean it's already rendered; moving might reduce fill
1572
+ try{
1573
+ return !!(host.querySelector && host.querySelector('iframe, amp-ad, amp-iframe, .amp-ads, .ezoic-ad iframe'));
1574
+ }catch(e){ return false; }
1552
1575
  }
1553
1576
 
1554
- function clearPiled(ul){
1555
- try {
1556
- if (!ul) return;
1557
- ul.querySelectorAll('['+PILED_ATTR+'="1"]').forEach(function(n){
1558
- try { n.removeAttribute(PILED_ATTR); } catch(e){}
1559
- });
1560
- } catch(e){}
1577
+ function nthTopic(ul, n){
1578
+ try{
1579
+ var topics = ul.querySelectorAll(TOPIC_LI_SEL);
1580
+ if (!topics || !topics.length) return null;
1581
+ if (n<1) n=1;
1582
+ if (n>topics.length) n=topics.length;
1583
+ return topics[n-1] || null;
1584
+ }catch(e){ return null; }
1561
1585
  }
1562
1586
 
1563
- function markTopPile(ul){
1564
- // Mark any consecutive between nodes near top (keep the first, hide the rest until next topic).
1565
- try {
1566
- if (!ul) return;
1587
+ function getAfter(host){
1588
+ try{
1589
+ var v = host.getAttribute('data-ezoic-after');
1590
+ if (!v){
1591
+ var wrap = host.querySelector && host.querySelector(BETWEEN_WRAP_SEL);
1592
+ if (wrap) v = wrap.getAttribute('data-ezoic-after');
1593
+ }
1594
+ var n = parseInt(v,10);
1595
+ return isNaN(n)? null : n;
1596
+ }catch(e){ return null; }
1597
+ }
1598
+
1599
+ function detectAndHideTopPile(ul){
1600
+ // Hide piled hosts near top so user doesn't see the stack.
1601
+ try{
1567
1602
  var kids = ul.children;
1568
- var limit = Math.min(kids.length, 140);
1569
- var inRun = 0;
1603
+ var limit = Math.min(kids.length, 60);
1604
+ var run = 0;
1570
1605
  for (var i=0;i<limit;i++){
1571
1606
  var el = kids[i];
1572
- if (isBetweenNode(el)){
1573
- inRun++;
1574
- if (inRun >= 2){
1575
- try { el.setAttribute(PILED_ATTR,'1'); } catch(e){}
1576
- } else {
1577
- try { el.removeAttribute(PILED_ATTR); } catch(e){}
1607
+ var isBetweenHost = (el.tagName==='LI' && el.classList && el.classList.contains(HOST_CLASS) && el.querySelector && el.querySelector(BETWEEN_WRAP_SEL));
1608
+ var isTopic = (el.matches && el.matches(TOPIC_LI_SEL));
1609
+ if (isBetweenHost){
1610
+ run++;
1611
+ if (run>=2){
1612
+ // mark piled beyond the first
1613
+ el.setAttribute(PILED_ATTR,'1');
1578
1614
  }
1579
- } else if (isTopicNode(el)){
1580
- inRun = 0;
1615
+ } else if (isTopic){
1616
+ run=0;
1581
1617
  } else {
1582
- // other node breaks run as well
1583
- inRun = 0;
1618
+ // reset on any non-topic
1584
1619
  }
1585
1620
  }
1586
- } catch(e){}
1621
+ }catch(e){}
1622
+ }
1623
+
1624
+ function rehomePiledEmptyOnDownscroll(ul){
1625
+ // On downscroll, try to move only EMPTY (not yet rendered) piled hosts to intended anchors and unhide them.
1626
+ try{
1627
+ var piled = ul.querySelectorAll('li.'+HOST_CLASS+'['+PILED_ATTR+'="1"]');
1628
+ if (!piled.length) return;
1629
+
1630
+ piled.forEach(function(host){
1631
+ try{
1632
+ // if it already has rendered ad, just unhide; do not move
1633
+ if (hostHasAdContent(host)){
1634
+ host.removeAttribute(PILED_ATTR);
1635
+ return;
1636
+ }
1637
+ var after = getAfter(host);
1638
+ var anchor = after!=null ? nthTopic(ul, after) : null;
1639
+ if (anchor){
1640
+ anchor.insertAdjacentElement('afterend', host);
1641
+ }
1642
+ host.removeAttribute(PILED_ATTR);
1643
+ }catch(e){}
1644
+ });
1645
+ }catch(e){}
1587
1646
  }
1588
1647
 
1589
1648
  function schedule(fn){
@@ -1592,7 +1651,7 @@ function buildOrdinalMap(items) {
1592
1651
  if (scheduled) return;
1593
1652
  scheduled = true;
1594
1653
  requestAnimationFrame(function(){
1595
- scheduled = false;
1654
+ scheduled=false;
1596
1655
  lastRun = Date.now();
1597
1656
  fn();
1598
1657
  });
@@ -1602,16 +1661,18 @@ function buildOrdinalMap(items) {
1602
1661
  var y = getY();
1603
1662
  var dy = y - lastY;
1604
1663
  lastY = y;
1605
-
1606
1664
  var ul = getTopicList();
1607
1665
  if (!ul) return;
1608
1666
 
1609
- // Only manage pile visibility when user is near the top area.
1610
- if (y < 1100 && dy < -6){
1611
- schedule(function(){ markTopPile(ul); });
1612
- } else if (y > 1300 || dy > 10){
1613
- // leaving top area or scrolling down: restore visibility
1614
- schedule(function(){ clearPiled(ul); });
1667
+ // always wrap invalid ul>div (cheap)
1668
+ try { ul.querySelectorAll(':scope > '+BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); }); } catch(e){}
1669
+
1670
+ if (dy < -8 && y < 900){
1671
+ // upscroll near top: hide pile quickly (no moving)
1672
+ schedule(function(){ detectAndHideTopPile(ul); });
1673
+ } else if (dy > 8){
1674
+ // downscroll: rehome only empty piled hosts (safe) and unhide
1675
+ schedule(function(){ rehomePiledEmptyOnDownscroll(ul); });
1615
1676
  }
1616
1677
  }
1617
1678
 
@@ -1621,16 +1682,14 @@ function buildOrdinalMap(items) {
1621
1682
  if (window.jQuery){
1622
1683
  try{
1623
1684
  window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
1624
- // if we are near top, re-evaluate pile quickly; otherwise ensure no stale hiding
1685
+ // wrap invalids only
1625
1686
  setTimeout(function(){
1626
- try {
1687
+ try{
1627
1688
  var ul = getTopicList();
1628
1689
  if (!ul) return;
1629
- var y = getY();
1630
- if (y < 1100) markTopPile(ul);
1631
- else clearPiled(ul);
1632
- } catch(e){}
1633
- }, 80);
1690
+ ul.querySelectorAll(':scope > '+BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1691
+ }catch(e){}
1692
+ }, 50);
1634
1693
  });
1635
1694
  }catch(e){}
1636
1695
  }
@@ -1639,5 +1698,5 @@ function buildOrdinalMap(items) {
1639
1698
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
1640
1699
  else init();
1641
1700
  })();
1642
- // ===== /V17.4 =====
1701
+ // ===== /V17.3 =====
1643
1702
 
package/public/style.css CHANGED
@@ -88,10 +88,10 @@ li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; displa
88
88
 
89
89
 
90
90
 
91
- /* ===== V17.4 pile hide ===== */
92
- [data-ezoic-piled="1"] { display: none !important; }
91
+ /* ===== V17.3 piled hide ===== */
92
+ li.nodebb-ezoic-host[data-ezoic-piled="1"] { display: none !important; }
93
93
  /* keep host stable */
94
94
  li.nodebb-ezoic-host { list-style:none; width:100%; display:block; }
95
95
  li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width:100%; display:block; }
96
- /* ===== /V17.4 ===== */
96
+ /* ===== /V17.3 ===== */
97
97