nodebb-plugin-ezoic-infinite 1.6.30 → 1.6.31

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.30",
3
+ "version": "1.6.31",
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,205 +1514,189 @@ function buildOrdinalMap(items) {
1514
1514
 
1515
1515
 
1516
1516
 
1517
- // ===== V17.2: Up-scroll "rehome" by data-ezoic-after (keeps injection intact) =====
1518
- (function () {
1517
+ // ===== V17.3: keep injection; hide top pile on upscroll; rehome EMPTY hosts on downscroll =====
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';
1521
1521
  var HOST_CLASS = 'nodebb-ezoic-host';
1522
+ var PILED_ATTR = 'data-ezoic-piled';
1522
1523
 
1523
1524
  var lastY = window.pageYOffset || document.documentElement.scrollTop || 0;
1524
1525
  var scheduled = false;
1525
1526
  var lastRun = 0;
1526
- var COOLDOWN = 160;
1527
+ var COOLDOWN = 120;
1527
1528
 
1528
- function getY() {
1529
- return window.pageYOffset || document.documentElement.scrollTop || 0;
1530
- }
1529
+ function getY(){ return window.pageYOffset || document.documentElement.scrollTop || 0; }
1531
1530
 
1532
- function getTopicList() {
1531
+ function getTopicList(){
1533
1532
  try {
1534
1533
  var li = document.querySelector(TOPIC_LI_SEL);
1535
1534
  if (!li) return null;
1536
1535
  return li.closest ? li.closest('ul,ol') : null;
1537
- } catch (e) { return null; }
1536
+ } catch(e){ return null; }
1538
1537
  }
1539
1538
 
1540
- function ensureHostForWrap(wrap, ul) {
1541
- try {
1542
- if (!wrap || wrap.nodeType !== 1) return null;
1539
+ function ensureHostForWrap(wrap, ul){
1540
+ try{
1541
+ if (!wrap || wrap.nodeType!==1) return null;
1543
1542
  if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
1544
1543
 
1545
- var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
1544
+ var host = wrap.closest ? wrap.closest('li.'+HOST_CLASS) : null;
1546
1545
  if (host) return host;
1547
1546
 
1548
1547
  if (!ul) ul = wrap.closest ? wrap.closest('ul,ol') : null;
1549
- if (!ul || !(ul.tagName === 'UL' || ul.tagName === 'OL')) return null;
1548
+ if (!ul || !(ul.tagName==='UL' || ul.tagName==='OL')) return null;
1550
1549
 
1551
- if (wrap.parentElement === ul) {
1550
+ if (wrap.parentElement === ul){
1552
1551
  host = document.createElement('li');
1553
1552
  host.className = HOST_CLASS;
1554
- host.setAttribute('role', 'listitem');
1555
- host.style.listStyle = 'none';
1556
- host.style.width = '100%';
1553
+ host.setAttribute('role','listitem');
1554
+ host.style.listStyle='none';
1555
+ host.style.width='100%';
1556
+ // preserve after
1557
1557
  try {
1558
1558
  var after = wrap.getAttribute('data-ezoic-after');
1559
1559
  if (after) host.setAttribute('data-ezoic-after', after);
1560
- } catch (e) {}
1560
+ } catch(e){}
1561
1561
  ul.insertBefore(host, wrap);
1562
1562
  host.appendChild(wrap);
1563
- try { wrap.style.width = '100%'; } catch (e) {}
1563
+ try { wrap.style.width='100%'; } catch(e){}
1564
1564
  return host;
1565
1565
  }
1566
- } catch (e) {}
1566
+ }catch(e){}
1567
1567
  return null;
1568
1568
  }
1569
1569
 
1570
- function nthTopic(ul, n) {
1571
- try {
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; }
1575
+ }
1576
+
1577
+ function nthTopic(ul, n){
1578
+ try{
1572
1579
  var topics = ul.querySelectorAll(TOPIC_LI_SEL);
1573
1580
  if (!topics || !topics.length) return null;
1574
- if (n < 1) n = 1;
1575
- if (n > topics.length) n = topics.length;
1581
+ if (n<1) n=1;
1582
+ if (n>topics.length) n=topics.length;
1576
1583
  return topics[n-1] || null;
1577
- } catch (e) {}
1578
- return null;
1584
+ }catch(e){ return null; }
1579
1585
  }
1580
1586
 
1581
- function getAfterValue(host) {
1582
- try {
1587
+ function getAfter(host){
1588
+ try{
1583
1589
  var v = host.getAttribute('data-ezoic-after');
1584
- if (!v) {
1590
+ if (!v){
1585
1591
  var wrap = host.querySelector && host.querySelector(BETWEEN_WRAP_SEL);
1586
1592
  if (wrap) v = wrap.getAttribute('data-ezoic-after');
1587
1593
  }
1588
- var n = parseInt(v, 10);
1589
- return isNaN(n) ? null : n;
1590
- } catch (e) {}
1591
- return null;
1594
+ var n = parseInt(v,10);
1595
+ return isNaN(n)? null : n;
1596
+ }catch(e){ return null; }
1592
1597
  }
1593
1598
 
1594
- function isTopPiled(ul) {
1595
- try {
1599
+ function detectAndHideTopPile(ul){
1600
+ // Hide piled hosts near top so user doesn't see the stack.
1601
+ try{
1596
1602
  var kids = ul.children;
1597
- var limit = Math.min(kids.length, 50);
1598
- var run = 0, maxRun = 0;
1603
+ var limit = Math.min(kids.length, 60);
1604
+ var run = 0;
1599
1605
  for (var i=0;i<limit;i++){
1600
1606
  var el = kids[i];
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;
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');
1614
+ }
1615
+ } else if (isTopic){
1616
+ run=0;
1617
+ } else {
1618
+ // reset on any non-topic
1606
1619
  }
1607
-
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; }
1611
1620
  }
1612
- return maxRun >= 2;
1613
- } catch (e) {}
1614
- return false;
1621
+ }catch(e){}
1615
1622
  }
1616
1623
 
1617
- function rehomeByAfter(ul) {
1618
- try {
1619
- if (!ul) return;
1620
-
1621
- try {
1622
- ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1623
- } catch (e) {}
1624
-
1625
- if (!isTopPiled(ul)) return;
1626
-
1627
- var kids = ul.children;
1628
- var limit = Math.min(kids.length, 80);
1629
- var topHosts = [];
1630
- for (var i=0;i<limit;i++){
1631
- var el = kids[i];
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);
1635
- }
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
- }
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;
1660
1636
  }
1661
- if (!anchor) return;
1662
-
1663
- if (host.previousElementSibling !== anchor) {
1637
+ var after = getAfter(host);
1638
+ var anchor = after!=null ? nthTopic(ul, after) : null;
1639
+ if (anchor){
1664
1640
  anchor.insertAdjacentElement('afterend', host);
1665
1641
  }
1666
- } catch (e) {}
1642
+ host.removeAttribute(PILED_ATTR);
1643
+ }catch(e){}
1667
1644
  });
1668
- } catch (e) {}
1645
+ }catch(e){}
1669
1646
  }
1670
1647
 
1671
- function schedule() {
1648
+ function schedule(fn){
1672
1649
  var now = Date.now();
1673
- if (now - lastRun < COOLDOWN) return;
1650
+ if (now-lastRun < COOLDOWN) return;
1674
1651
  if (scheduled) return;
1675
1652
  scheduled = true;
1676
1653
  requestAnimationFrame(function(){
1677
- scheduled = false;
1654
+ scheduled=false;
1678
1655
  lastRun = Date.now();
1679
- var ul = getTopicList();
1680
- if (!ul) return;
1681
- rehomeByAfter(ul);
1656
+ fn();
1682
1657
  });
1683
1658
  }
1684
1659
 
1685
- function onScroll() {
1660
+ function onScroll(){
1686
1661
  var y = getY();
1687
1662
  var dy = y - lastY;
1688
1663
  lastY = y;
1689
- if (dy < -8 && y < 900) schedule();
1664
+ var ul = getTopicList();
1665
+ if (!ul) return;
1666
+
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); });
1676
+ }
1690
1677
  }
1691
1678
 
1692
- function init() {
1693
- try {
1694
- var ul = getTopicList();
1695
- if (ul) ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1696
- } catch (e) {}
1697
-
1679
+ function init(){
1698
1680
  window.addEventListener('scroll', onScroll, { passive:true });
1699
1681
 
1700
- if (window.jQuery) {
1701
- try {
1682
+ if (window.jQuery){
1683
+ try{
1702
1684
  window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
1685
+ // wrap invalids only
1703
1686
  setTimeout(function(){
1704
- try {
1687
+ try{
1705
1688
  var ul = getTopicList();
1706
- if (ul) ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1707
- } catch(e){}
1689
+ if (!ul) return;
1690
+ ul.querySelectorAll(':scope > '+BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
1691
+ }catch(e){}
1708
1692
  }, 50);
1709
1693
  });
1710
- } catch (e) {}
1694
+ }catch(e){}
1711
1695
  }
1712
1696
  }
1713
1697
 
1714
1698
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
1715
1699
  else init();
1716
1700
  })();
1717
- // ===== /V17.2 =====
1701
+ // ===== /V17.3 =====
1718
1702
 
package/public/style.css CHANGED
@@ -88,8 +88,10 @@ li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; displa
88
88
 
89
89
 
90
90
 
91
- /* ===== V17.2 host styling ===== */
91
+ /* ===== V17.3 piled hide ===== */
92
+ li.nodebb-ezoic-host[data-ezoic-piled="1"] { display: none !important; }
93
+ /* keep host stable */
92
94
  li.nodebb-ezoic-host { list-style:none; width:100%; display:block; }
93
95
  li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width:100%; display:block; }
94
- /* ===== /V17.2 ===== */
96
+ /* ===== /V17.3 ===== */
95
97