nodebb-plugin-ezoic-infinite 1.6.22 → 1.6.23
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 +155 -97
- package/public/style.css +2 -2
- package/README.md +0 -15
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1511,58 +1511,44 @@ function buildOrdinalMap(items) {
|
|
|
1511
1511
|
|
|
1512
1512
|
|
|
1513
1513
|
|
|
1514
|
-
// ===== V14.1.
|
|
1514
|
+
// ===== V14.1.3: Mutation-based reconcile (no scroll listener), place by data-ezoic-after =====
|
|
1515
1515
|
(function () {
|
|
1516
1516
|
var WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1517
|
-
var
|
|
1518
|
-
var
|
|
1517
|
+
var HOST_SEL = 'li.nodebb-ezoic-host';
|
|
1518
|
+
var TOPIC_LI_SEL = 'li[component="category/topic"]';
|
|
1519
1519
|
|
|
1520
|
-
|
|
1520
|
+
var freezeUntil = 0;
|
|
1521
|
+
var pending = false;
|
|
1522
|
+
var queue = new Set();
|
|
1523
|
+
|
|
1524
|
+
function now() { return Date.now(); }
|
|
1525
|
+
|
|
1526
|
+
function getTopicList() {
|
|
1521
1527
|
try {
|
|
1522
|
-
var
|
|
1523
|
-
if (
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
if (p.tagName === 'UL' || p.tagName === 'OL') return p;
|
|
1527
|
-
p = p.parentElement;
|
|
1528
|
-
}
|
|
1529
|
-
} catch (e) {}
|
|
1530
|
-
return null;
|
|
1528
|
+
var topic = document.querySelector(TOPIC_LI_SEL);
|
|
1529
|
+
if (!topic) return null;
|
|
1530
|
+
return topic.closest ? topic.closest('ul,ol') : null;
|
|
1531
|
+
} catch (e) { return null; }
|
|
1531
1532
|
}
|
|
1532
1533
|
|
|
1533
|
-
function
|
|
1534
|
+
function isLoading() {
|
|
1534
1535
|
try {
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
if (cur.closest) {
|
|
1539
|
-
var host = cur.closest('li.' + HOST_CLASS);
|
|
1540
|
-
if (host) cur = host;
|
|
1541
|
-
}
|
|
1542
|
-
var prev = cur.previousElementSibling;
|
|
1543
|
-
while (prev) {
|
|
1544
|
-
if (prev.tagName === 'LI' && !(prev.classList && prev.classList.contains(HOST_CLASS))) return prev;
|
|
1545
|
-
prev = prev.previousElementSibling;
|
|
1546
|
-
}
|
|
1547
|
-
} catch (e) {}
|
|
1548
|
-
return null;
|
|
1536
|
+
if (now() < freezeUntil) return true;
|
|
1537
|
+
return !!document.querySelector('.infinite-loading, .infinite-scroll-loading, .loading-indicator, .spinner, .topic-loading');
|
|
1538
|
+
} catch (e) { return false; }
|
|
1549
1539
|
}
|
|
1550
1540
|
|
|
1551
|
-
function ensureHostForWrap(wrap) {
|
|
1552
|
-
// Only repairs invalid UL>DIV. Does NOT move around the list except wrapping in-place.
|
|
1541
|
+
function ensureHostForWrap(wrap, ul) {
|
|
1553
1542
|
try {
|
|
1554
1543
|
if (!wrap || wrap.nodeType !== 1) return null;
|
|
1555
1544
|
if (!(wrap.matches && wrap.matches(WRAP_SEL))) return null;
|
|
1556
|
-
|
|
1557
|
-
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1545
|
+
var host = wrap.closest ? wrap.closest(HOST_SEL) : null;
|
|
1558
1546
|
if (host) return host;
|
|
1559
|
-
|
|
1560
|
-
var ul = getListContainer(wrap);
|
|
1547
|
+
ul = ul || (wrap.closest ? wrap.closest('ul,ol') : null);
|
|
1561
1548
|
if (!ul) return null;
|
|
1562
|
-
|
|
1563
1549
|
if (wrap.parentElement === ul) {
|
|
1564
1550
|
host = document.createElement('li');
|
|
1565
|
-
host.className =
|
|
1551
|
+
host.className = 'nodebb-ezoic-host';
|
|
1566
1552
|
host.setAttribute('role', 'listitem');
|
|
1567
1553
|
host.style.listStyle = 'none';
|
|
1568
1554
|
host.style.width = '100%';
|
|
@@ -1575,98 +1561,170 @@ function buildOrdinalMap(items) {
|
|
|
1575
1561
|
return null;
|
|
1576
1562
|
}
|
|
1577
1563
|
|
|
1578
|
-
function
|
|
1579
|
-
|
|
1564
|
+
function nthTopic(ul, n) {
|
|
1565
|
+
try {
|
|
1566
|
+
var topics = ul.querySelectorAll(TOPIC_LI_SEL);
|
|
1567
|
+
if (!topics || !topics.length) return null;
|
|
1568
|
+
if (n < 1) n = 1;
|
|
1569
|
+
if (n > topics.length) n = topics.length;
|
|
1570
|
+
return topics[n-1] || null;
|
|
1571
|
+
} catch (e) { return null; }
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
function previousTopicLi(host) {
|
|
1575
|
+
try {
|
|
1576
|
+
var prev = host.previousElementSibling;
|
|
1577
|
+
while (prev) {
|
|
1578
|
+
if (prev.matches && prev.matches(TOPIC_LI_SEL)) return prev;
|
|
1579
|
+
prev = prev.previousElementSibling;
|
|
1580
|
+
}
|
|
1581
|
+
} catch (e) {}
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
function placeHost(host, ul) {
|
|
1580
1586
|
try {
|
|
1581
1587
|
if (!host || host.nodeType !== 1) return;
|
|
1582
|
-
|
|
1588
|
+
ul = ul || host.parentElement;
|
|
1583
1589
|
if (!ul) return;
|
|
1584
1590
|
|
|
1585
|
-
var
|
|
1586
|
-
if (!
|
|
1591
|
+
var wrap = host.querySelector && host.querySelector(WRAP_SEL);
|
|
1592
|
+
if (!wrap) { host.remove(); return; }
|
|
1593
|
+
|
|
1594
|
+
var after = wrap.getAttribute('data-ezoic-after');
|
|
1595
|
+
var anchor = null;
|
|
1596
|
+
|
|
1597
|
+
if (after) {
|
|
1598
|
+
anchor = nthTopic(ul, parseInt(after, 10));
|
|
1599
|
+
}
|
|
1600
|
+
if (!anchor) {
|
|
1601
|
+
anchor = previousTopicLi(host);
|
|
1602
|
+
}
|
|
1603
|
+
if (!anchor) {
|
|
1604
|
+
// Can't place reliably; if host drifted to top area, remove to avoid pile-up.
|
|
1605
|
+
// Otherwise keep as-is.
|
|
1606
|
+
var firstTopic = nthTopic(ul, 1);
|
|
1607
|
+
if (firstTopic && host !== firstTopic && host.compareDocumentPosition(firstTopic) & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
1608
|
+
host.remove();
|
|
1609
|
+
}
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1587
1612
|
|
|
1588
|
-
if (host.previousElementSibling !==
|
|
1589
|
-
|
|
1613
|
+
if (host.previousElementSibling !== anchor) {
|
|
1614
|
+
anchor.insertAdjacentElement('afterend', host);
|
|
1590
1615
|
}
|
|
1591
1616
|
} catch (e) {}
|
|
1592
1617
|
}
|
|
1593
1618
|
|
|
1594
|
-
function
|
|
1619
|
+
function drain() {
|
|
1620
|
+
pending = false;
|
|
1621
|
+
if (isLoading()) {
|
|
1622
|
+
// retry later
|
|
1623
|
+
scheduleDrain(250);
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
var ul = getTopicList();
|
|
1627
|
+
if (!ul) return;
|
|
1628
|
+
|
|
1629
|
+
// repair invalid ul>div once
|
|
1595
1630
|
try {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1631
|
+
ul.querySelectorAll(':scope > ' + WRAP_SEL).forEach(function(w){
|
|
1632
|
+
var h = ensureHostForWrap(w, ul);
|
|
1633
|
+
if (h) queue.add(h);
|
|
1634
|
+
});
|
|
1599
1635
|
} catch (e) {}
|
|
1636
|
+
|
|
1637
|
+
var count = 0;
|
|
1638
|
+
for (var host of Array.from(queue)) {
|
|
1639
|
+
queue.delete(host);
|
|
1640
|
+
placeHost(host, ul);
|
|
1641
|
+
count++;
|
|
1642
|
+
if (count >= 8) break; // keep it light
|
|
1643
|
+
}
|
|
1644
|
+
if (queue.size) scheduleDrain(60);
|
|
1600
1645
|
}
|
|
1601
1646
|
|
|
1602
|
-
function
|
|
1603
|
-
if (
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
});
|
|
1611
|
-
} catch (e) {}
|
|
1612
|
-
}, { root: null, rootMargin: '800px 0px 800px 0px', threshold: 0.01 });
|
|
1647
|
+
function scheduleDrain(delay) {
|
|
1648
|
+
if (pending) return;
|
|
1649
|
+
pending = true;
|
|
1650
|
+
if (delay) {
|
|
1651
|
+
setTimeout(function(){ requestAnimationFrame(drain); }, delay);
|
|
1652
|
+
} else {
|
|
1653
|
+
requestAnimationFrame(drain);
|
|
1654
|
+
}
|
|
1613
1655
|
}
|
|
1614
1656
|
|
|
1615
|
-
function
|
|
1657
|
+
function enqueueFromNode(node) {
|
|
1616
1658
|
try {
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1659
|
+
if (!node || node.nodeType !== 1) return;
|
|
1660
|
+
var ul = getTopicList();
|
|
1661
|
+
if (!ul) return;
|
|
1662
|
+
|
|
1663
|
+
if (node.matches && node.matches(WRAP_SEL)) {
|
|
1664
|
+
var h = ensureHostForWrap(node, ul) || (node.closest ? node.closest(HOST_SEL) : null);
|
|
1665
|
+
if (h) queue.add(h);
|
|
1666
|
+
} else if (node.matches && node.matches(HOST_SEL)) {
|
|
1667
|
+
queue.add(node);
|
|
1668
|
+
} else if (node.querySelectorAll) {
|
|
1669
|
+
node.querySelectorAll(WRAP_SEL).forEach(function(w){
|
|
1670
|
+
var h2 = ensureHostForWrap(w, ul) || (w.closest ? w.closest(HOST_SEL) : null);
|
|
1671
|
+
if (h2) queue.add(h2);
|
|
1672
|
+
});
|
|
1673
|
+
node.querySelectorAll(HOST_SEL).forEach(function(h3){ queue.add(h3); });
|
|
1622
1674
|
}
|
|
1623
1675
|
} catch (e) {}
|
|
1624
1676
|
}
|
|
1625
1677
|
|
|
1626
|
-
function
|
|
1678
|
+
function init() {
|
|
1679
|
+
// initial scan (light)
|
|
1627
1680
|
try {
|
|
1628
|
-
|
|
1681
|
+
var ul = getTopicList();
|
|
1682
|
+
if (ul) {
|
|
1683
|
+
ul.querySelectorAll(WRAP_SEL).forEach(function(w){
|
|
1684
|
+
var h = ensureHostForWrap(w, ul) || (w.closest ? w.closest(HOST_SEL) : null);
|
|
1685
|
+
if (h) queue.add(h);
|
|
1686
|
+
});
|
|
1687
|
+
scheduleDrain(0);
|
|
1688
|
+
}
|
|
1629
1689
|
} catch (e) {}
|
|
1630
|
-
}
|
|
1631
1690
|
|
|
1632
|
-
|
|
1691
|
+
// Observe ONLY the topic list container once available
|
|
1633
1692
|
try {
|
|
1634
|
-
if (typeof MutationObserver
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
for (var i = 0; i < muts.length; i++) {
|
|
1693
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
1694
|
+
var mo = new MutationObserver(function(muts){
|
|
1695
|
+
for (var i=0;i<muts.length;i++){
|
|
1638
1696
|
var m = muts[i];
|
|
1639
|
-
if (
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
var n = m.addedNodes[j];
|
|
1643
|
-
if (!n || n.nodeType !== 1) continue;
|
|
1644
|
-
|
|
1645
|
-
if (n.matches && n.matches(WRAP_SEL)) {
|
|
1646
|
-
processWrap(n);
|
|
1647
|
-
} else if (n.querySelectorAll) {
|
|
1648
|
-
var inner = n.querySelectorAll(WRAP_SEL);
|
|
1649
|
-
if (inner && inner.length) inner.forEach(processWrap);
|
|
1697
|
+
if (m.addedNodes && m.addedNodes.length) {
|
|
1698
|
+
for (var j=0;j<m.addedNodes.length;j++){
|
|
1699
|
+
enqueueFromNode(m.addedNodes[j]);
|
|
1650
1700
|
}
|
|
1701
|
+
scheduleDrain(0);
|
|
1651
1702
|
}
|
|
1652
1703
|
}
|
|
1653
|
-
}
|
|
1654
|
-
});
|
|
1655
|
-
mo.observe(document.documentElement || document.body, { childList: true, subtree: true });
|
|
1656
|
-
} catch (e) {}
|
|
1657
|
-
}
|
|
1704
|
+
});
|
|
1658
1705
|
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1706
|
+
var ul = getTopicList();
|
|
1707
|
+
if (ul) mo.observe(ul, { childList:true, subtree:true });
|
|
1708
|
+
else {
|
|
1709
|
+
// wait for ajaxify
|
|
1710
|
+
var mo2 = new MutationObserver(function(){
|
|
1711
|
+
var u2 = getTopicList();
|
|
1712
|
+
if (u2) {
|
|
1713
|
+
try { mo.observe(u2, { childList:true, subtree:true }); } catch(e){}
|
|
1714
|
+
try { mo2.disconnect(); } catch(e){}
|
|
1715
|
+
}
|
|
1716
|
+
});
|
|
1717
|
+
mo2.observe(document.documentElement || document.body, { childList:true, subtree:true });
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
} catch (e) {}
|
|
1663
1721
|
|
|
1664
1722
|
if (window.jQuery) {
|
|
1665
1723
|
try {
|
|
1666
|
-
window.jQuery(window).on('action:
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
setTimeout(
|
|
1724
|
+
window.jQuery(window).on('action:infiniteScroll.loaded action:ajaxify.end', function(){
|
|
1725
|
+
freezeUntil = now() + 900; // allow NodeBB to settle
|
|
1726
|
+
// then reconcile any drift
|
|
1727
|
+
setTimeout(function(){ scheduleDrain(0); }, 950);
|
|
1670
1728
|
});
|
|
1671
1729
|
} catch (e) {}
|
|
1672
1730
|
}
|
|
@@ -1675,5 +1733,5 @@ function buildOrdinalMap(items) {
|
|
|
1675
1733
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1676
1734
|
else init();
|
|
1677
1735
|
})();
|
|
1678
|
-
// ===== /V14.1.
|
|
1736
|
+
// ===== /V14.1.3 =====
|
|
1679
1737
|
|
package/public/style.css
CHANGED
package/README.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# NodeBB Plugin – Ezoic Infinite (Production)
|
|
2
|
-
|
|
3
|
-
This plugin injects Ezoic placeholders between topics and posts on NodeBB 4.x,
|
|
4
|
-
with full support for infinite scroll.
|
|
5
|
-
|
|
6
|
-
## Key guarantees
|
|
7
|
-
- No duplicate ads back-to-back
|
|
8
|
-
- One showAds call per placeholder
|
|
9
|
-
- Fast reveal (MutationObserver on first child)
|
|
10
|
-
- Safe with ajaxify navigation
|
|
11
|
-
- Works with NodeBB 4.x + Harmony
|
|
12
|
-
|
|
13
|
-
## Notes
|
|
14
|
-
- Placeholders must exist and be selected in Ezoic
|
|
15
|
-
- Use separate ID pools for topics vs messages
|