nodebb-plugin-ezoic-infinite 1.6.20 → 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 +114 -65
- package/public/style.css +2 -2
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1511,46 +1511,75 @@ 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
|
+
var BETWEEN_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1516
1517
|
var HOST_CLASS = 'nodebb-ezoic-host';
|
|
1517
|
-
var
|
|
1518
|
-
|
|
1518
|
+
var TOKEN_ATTR = 'data-ezoic-anchor-token';
|
|
1519
|
+
|
|
1519
1520
|
var lastY = window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
1521
|
+
var pending = false;
|
|
1522
|
+
var lastRun = 0;
|
|
1523
|
+
var COOLDOWN = 140;
|
|
1520
1524
|
|
|
1521
1525
|
function getY() {
|
|
1522
1526
|
return window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
1523
1527
|
}
|
|
1524
1528
|
|
|
1525
|
-
function
|
|
1529
|
+
function token() {
|
|
1530
|
+
try { return String(Date.now()) + '-' + Math.random().toString(16).slice(2); } catch (e) { return String(Date.now()); }
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
function getListContainer(node) {
|
|
1526
1534
|
try {
|
|
1527
|
-
var
|
|
1528
|
-
if (
|
|
1529
|
-
var
|
|
1530
|
-
|
|
1531
|
-
|
|
1535
|
+
var ul = node && node.closest ? node.closest('ul,ol') : null;
|
|
1536
|
+
if (ul && (ul.tagName === 'UL' || ul.tagName === 'OL')) return ul;
|
|
1537
|
+
var p = node && node.parentElement;
|
|
1538
|
+
while (p) {
|
|
1539
|
+
if (p.tagName === 'UL' || p.tagName === 'OL') return p;
|
|
1540
|
+
p = p.parentElement;
|
|
1541
|
+
}
|
|
1542
|
+
} catch (e) {}
|
|
1543
|
+
return null;
|
|
1532
1544
|
}
|
|
1533
1545
|
|
|
1534
|
-
function
|
|
1535
|
-
|
|
1546
|
+
function closestNonHostLi(node) {
|
|
1547
|
+
try {
|
|
1548
|
+
var cur = node;
|
|
1549
|
+
if (!cur) return null;
|
|
1550
|
+
if (cur.closest) {
|
|
1551
|
+
var host = cur.closest('li.' + HOST_CLASS);
|
|
1552
|
+
if (host) cur = host;
|
|
1553
|
+
}
|
|
1554
|
+
var prev = cur.previousElementSibling;
|
|
1555
|
+
while (prev) {
|
|
1556
|
+
if (prev.tagName === 'LI' && !(prev.classList && prev.classList.contains(HOST_CLASS))) return prev;
|
|
1557
|
+
prev = prev.previousElementSibling;
|
|
1558
|
+
}
|
|
1559
|
+
} catch (e) {}
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
function ensureHost(wrap) {
|
|
1564
|
+
// Always ensure UL/OL doesn't directly contain DIV wraps.
|
|
1536
1565
|
try {
|
|
1537
1566
|
if (!wrap || wrap.nodeType !== 1) return null;
|
|
1538
|
-
if (!(wrap.matches && wrap.matches(
|
|
1539
|
-
|
|
1540
|
-
|
|
1567
|
+
if (!(wrap.matches && wrap.matches(BETWEEN_SEL))) return null;
|
|
1568
|
+
|
|
1569
|
+
var ul = getListContainer(wrap);
|
|
1541
1570
|
if (!ul) return null;
|
|
1542
1571
|
|
|
1543
|
-
// If already inside host, return host
|
|
1544
1572
|
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1545
1573
|
if (host) return host;
|
|
1546
1574
|
|
|
1547
|
-
// If direct child of UL/OL, create host
|
|
1575
|
+
// If direct child of UL/OL, create host in-place (does not affect infinite scroll measurements much)
|
|
1548
1576
|
if (wrap.parentElement === ul) {
|
|
1549
1577
|
host = document.createElement('li');
|
|
1550
1578
|
host.className = HOST_CLASS;
|
|
1551
1579
|
host.setAttribute('role', 'listitem');
|
|
1552
1580
|
host.style.listStyle = 'none';
|
|
1553
1581
|
host.style.width = '100%';
|
|
1582
|
+
|
|
1554
1583
|
ul.insertBefore(host, wrap);
|
|
1555
1584
|
host.appendChild(wrap);
|
|
1556
1585
|
try { wrap.style.width = '100%'; } catch (e) {}
|
|
@@ -1560,66 +1589,72 @@ function buildOrdinalMap(items) {
|
|
|
1560
1589
|
return null;
|
|
1561
1590
|
}
|
|
1562
1591
|
|
|
1563
|
-
function
|
|
1564
|
-
//
|
|
1565
|
-
// We'll locate the nth topic LI currently in DOM (1-indexed), then anchor after it.
|
|
1592
|
+
function tetherHostToAnchor(host, ul) {
|
|
1593
|
+
// Only called on upscroll to avoid breaking load logic.
|
|
1566
1594
|
try {
|
|
1567
|
-
if (!
|
|
1568
|
-
var
|
|
1569
|
-
if (!
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
if (
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1595
|
+
if (!host) return;
|
|
1596
|
+
var wrap = host.querySelector && host.querySelector(BETWEEN_SEL);
|
|
1597
|
+
if (!wrap) { host.remove(); return; }
|
|
1598
|
+
|
|
1599
|
+
// Determine anchor = nearest previous non-host li
|
|
1600
|
+
var anchorLi = closestNonHostLi(host);
|
|
1601
|
+
if (!anchorLi) {
|
|
1602
|
+
// No anchor in DOM -> mark orphan (handled on upscroll only)
|
|
1603
|
+
host.setAttribute('data-ezoic-orphan', '1');
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1577
1606
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1607
|
+
var t = anchorLi.getAttribute(TOKEN_ATTR);
|
|
1608
|
+
if (!t) {
|
|
1609
|
+
t = token();
|
|
1610
|
+
anchorLi.setAttribute(TOKEN_ATTR, t);
|
|
1611
|
+
}
|
|
1612
|
+
host.setAttribute(TOKEN_ATTR, t);
|
|
1613
|
+
host.removeAttribute('data-ezoic-orphan');
|
|
1581
1614
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1615
|
+
// If host drifted, put it back right after anchor
|
|
1616
|
+
if (host.previousElementSibling !== anchorLi) {
|
|
1617
|
+
anchorLi.insertAdjacentElement('afterend', host);
|
|
1618
|
+
}
|
|
1585
1619
|
} catch (e) {}
|
|
1620
|
+
}
|
|
1586
1621
|
|
|
1587
|
-
|
|
1622
|
+
function reconcileUpScroll() {
|
|
1588
1623
|
var y = getY();
|
|
1589
1624
|
var dy = y - lastY;
|
|
1590
1625
|
lastY = y;
|
|
1591
|
-
if (dy >= -10) return;
|
|
1592
1626
|
|
|
1627
|
+
// ALWAYS: repair invalid ul>div occurrences (cheap) but don't reorder
|
|
1593
1628
|
try {
|
|
1594
|
-
var
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
var wrap = host.querySelector && host.querySelector(WRAP_SEL);
|
|
1598
|
-
if (!wrap) { host.remove(); return; }
|
|
1629
|
+
var bad = document.querySelectorAll('ul > ' + BETWEEN_SEL + ', ol > ' + BETWEEN_SEL);
|
|
1630
|
+
bad.forEach(function(w){ ensureHost(w); });
|
|
1631
|
+
} catch (e) {}
|
|
1599
1632
|
|
|
1600
|
-
|
|
1601
|
-
|
|
1633
|
+
// If NOT upscroll, do nothing else (prevents "ads accumulate at bottom" and avoids scroll blocking)
|
|
1634
|
+
if (dy > -8) return;
|
|
1602
1635
|
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1636
|
+
// Up-scroll: tether/reconcile and drop orphan hosts (they cause pile-ups)
|
|
1637
|
+
try {
|
|
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);
|
|
1644
|
+
});
|
|
1609
1645
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
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) {}
|
|
1614
1654
|
});
|
|
1615
1655
|
} catch (e) {}
|
|
1616
1656
|
}
|
|
1617
1657
|
|
|
1618
|
-
// Throttled scheduler (scroll)
|
|
1619
|
-
var pending = false;
|
|
1620
|
-
var lastRun = 0;
|
|
1621
|
-
var COOLDOWN = 160;
|
|
1622
|
-
|
|
1623
1658
|
function schedule() {
|
|
1624
1659
|
var now = Date.now();
|
|
1625
1660
|
if (now - lastRun < COOLDOWN) return;
|
|
@@ -1628,15 +1663,15 @@ function buildOrdinalMap(items) {
|
|
|
1628
1663
|
requestAnimationFrame(function(){
|
|
1629
1664
|
pending = false;
|
|
1630
1665
|
lastRun = Date.now();
|
|
1631
|
-
|
|
1666
|
+
reconcileUpScroll();
|
|
1632
1667
|
});
|
|
1633
1668
|
}
|
|
1634
1669
|
|
|
1635
1670
|
function init() {
|
|
1636
|
-
//
|
|
1671
|
+
// Initial repair only
|
|
1637
1672
|
try {
|
|
1638
|
-
var
|
|
1639
|
-
|
|
1673
|
+
var bad = document.querySelectorAll('ul > ' + BETWEEN_SEL + ', ol > ' + BETWEEN_SEL);
|
|
1674
|
+
bad.forEach(function(w){ ensureHost(w); });
|
|
1640
1675
|
} catch (e) {}
|
|
1641
1676
|
|
|
1642
1677
|
window.addEventListener('scroll', schedule, { passive: true });
|
|
@@ -1645,13 +1680,27 @@ function buildOrdinalMap(items) {
|
|
|
1645
1680
|
if (window.jQuery) {
|
|
1646
1681
|
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
|
|
1647
1682
|
setTimeout(schedule, 0);
|
|
1648
|
-
setTimeout(schedule,
|
|
1683
|
+
setTimeout(schedule, 250);
|
|
1684
|
+
setTimeout(schedule, 900);
|
|
1649
1685
|
});
|
|
1650
1686
|
}
|
|
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) {}
|
|
1651
1700
|
}
|
|
1652
1701
|
|
|
1653
1702
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1654
1703
|
else init();
|
|
1655
1704
|
})();
|
|
1656
|
-
// ===== /V14.
|
|
1705
|
+
// ===== /V14.1.1 =====
|
|
1657
1706
|
|