nodebb-plugin-ezoic-infinite 1.6.19 → 1.6.21
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 +1 -1
- package/public/client.js +110 -119
- package/public/style.css +2 -2
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1511,86 +1511,95 @@ function buildOrdinalMap(items) {
|
|
|
1511
1511
|
|
|
1512
1512
|
|
|
1513
1513
|
|
|
1514
|
-
// ===== V14.
|
|
1514
|
+
// ===== V14.1.1 Hook between-wrap (safe): host repair always, reconcile ONLY on upscroll, never append host to bottom =====
|
|
1515
1515
|
(function () {
|
|
1516
1516
|
var BETWEEN_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1517
1517
|
var HOST_CLASS = 'nodebb-ezoic-host';
|
|
1518
1518
|
var TOKEN_ATTR = 'data-ezoic-anchor-token';
|
|
1519
|
-
var TOPIC_LI_SEL = 'li[component="category/topic"]';
|
|
1520
1519
|
|
|
1521
1520
|
var lastY = window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
1521
|
+
var pending = false;
|
|
1522
|
+
var lastRun = 0;
|
|
1523
|
+
var COOLDOWN = 140;
|
|
1522
1524
|
|
|
1523
|
-
function
|
|
1524
|
-
try { return String(Date.now()) + '-' + Math.random().toString(16).slice(2); } catch (e) { return String(Date.now()); }
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
function getScrollY() {
|
|
1525
|
+
function getY() {
|
|
1528
1526
|
return window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
1529
1527
|
}
|
|
1530
1528
|
|
|
1531
|
-
function
|
|
1532
|
-
try { return
|
|
1529
|
+
function token() {
|
|
1530
|
+
try { return String(Date.now()) + '-' + Math.random().toString(16).slice(2); } catch (e) { return String(Date.now()); }
|
|
1533
1531
|
}
|
|
1534
1532
|
|
|
1535
|
-
function
|
|
1533
|
+
function getListContainer(node) {
|
|
1536
1534
|
try {
|
|
1537
1535
|
var ul = node && node.closest ? node.closest('ul,ol') : null;
|
|
1538
|
-
if (ul &&
|
|
1539
|
-
// sometimes wrap is outside: search up
|
|
1536
|
+
if (ul && (ul.tagName === 'UL' || ul.tagName === 'OL')) return ul;
|
|
1540
1537
|
var p = node && node.parentElement;
|
|
1541
1538
|
while (p) {
|
|
1542
|
-
if (
|
|
1539
|
+
if (p.tagName === 'UL' || p.tagName === 'OL') return p;
|
|
1543
1540
|
p = p.parentElement;
|
|
1544
1541
|
}
|
|
1545
1542
|
} catch (e) {}
|
|
1546
1543
|
return null;
|
|
1547
1544
|
}
|
|
1548
1545
|
|
|
1549
|
-
function
|
|
1546
|
+
function closestNonHostLi(node) {
|
|
1550
1547
|
try {
|
|
1551
|
-
var cur =
|
|
1548
|
+
var cur = node;
|
|
1552
1549
|
if (!cur) return null;
|
|
1553
|
-
// if wrap inside host li, start from host
|
|
1554
1550
|
if (cur.closest) {
|
|
1555
|
-
var
|
|
1556
|
-
if (
|
|
1551
|
+
var host = cur.closest('li.' + HOST_CLASS);
|
|
1552
|
+
if (host) cur = host;
|
|
1557
1553
|
}
|
|
1558
1554
|
var prev = cur.previousElementSibling;
|
|
1559
1555
|
while (prev) {
|
|
1560
|
-
if (prev.
|
|
1556
|
+
if (prev.tagName === 'LI' && !(prev.classList && prev.classList.contains(HOST_CLASS))) return prev;
|
|
1561
1557
|
prev = prev.previousElementSibling;
|
|
1562
1558
|
}
|
|
1563
1559
|
} catch (e) {}
|
|
1564
1560
|
return null;
|
|
1565
1561
|
}
|
|
1566
1562
|
|
|
1567
|
-
function
|
|
1563
|
+
function ensureHost(wrap) {
|
|
1564
|
+
// Always ensure UL/OL doesn't directly contain DIV wraps.
|
|
1568
1565
|
try {
|
|
1569
|
-
if (!wrap || wrap.nodeType !== 1) return;
|
|
1570
|
-
if (!(wrap.matches && wrap.matches(BETWEEN_SEL))) return;
|
|
1566
|
+
if (!wrap || wrap.nodeType !== 1) return null;
|
|
1567
|
+
if (!(wrap.matches && wrap.matches(BETWEEN_SEL))) return null;
|
|
1571
1568
|
|
|
1572
|
-
var ul =
|
|
1573
|
-
if (!ul) return;
|
|
1569
|
+
var ul = getListContainer(wrap);
|
|
1570
|
+
if (!ul) return null;
|
|
1574
1571
|
|
|
1575
|
-
// Ensure wrap is not direct child of UL/OL
|
|
1576
1572
|
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1577
|
-
if (
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1573
|
+
if (host) return host;
|
|
1574
|
+
|
|
1575
|
+
// If direct child of UL/OL, create host in-place (does not affect infinite scroll measurements much)
|
|
1576
|
+
if (wrap.parentElement === ul) {
|
|
1577
|
+
host = document.createElement('li');
|
|
1578
|
+
host.className = HOST_CLASS;
|
|
1579
|
+
host.setAttribute('role', 'listitem');
|
|
1580
|
+
host.style.listStyle = 'none';
|
|
1581
|
+
host.style.width = '100%';
|
|
1582
|
+
|
|
1583
|
+
ul.insertBefore(host, wrap);
|
|
1584
|
+
host.appendChild(wrap);
|
|
1585
|
+
try { wrap.style.width = '100%'; } catch (e) {}
|
|
1586
|
+
return host;
|
|
1587
1587
|
}
|
|
1588
|
+
} catch (e) {}
|
|
1589
|
+
return null;
|
|
1590
|
+
}
|
|
1588
1591
|
|
|
1592
|
+
function tetherHostToAnchor(host, ul) {
|
|
1593
|
+
// Only called on upscroll to avoid breaking load logic.
|
|
1594
|
+
try {
|
|
1589
1595
|
if (!host) return;
|
|
1596
|
+
var wrap = host.querySelector && host.querySelector(BETWEEN_SEL);
|
|
1597
|
+
if (!wrap) { host.remove(); return; }
|
|
1590
1598
|
|
|
1591
|
-
//
|
|
1592
|
-
var anchorLi =
|
|
1599
|
+
// Determine anchor = nearest previous non-host li
|
|
1600
|
+
var anchorLi = closestNonHostLi(host);
|
|
1593
1601
|
if (!anchorLi) {
|
|
1602
|
+
// No anchor in DOM -> mark orphan (handled on upscroll only)
|
|
1594
1603
|
host.setAttribute('data-ezoic-orphan', '1');
|
|
1595
1604
|
return;
|
|
1596
1605
|
}
|
|
@@ -1602,114 +1611,96 @@ function buildOrdinalMap(items) {
|
|
|
1602
1611
|
}
|
|
1603
1612
|
host.setAttribute(TOKEN_ATTR, t);
|
|
1604
1613
|
host.removeAttribute('data-ezoic-orphan');
|
|
1614
|
+
|
|
1615
|
+
// If host drifted, put it back right after anchor
|
|
1616
|
+
if (host.previousElementSibling !== anchorLi) {
|
|
1617
|
+
anchorLi.insertAdjacentElement('afterend', host);
|
|
1618
|
+
}
|
|
1605
1619
|
} catch (e) {}
|
|
1606
1620
|
}
|
|
1607
1621
|
|
|
1608
|
-
function
|
|
1622
|
+
function reconcileUpScroll() {
|
|
1623
|
+
var y = getY();
|
|
1624
|
+
var dy = y - lastY;
|
|
1625
|
+
lastY = y;
|
|
1626
|
+
|
|
1627
|
+
// ALWAYS: repair invalid ul>div occurrences (cheap) but don't reorder
|
|
1609
1628
|
try {
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
var bad = ul.querySelectorAll(':scope > ' + BETWEEN_SEL);
|
|
1613
|
-
bad.forEach(function(wrap){ ensureHostAndTokenForWrap(wrap); });
|
|
1629
|
+
var bad = document.querySelectorAll('ul > ' + BETWEEN_SEL + ', ol > ' + BETWEEN_SEL);
|
|
1630
|
+
bad.forEach(function(w){ ensureHost(w); });
|
|
1614
1631
|
} catch (e) {}
|
|
1615
|
-
}
|
|
1616
1632
|
|
|
1617
|
-
|
|
1633
|
+
// If NOT upscroll, do nothing else (prevents "ads accumulate at bottom" and avoids scroll blocking)
|
|
1634
|
+
if (dy > -8) return;
|
|
1635
|
+
|
|
1636
|
+
// Up-scroll: tether/reconcile and drop orphan hosts (they cause pile-ups)
|
|
1618
1637
|
try {
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
} catch (e) {}
|
|
1638
|
+
var wraps = document.querySelectorAll(BETWEEN_SEL);
|
|
1639
|
+
wraps.forEach(function(w){
|
|
1640
|
+
var host = ensureHost(w);
|
|
1641
|
+
if (!host) host = w.closest ? w.closest('li.' + HOST_CLASS) : null;
|
|
1642
|
+
var ul = getListContainer(host || w);
|
|
1643
|
+
if (host && ul) tetherHostToAnchor(host, ul);
|
|
1626
1644
|
});
|
|
1627
|
-
} catch (e) {}
|
|
1628
|
-
}
|
|
1629
1645
|
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
var
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
// Reconcile / drop
|
|
1640
|
-
var hosts = ul.querySelectorAll('li.' + HOST_CLASS + '[' + TOKEN_ATTR + '], li.' + HOST_CLASS + '[data-ezoic-orphan="1"]');
|
|
1641
|
-
hosts.forEach(function(host){
|
|
1642
|
-
try {
|
|
1643
|
-
if (host.getAttribute('data-ezoic-orphan') === '1') { host.remove(); return; }
|
|
1644
|
-
var t = host.getAttribute(TOKEN_ATTR);
|
|
1645
|
-
if (!t) { host.remove(); return; }
|
|
1646
|
-
var anchor = ul.querySelector(TOPIC_LI_SEL + '[' + TOKEN_ATTR + '="' + t + '"]');
|
|
1647
|
-
if (!anchor) { host.remove(); return; }
|
|
1648
|
-
if (host.previousElementSibling !== anchor) anchor.insertAdjacentElement('afterend', host);
|
|
1649
|
-
} catch (e) {}
|
|
1646
|
+
// Drop orphans only on upscroll
|
|
1647
|
+
var orphans = document.querySelectorAll('li.' + HOST_CLASS + '[data-ezoic-orphan="1"]');
|
|
1648
|
+
orphans.forEach(function(h){ try { h.remove(); } catch(e) {} });
|
|
1649
|
+
|
|
1650
|
+
// Drop empty hosts
|
|
1651
|
+
var empties = document.querySelectorAll('li.' + HOST_CLASS);
|
|
1652
|
+
empties.forEach(function(h){
|
|
1653
|
+
try { if (!(h.querySelector && h.querySelector(BETWEEN_SEL))) h.remove(); } catch(e) {}
|
|
1650
1654
|
});
|
|
1651
1655
|
} catch (e) {}
|
|
1652
1656
|
}
|
|
1653
1657
|
|
|
1654
|
-
function
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
cleanupEmptyHosts(ul);
|
|
1665
|
-
reconcileUpScroll(ul);
|
|
1666
|
-
} catch (e) {}
|
|
1658
|
+
function schedule() {
|
|
1659
|
+
var now = Date.now();
|
|
1660
|
+
if (now - lastRun < COOLDOWN) return;
|
|
1661
|
+
if (pending) return;
|
|
1662
|
+
pending = true;
|
|
1663
|
+
requestAnimationFrame(function(){
|
|
1664
|
+
pending = false;
|
|
1665
|
+
lastRun = Date.now();
|
|
1666
|
+
reconcileUpScroll();
|
|
1667
|
+
});
|
|
1667
1668
|
}
|
|
1668
1669
|
|
|
1669
|
-
|
|
1670
|
-
|
|
1670
|
+
function init() {
|
|
1671
|
+
// Initial repair only
|
|
1671
1672
|
try {
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
for (var i=0;i<muts.length;i++){
|
|
1675
|
-
var m = muts[i];
|
|
1676
|
-
if (m.addedNodes && m.addedNodes.length) {
|
|
1677
|
-
for (var j=0;j<m.addedNodes.length;j++){
|
|
1678
|
-
var n = m.addedNodes[j];
|
|
1679
|
-
if (!n || n.nodeType !== 1) continue;
|
|
1680
|
-
if (n.matches && n.matches(BETWEEN_SEL)) {
|
|
1681
|
-
ensureHostAndTokenForWrap(n);
|
|
1682
|
-
} else if (n.querySelector && n.querySelector(BETWEEN_SEL)) {
|
|
1683
|
-
n.querySelectorAll(BETWEEN_SEL).forEach(function(w){ ensureHostAndTokenForWrap(w); });
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
// light sweep on the container only
|
|
1687
|
-
sweepAll(m.target);
|
|
1688
|
-
break;
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
});
|
|
1692
|
-
mo.observe(document.documentElement || document.body, { childList: true, subtree: true });
|
|
1673
|
+
var bad = document.querySelectorAll('ul > ' + BETWEEN_SEL + ', ol > ' + BETWEEN_SEL);
|
|
1674
|
+
bad.forEach(function(w){ ensureHost(w); });
|
|
1693
1675
|
} catch (e) {}
|
|
1694
|
-
}
|
|
1695
1676
|
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
sweepAll(document);
|
|
1699
|
-
window.addEventListener('scroll', function(){ sweepAll(document); }, { passive: true });
|
|
1700
|
-
window.addEventListener('resize', function(){ sweepAll(document); }, { passive: true });
|
|
1677
|
+
window.addEventListener('scroll', schedule, { passive: true });
|
|
1678
|
+
window.addEventListener('resize', schedule, { passive: true });
|
|
1701
1679
|
|
|
1702
1680
|
if (window.jQuery) {
|
|
1703
1681
|
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
|
|
1704
|
-
setTimeout(
|
|
1705
|
-
setTimeout(
|
|
1682
|
+
setTimeout(schedule, 0);
|
|
1683
|
+
setTimeout(schedule, 250);
|
|
1684
|
+
setTimeout(schedule, 900);
|
|
1706
1685
|
});
|
|
1707
1686
|
}
|
|
1708
|
-
|
|
1687
|
+
|
|
1688
|
+
// MutationObserver: only schedule (no immediate heavy work)
|
|
1689
|
+
try {
|
|
1690
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
1691
|
+
var mo = new MutationObserver(function(muts){
|
|
1692
|
+
for (var i=0;i<muts.length;i++){
|
|
1693
|
+
var m=muts[i];
|
|
1694
|
+
if ((m.addedNodes && m.addedNodes.length) || (m.removedNodes && m.removedNodes.length)) { schedule(); break; }
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1697
|
+
mo.observe(document.documentElement || document.body, { childList:true, subtree:true });
|
|
1698
|
+
}
|
|
1699
|
+
} catch (e) {}
|
|
1709
1700
|
}
|
|
1710
1701
|
|
|
1711
1702
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1712
1703
|
else init();
|
|
1713
1704
|
})();
|
|
1714
|
-
// ===== /V14.
|
|
1705
|
+
// ===== /V14.1.1 =====
|
|
1715
1706
|
|