nodebb-plugin-ezoic-infinite 1.6.49 → 1.6.51
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/README-V17.md +17 -0
- package/package.json +1 -1
- package/public/client.js +105 -163
- package/public/style.css +0 -17
package/README-V17.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# V17 – Minimal pile-up fixer (base: ezoic-infinite-fixed (1).zip)
|
|
2
|
+
|
|
3
|
+
Tu as demandé de repartir du zip d'origine: certaines variantes ont cassé l'injection.
|
|
4
|
+
|
|
5
|
+
## Principe
|
|
6
|
+
V17 **ne touche pas** au code d'injection (pas de hook dans insertAfter/moveWrapAfter).
|
|
7
|
+
Les pubs continuent de se créer comme avant.
|
|
8
|
+
|
|
9
|
+
V17 intervient seulement quand on détecte un symptôme "pile-up" (2+ pubs between consécutives sans topics entre):
|
|
10
|
+
- encapsule les `ul > div.nodebb-ezoic-wrap.ezoic-ad-between` dans un `<li class="nodebb-ezoic-host">` (DOM stable)
|
|
11
|
+
- redistribue les hosts en les replaçant juste après le topic `<li>` précédent dans la liste
|
|
12
|
+
|
|
13
|
+
## Déclencheurs
|
|
14
|
+
- MutationObserver uniquement sur la liste de topics (guarded par détection pile-up)
|
|
15
|
+
- events NodeBB ajaxify / infiniteScroll
|
|
16
|
+
|
|
17
|
+
Build: 2026-02-18T14:53:42.167388Z
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -63,31 +63,6 @@
|
|
|
63
63
|
// Production build: debug disabled
|
|
64
64
|
function dbg() {}
|
|
65
65
|
|
|
66
|
-
// Some ad providers (notably Google Safeframe) can defer creative rendering until a
|
|
67
|
-
// scroll/resize tick is observed. On SPA/infinite-scroll pages we can end up with
|
|
68
|
-
// valid iframes that stay visually empty until the user nudges the scroll.
|
|
69
|
-
// We gently "poke" observers by dispatching scroll/resize events (debounced).
|
|
70
|
-
let _ezPokeScheduled = false;
|
|
71
|
-
function scheduleViewportPoke(reason) {
|
|
72
|
-
if (_ezPokeScheduled) return;
|
|
73
|
-
_ezPokeScheduled = true;
|
|
74
|
-
try {
|
|
75
|
-
requestAnimationFrame(() => {
|
|
76
|
-
_ezPokeScheduled = false;
|
|
77
|
-
try {
|
|
78
|
-
window.dispatchEvent(new Event('scroll'));
|
|
79
|
-
window.dispatchEvent(new Event('resize'));
|
|
80
|
-
} catch (e) {}
|
|
81
|
-
try {
|
|
82
|
-
document.dispatchEvent(new Event('scroll'));
|
|
83
|
-
} catch (e) {}
|
|
84
|
-
dbg('viewportPoke', reason || '');
|
|
85
|
-
});
|
|
86
|
-
} catch (e) {
|
|
87
|
-
_ezPokeScheduled = false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
66
|
// Ezoic (and some partner scripts) can be very noisy in console on SPA/Ajaxify setups.
|
|
92
67
|
// These warnings are not actionable for end-users and can flood the console.
|
|
93
68
|
// We selectively silence the known spam patterns while keeping other warnings intact.
|
|
@@ -704,9 +679,6 @@ function globalGapFixInit() {
|
|
|
704
679
|
} catch (e) {}
|
|
705
680
|
}
|
|
706
681
|
|
|
707
|
-
// Help creatives render promptly without requiring an actual user scroll.
|
|
708
|
-
scheduleViewportPoke(`afterInsert:${kindClass}:${id}`);
|
|
709
|
-
|
|
710
682
|
return wrap;
|
|
711
683
|
} finally {
|
|
712
684
|
insertingIds.delete(id);
|
|
@@ -1539,21 +1511,16 @@ function buildOrdinalMap(items) {
|
|
|
1539
1511
|
|
|
1540
1512
|
|
|
1541
1513
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
// ===== V17.8: No pile-up by design — keep "future" between slots pending/collapsed until their anchor topic exists =====
|
|
1514
|
+
// ===== V17 minimal pile-fix (no insert hooks) =====
|
|
1546
1515
|
(function () {
|
|
1516
|
+
// Goal: keep ad injection intact. Only repair when we detect "pile-up" of between wraps.
|
|
1547
1517
|
var TOPIC_LI_SEL = 'li[component="category/topic"]';
|
|
1548
1518
|
var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1549
1519
|
var HOST_CLASS = 'nodebb-ezoic-host';
|
|
1550
|
-
var PENDING_CLASS = 'nodebb-ezoic-pending';
|
|
1551
1520
|
|
|
1552
|
-
var ul = null;
|
|
1553
|
-
var mo = null;
|
|
1554
1521
|
var scheduled = false;
|
|
1555
1522
|
var lastRun = 0;
|
|
1556
|
-
var COOLDOWN =
|
|
1523
|
+
var COOLDOWN = 180;
|
|
1557
1524
|
|
|
1558
1525
|
function getTopicList() {
|
|
1559
1526
|
try {
|
|
@@ -1563,41 +1530,29 @@ function buildOrdinalMap(items) {
|
|
|
1563
1530
|
} catch (e) { return null; }
|
|
1564
1531
|
}
|
|
1565
1532
|
|
|
1566
|
-
function
|
|
1567
|
-
|
|
1568
|
-
function isHost(el) {
|
|
1569
|
-
return !!(el && el.nodeType === 1 && el.tagName === 'LI' && el.classList && el.classList.contains(HOST_CLASS));
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
function isBetweenWrap(el) {
|
|
1573
|
-
try { return !!(el && el.nodeType === 1 && el.matches && el.matches(BETWEEN_WRAP_SEL)); } catch(e){ return false; }
|
|
1533
|
+
function isHost(node) {
|
|
1534
|
+
return !!(node && node.nodeType === 1 && node.tagName === 'LI' && node.classList && node.classList.contains(HOST_CLASS));
|
|
1574
1535
|
}
|
|
1575
1536
|
|
|
1576
|
-
function ensureHostForWrap(wrap,
|
|
1577
|
-
// If Ezoic inserts ul > div..., wrap it into a LI to keep a valid list structure (less NodeBB churn).
|
|
1537
|
+
function ensureHostForWrap(wrap, ul) {
|
|
1578
1538
|
try {
|
|
1579
|
-
if (!wrap ||
|
|
1539
|
+
if (!wrap || wrap.nodeType !== 1) return null;
|
|
1540
|
+
if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
|
|
1541
|
+
|
|
1580
1542
|
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1581
1543
|
if (host) return host;
|
|
1582
1544
|
|
|
1583
|
-
|
|
1584
|
-
if (!
|
|
1545
|
+
if (!ul) ul = wrap.closest ? wrap.closest('ul,ol') : null;
|
|
1546
|
+
if (!ul || !(ul.tagName === 'UL' || ul.tagName === 'OL')) return null;
|
|
1585
1547
|
|
|
1586
|
-
if (
|
|
1548
|
+
// Only wrap if direct child of list (invalid / fragile)
|
|
1549
|
+
if (wrap.parentElement === ul) {
|
|
1587
1550
|
host = document.createElement('li');
|
|
1588
1551
|
host.className = HOST_CLASS;
|
|
1589
1552
|
host.setAttribute('role', 'listitem');
|
|
1590
1553
|
host.style.listStyle = 'none';
|
|
1591
1554
|
host.style.width = '100%';
|
|
1592
|
-
|
|
1593
|
-
try {
|
|
1594
|
-
var after = wrap.getAttribute('data-ezoic-after');
|
|
1595
|
-
if (after) host.setAttribute('data-ezoic-after', after);
|
|
1596
|
-
var pin = wrap.getAttribute('data-ezoic-pin');
|
|
1597
|
-
if (pin) host.setAttribute('data-ezoic-pin', pin);
|
|
1598
|
-
} catch (e) {}
|
|
1599
|
-
|
|
1600
|
-
ulEl.insertBefore(host, wrap);
|
|
1555
|
+
ul.insertBefore(host, wrap);
|
|
1601
1556
|
host.appendChild(wrap);
|
|
1602
1557
|
try { wrap.style.width = '100%'; } catch (e) {}
|
|
1603
1558
|
return host;
|
|
@@ -1606,147 +1561,134 @@ function buildOrdinalMap(items) {
|
|
|
1606
1561
|
return null;
|
|
1607
1562
|
}
|
|
1608
1563
|
|
|
1609
|
-
function
|
|
1564
|
+
function previousTopicLi(node) {
|
|
1610
1565
|
try {
|
|
1611
|
-
var
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1566
|
+
var prev = node.previousElementSibling;
|
|
1567
|
+
while (prev) {
|
|
1568
|
+
if (prev.matches && prev.matches(TOPIC_LI_SEL)) return prev;
|
|
1569
|
+
// skip other hosts/wraps
|
|
1570
|
+
prev = prev.previousElementSibling;
|
|
1616
1571
|
}
|
|
1617
|
-
var n = parseInt(v, 10);
|
|
1618
|
-
return isNaN(n) ? null : n;
|
|
1619
|
-
} catch (e) { return null; }
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
function getTopics(ulEl) {
|
|
1623
|
-
try { return ulEl ? ulEl.querySelectorAll(TOPIC_LI_SEL) : []; } catch(e){ return []; }
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
function lastTopic(ulEl, topics) {
|
|
1627
|
-
try {
|
|
1628
|
-
topics = topics || getTopics(ulEl);
|
|
1629
|
-
return topics && topics.length ? topics[topics.length - 1] : null;
|
|
1630
|
-
} catch (e) { return null; }
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
function moveAfter(node, anchor) {
|
|
1634
|
-
try {
|
|
1635
|
-
if (!node || !anchor || !anchor.insertAdjacentElement) return;
|
|
1636
|
-
if (node.previousElementSibling === anchor) return;
|
|
1637
|
-
anchor.insertAdjacentElement('afterend', node);
|
|
1638
1572
|
} catch (e) {}
|
|
1573
|
+
return null;
|
|
1639
1574
|
}
|
|
1640
1575
|
|
|
1641
|
-
function
|
|
1576
|
+
function detectPileUp(ul) {
|
|
1577
|
+
// Pile-up signature: 2+ between wraps/hosts adjacent with no topics between, often near top.
|
|
1642
1578
|
try {
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1579
|
+
var kids = ul.children;
|
|
1580
|
+
var run = 0;
|
|
1581
|
+
var maxRun = 0;
|
|
1582
|
+
for (var i = 0; i < kids.length; i++) {
|
|
1583
|
+
var el = kids[i];
|
|
1584
|
+
var isBetween = false;
|
|
1585
|
+
if (isHost(el)) {
|
|
1586
|
+
isBetween = !!(el.querySelector && el.querySelector(BETWEEN_WRAP_SEL));
|
|
1587
|
+
} else if (el.matches && el.matches(BETWEEN_WRAP_SEL)) {
|
|
1588
|
+
isBetween = true;
|
|
1589
|
+
}
|
|
1590
|
+
if (isBetween) {
|
|
1591
|
+
run++;
|
|
1592
|
+
if (run > maxRun) maxRun = run;
|
|
1593
|
+
} else if (el.matches && el.matches(TOPIC_LI_SEL)) {
|
|
1594
|
+
run = 0;
|
|
1595
|
+
} else {
|
|
1596
|
+
// other nodes reset lightly
|
|
1597
|
+
run = 0;
|
|
1598
|
+
}
|
|
1662
1599
|
}
|
|
1663
|
-
|
|
1664
|
-
var anchor = topics[after - 1];
|
|
1665
|
-
if (anchor) moveAfter(host, anchor);
|
|
1666
|
-
host.classList && host.classList.remove(PENDING_CLASS);
|
|
1600
|
+
return maxRun >= 2;
|
|
1667
1601
|
} catch (e) {}
|
|
1602
|
+
return false;
|
|
1668
1603
|
}
|
|
1669
1604
|
|
|
1670
|
-
function
|
|
1671
|
-
|
|
1672
|
-
|
|
1605
|
+
function redistribute(ul) {
|
|
1606
|
+
try {
|
|
1607
|
+
if (!ul) return;
|
|
1673
1608
|
|
|
1674
|
-
|
|
1609
|
+
// Step 1: wrap any direct child between DIVs into LI hosts (makes list stable)
|
|
1610
|
+
ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
|
|
1675
1611
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
var h = ensureHostForWrap(w, ulEl);
|
|
1679
|
-
if (h) placeOrPend(h, ulEl, topics);
|
|
1680
|
-
});
|
|
1612
|
+
// Step 2: only act if we see pile-up (avoid touching infinite scroll during normal flow)
|
|
1613
|
+
if (!detectPileUp(ul)) return;
|
|
1681
1614
|
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1615
|
+
// Move each host to immediately after the closest previous topic LI at its current position.
|
|
1616
|
+
var hosts = ul.querySelectorAll(':scope > li.' + HOST_CLASS);
|
|
1617
|
+
hosts.forEach(function(host){
|
|
1618
|
+
try {
|
|
1619
|
+
var wrap = host.querySelector && host.querySelector(BETWEEN_WRAP_SEL);
|
|
1620
|
+
if (!wrap) return;
|
|
1621
|
+
|
|
1622
|
+
var anchor = previousTopicLi(host);
|
|
1623
|
+
if (!anchor) return; // if none, don't move (prevents yanking to top/bottom)
|
|
1685
1624
|
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1625
|
+
if (host.previousElementSibling !== anchor) {
|
|
1626
|
+
anchor.insertAdjacentElement('afterend', host);
|
|
1627
|
+
}
|
|
1628
|
+
} catch (e) {}
|
|
1689
1629
|
});
|
|
1690
1630
|
} catch (e) {}
|
|
1691
1631
|
}
|
|
1692
1632
|
|
|
1693
|
-
function
|
|
1633
|
+
function schedule(reason) {
|
|
1694
1634
|
var now = Date.now();
|
|
1695
|
-
if (scheduled) return;
|
|
1696
1635
|
if (now - lastRun < COOLDOWN) return;
|
|
1636
|
+
if (scheduled) return;
|
|
1697
1637
|
scheduled = true;
|
|
1698
|
-
lastRun = now;
|
|
1699
1638
|
requestAnimationFrame(function () {
|
|
1700
1639
|
scheduled = false;
|
|
1701
|
-
|
|
1640
|
+
lastRun = Date.now();
|
|
1641
|
+
try {
|
|
1642
|
+
var ul = getTopicList();
|
|
1643
|
+
if (!ul) return;
|
|
1644
|
+
redistribute(ul);
|
|
1645
|
+
} catch (e) {}
|
|
1702
1646
|
});
|
|
1703
1647
|
}
|
|
1704
1648
|
|
|
1705
|
-
function
|
|
1706
|
-
|
|
1707
|
-
if (!ulEl || mo) return;
|
|
1708
|
-
|
|
1709
|
-
mo = new MutationObserver(function () {
|
|
1710
|
-
scheduleReconcile();
|
|
1711
|
-
});
|
|
1649
|
+
function init() {
|
|
1650
|
+
schedule('init');
|
|
1712
1651
|
|
|
1713
|
-
|
|
1714
|
-
|
|
1652
|
+
// Observe only the topic list once available
|
|
1653
|
+
try {
|
|
1654
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
1655
|
+
var observeList = function(ul){
|
|
1656
|
+
if (!ul) return;
|
|
1657
|
+
var mo = new MutationObserver(function(muts){
|
|
1658
|
+
// schedule on any change; redistribute() itself is guarded by pile-up detection
|
|
1659
|
+
schedule('mo');
|
|
1660
|
+
});
|
|
1661
|
+
mo.observe(ul, { childList: true, subtree: true });
|
|
1662
|
+
};
|
|
1715
1663
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1664
|
+
var ul = getTopicList();
|
|
1665
|
+
if (ul) observeList(ul);
|
|
1666
|
+
else {
|
|
1667
|
+
var mo2 = new MutationObserver(function(){
|
|
1668
|
+
var u2 = getTopicList();
|
|
1669
|
+
if (u2) {
|
|
1670
|
+
try { observeList(u2); } catch(e){}
|
|
1671
|
+
try { mo2.disconnect(); } catch(e){}
|
|
1672
|
+
}
|
|
1673
|
+
});
|
|
1674
|
+
mo2.observe(document.documentElement || document.body, { childList: true, subtree: true });
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
} catch (e) {}
|
|
1720
1678
|
|
|
1679
|
+
// NodeBB events: run after infinite scroll batches
|
|
1721
1680
|
if (window.jQuery) {
|
|
1722
1681
|
try {
|
|
1723
|
-
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
mo = null;
|
|
1727
|
-
initObserver();
|
|
1728
|
-
scheduleReconcile();
|
|
1729
|
-
scheduleViewportPoke('ajaxify.end');
|
|
1730
|
-
setTimeout(scheduleReconcile, 120);
|
|
1731
|
-
setTimeout(scheduleReconcile, 500);
|
|
1682
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
|
|
1683
|
+
setTimeout(function(){ schedule('event'); }, 50);
|
|
1684
|
+
setTimeout(function(){ schedule('event2'); }, 400);
|
|
1732
1685
|
});
|
|
1733
1686
|
} catch (e) {}
|
|
1734
1687
|
}
|
|
1735
|
-
|
|
1736
|
-
var tries = 0;
|
|
1737
|
-
var t = setInterval(function () {
|
|
1738
|
-
tries++;
|
|
1739
|
-
if (ensureUL()) {
|
|
1740
|
-
clearInterval(t);
|
|
1741
|
-
initObserver();
|
|
1742
|
-
scheduleReconcile();
|
|
1743
|
-
}
|
|
1744
|
-
if (tries > 30) clearInterval(t);
|
|
1745
|
-
}, 200);
|
|
1746
1688
|
}
|
|
1747
1689
|
|
|
1748
1690
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1749
1691
|
else init();
|
|
1750
1692
|
})();
|
|
1751
|
-
// ===== /V17
|
|
1693
|
+
// ===== /V17 =====
|
|
1752
1694
|
|
package/public/style.css
CHANGED
|
@@ -86,20 +86,3 @@ li.nodebb-ezoic-host { list-style: none; width: 100%; display: block; }
|
|
|
86
86
|
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; display: block; }
|
|
87
87
|
/* ===== /V17 ===== */
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
/* ===== V17.8 pending slots (prevents pile-up top/bottom) ===== */
|
|
92
|
-
li.nodebb-ezoic-host { list-style:none; width:100%; display:block; }
|
|
93
|
-
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width:100%; display:block; }
|
|
94
|
-
|
|
95
|
-
/* slots whose anchor topic isn't loaded yet */
|
|
96
|
-
li.nodebb-ezoic-host.nodebb-ezoic-pending{
|
|
97
|
-
max-height: 0 !important;
|
|
98
|
-
margin: 0 !important;
|
|
99
|
-
padding: 0 !important;
|
|
100
|
-
overflow: hidden !important;
|
|
101
|
-
opacity: 0 !important;
|
|
102
|
-
pointer-events: none !important;
|
|
103
|
-
}
|
|
104
|
-
/* ===== /V17.8 ===== */
|
|
105
|
-
|