nodebb-plugin-ezoic-infinite 1.6.16 → 1.6.18

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.16",
3
+ "version": "1.6.18",
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,103 +1511,172 @@ function buildOrdinalMap(items) {
1511
1511
 
1512
1512
 
1513
1513
 
1514
- // ===== V13 sticky-killer for Ezoic/AMP in list ads =====
1514
+ // ===== V14.1 Hook any between-wrap and tether to nearest topic LI =====
1515
1515
  (function () {
1516
- // Hypothesis: the "pile at top" is a visual effect from multiple sticky intradiv ads
1517
- // (ezads-sticky-intradiv with inline !important). We neutralize sticky continuously.
1516
+ var BETWEEN_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
1517
+ var HOST_CLASS = 'nodebb-ezoic-host';
1518
+ var TOKEN_ATTR = 'data-ezoic-anchor-token';
1519
+ var pendingSweep = false;
1520
+ var lastSweep = 0;
1521
+ var COOLDOWN = 120;
1522
+
1523
+ function token() {
1524
+ try { return String(Date.now()) + '-' + Math.random().toString(16).slice(2); } catch (e) { return String(Date.now()); }
1525
+ }
1518
1526
 
1519
- function killStickyIn(root) {
1527
+ function closestTopicLi(node) {
1528
+ // Find the topic <li> that this wrap should be associated with:
1529
+ // prefer previous sibling <li> that is NOT a host, else climb to nearest previous in the list.
1520
1530
  try {
1521
- var scope = root || document;
1522
- var nodes = scope.querySelectorAll('.nodebb-ezoic-wrap .ezads-sticky-intradiv, .nodebb-ezoic-wrap [style*="position: sticky"], .nodebb-ezoic-wrap [style*="position:sticky"]');
1523
- nodes.forEach(function(n){
1524
- try {
1525
- n.style.setProperty('position', 'static', 'important');
1526
- n.style.setProperty('top', 'auto', 'important');
1527
- n.style.setProperty('bottom', 'auto', 'important');
1528
- n.style.setProperty('left', 'auto', 'important');
1529
- n.style.setProperty('right', 'auto', 'important');
1530
- // if they force translate/transform for sticky, remove it
1531
- n.style.setProperty('transform', 'none', 'important');
1532
- n.style.setProperty('will-change', 'auto', 'important');
1533
- // also remove class to reduce re-application
1534
- if (n.classList) n.classList.remove('ezads-sticky-intradiv');
1535
- } catch (e) {}
1536
- // If inline style string contains "sticky", scrub it (some libs reparse cssText)
1531
+ if (!node) return null;
1532
+ var cur = node;
1533
+ // If wrap is inside a host li, start from host
1534
+ if (cur.closest) {
1535
+ var host = cur.closest('li.' + HOST_CLASS);
1536
+ if (host) cur = host;
1537
+ }
1538
+ // Find list container
1539
+ var ul = cur.parentElement;
1540
+ while (ul && !(ul.tagName === 'UL' || ul.tagName === 'OL')) ul = ul.parentElement;
1541
+ if (!ul) return null;
1542
+
1543
+ // Start from current element position within ul
1544
+ var prev = cur.previousElementSibling;
1545
+ while (prev) {
1546
+ if (prev.tagName === 'LI' && !(prev.classList && prev.classList.contains(HOST_CLASS))) return prev;
1547
+ prev = prev.previousElementSibling;
1548
+ }
1549
+ } catch (e) {}
1550
+ return null;
1551
+ }
1552
+
1553
+ function ensureHostForWrap(wrap) {
1554
+ try {
1555
+ if (!wrap || wrap.nodeType !== 1) return;
1556
+ if (!(wrap.matches && wrap.matches(BETWEEN_SEL))) return;
1557
+
1558
+ // Already in a host?
1559
+ var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
1560
+ var ul = wrap.parentElement;
1561
+ while (ul && !(ul.tagName === 'UL' || ul.tagName === 'OL')) ul = ul.parentElement;
1562
+ if (!ul) return;
1563
+
1564
+ // Ensure wrap is inside UL only via LI host (never direct ul>div)
1565
+ if (!host) {
1566
+ host = document.createElement('li');
1567
+ host.className = HOST_CLASS;
1568
+ host.setAttribute('role', 'listitem');
1569
+ host.style.listStyle = 'none';
1570
+ host.style.width = '100%';
1571
+ // Insert host at wrap position
1572
+ ul.insertBefore(host, wrap);
1573
+ host.appendChild(wrap);
1574
+ } else if (host.parentElement !== ul) {
1575
+ // move host into correct list container if needed
1576
+ ul.appendChild(host);
1577
+ }
1578
+
1579
+ // Tether to nearest topic li and tag both with same token
1580
+ var anchorLi = closestTopicLi(host) || closestTopicLi(wrap);
1581
+ if (anchorLi) {
1582
+ var t = anchorLi.getAttribute(TOKEN_ATTR);
1583
+ if (!t) {
1584
+ t = token();
1585
+ anchorLi.setAttribute(TOKEN_ATTR, t);
1586
+ }
1587
+ host.setAttribute(TOKEN_ATTR, t);
1588
+ // keep host just after anchor
1589
+ if (host.previousElementSibling !== anchorLi) {
1590
+ anchorLi.insertAdjacentElement('afterend', host);
1591
+ }
1592
+ } else {
1593
+ // No anchor -> it's dangerous; mark host so we can drop it later
1594
+ host.setAttribute('data-ezoic-orphan', '1');
1595
+ }
1596
+ } catch (e) {}
1597
+ }
1598
+
1599
+ function sweepAll() {
1600
+ var now = Date.now();
1601
+ if (now - lastSweep < COOLDOWN) return;
1602
+ lastSweep = now;
1603
+
1604
+ try {
1605
+ // 1) repair direct ul>div wraps
1606
+ var bad = document.querySelectorAll('ul > ' + BETWEEN_SEL + ', ol > ' + BETWEEN_SEL);
1607
+ bad.forEach(ensureHostForWrap);
1608
+
1609
+ // 2) ensure any between wrap anywhere is hosted+tethered
1610
+ var wraps = document.querySelectorAll(BETWEEN_SEL);
1611
+ wraps.forEach(ensureHostForWrap);
1612
+
1613
+ // 3) reconcile/drop hosts that lost their anchor (virtualized)
1614
+ var hosts = document.querySelectorAll('li.' + HOST_CLASS + '[' + TOKEN_ATTR + ']');
1615
+ hosts.forEach(function(host){
1537
1616
  try {
1538
- var st = n.getAttribute && n.getAttribute('style');
1539
- if (st && (st.indexOf('sticky') !== -1)) {
1540
- var cleaned = st.replace(/position\s*:\s*sticky\s*!important\s*;?/gi, 'position: static !important;')
1541
- .replace(/position\s*:\s*sticky\s*;?/gi, 'position: static;')
1542
- .replace(/top\s*:\s*0px\s*!important\s*;?/gi, 'top: auto !important;')
1543
- .replace(/top\s*:\s*0px\s*;?/gi, 'top: auto;');
1544
- n.setAttribute('style', cleaned);
1617
+ var ul = host.parentElement;
1618
+ if (!ul) { host.remove(); return; }
1619
+ var t = host.getAttribute(TOKEN_ATTR);
1620
+ if (!t) { host.remove(); return; }
1621
+ var anchor = ul.querySelector('li[' + TOKEN_ATTR + '="' + t + '"]');
1622
+ if (!anchor) {
1623
+ host.remove();
1624
+ return;
1625
+ }
1626
+ if (host.previousElementSibling !== anchor) {
1627
+ anchor.insertAdjacentElement('afterend', host);
1545
1628
  }
1546
1629
  } catch (e) {}
1547
1630
  });
1631
+
1632
+ // 4) drop explicit orphans
1633
+ var orphans = document.querySelectorAll('li.' + HOST_CLASS + '[data-ezoic-orphan="1"]');
1634
+ orphans.forEach(function(h){ try { h.remove(); } catch(e) {} });
1548
1635
  } catch (e) {}
1549
1636
  }
1550
1637
 
1551
- function install() {
1552
- // initial pass
1553
- killStickyIn(document);
1554
-
1555
- // Throttled scroll pass (in case scripts reapply on scroll)
1556
- var t = null;
1557
- window.addEventListener('scroll', function(){
1558
- if (t) return;
1559
- t = setTimeout(function(){ t = null; killStickyIn(document); }, 120);
1560
- }, { passive: true });
1638
+ function scheduleSweep() {
1639
+ if (pendingSweep) return;
1640
+ pendingSweep = true;
1641
+ requestAnimationFrame(function(){
1642
+ pendingSweep = false;
1643
+ sweepAll();
1644
+ });
1645
+ }
1561
1646
 
1562
- // MutationObserver to catch late injections / style changes
1647
+ function installObservers() {
1648
+ // Observe the document for any between-wrap insertion/moves
1563
1649
  try {
1564
1650
  if (typeof MutationObserver !== 'undefined') {
1565
1651
  var mo = new MutationObserver(function(muts){
1566
- try {
1567
- for (var i=0;i<muts.length;i++){
1568
- var m = muts[i];
1569
- if (m.type === 'attributes') {
1570
- // style/class changed
1571
- if (m.attributeName === 'style' || m.attributeName === 'class') {
1572
- killStickyIn(m.target && m.target.closest ? (m.target.closest('.nodebb-ezoic-wrap') || document) : document);
1573
- }
1574
- } else if (m.addedNodes && m.addedNodes.length) {
1575
- for (var j=0;j<m.addedNodes.length;j++){
1576
- var n = m.addedNodes[j];
1577
- if (!n || n.nodeType !== 1) continue;
1578
- if (n.matches && (n.matches('.nodebb-ezoic-wrap') || n.matches('.ezads-sticky-intradiv'))) {
1579
- killStickyIn(n);
1580
- } else if (n.querySelector) {
1581
- if (n.querySelector('.nodebb-ezoic-wrap, .ezads-sticky-intradiv')) killStickyIn(n);
1582
- }
1583
- }
1584
- }
1585
- }
1586
- } catch (e) {}
1587
- });
1588
- mo.observe(document.documentElement || document.body, {
1589
- subtree: true,
1590
- childList: true,
1591
- attributes: true,
1592
- attributeFilter: ['style','class']
1652
+ for (var i=0;i<muts.length;i++){
1653
+ var m = muts[i];
1654
+ if (m.addedNodes && m.addedNodes.length) { scheduleSweep(); break; }
1655
+ if (m.removedNodes && m.removedNodes.length) { scheduleSweep(); break; }
1656
+ }
1593
1657
  });
1658
+ mo.observe(document.documentElement || document.body, { childList: true, subtree: true });
1594
1659
  }
1595
1660
  } catch (e) {}
1661
+ }
1662
+
1663
+ function init() {
1664
+ scheduleSweep();
1665
+ window.addEventListener('scroll', scheduleSweep, { passive: true });
1666
+ window.addEventListener('resize', scheduleSweep, { passive: true });
1596
1667
 
1597
- // NodeBB events
1598
1668
  if (window.jQuery) {
1599
- try {
1600
- window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
1601
- setTimeout(function(){ killStickyIn(document); }, 0);
1602
- setTimeout(function(){ killStickyIn(document); }, 250);
1603
- setTimeout(function(){ killStickyIn(document); }, 900);
1604
- });
1605
- } catch (e) {}
1669
+ window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
1670
+ setTimeout(scheduleSweep, 0);
1671
+ setTimeout(scheduleSweep, 250);
1672
+ setTimeout(scheduleSweep, 900);
1673
+ });
1606
1674
  }
1675
+ installObservers();
1607
1676
  }
1608
1677
 
1609
- if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', install);
1610
- else install();
1678
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
1679
+ else init();
1611
1680
  })();
1612
- // ===== /V13 =====
1681
+ // ===== /V14.1 =====
1613
1682
 
package/public/style.css CHANGED
@@ -81,11 +81,7 @@
81
81
  }
82
82
 
83
83
 
84
- /* ===== V13 sticky-killer CSS fallback ===== */
85
- .nodebb-ezoic-wrap .ezads-sticky-intradiv {
86
- position: static !important;
87
- top: auto !important;
88
- transform: none !important;
89
- }
90
- /* ===== /V13 ===== */
84
+ /* ===== V14.1 host ===== */
85
+ li.nodebb-ezoic-host { list-style: none; width: 100%; }
86
+ /* ===== /V14.1 ===== */
91
87