nodebb-plugin-ezoic-infinite 1.6.50 → 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 +108 -178
- 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.
|
|
@@ -661,8 +636,8 @@ function globalGapFixInit() {
|
|
|
661
636
|
|
|
662
637
|
// ---------------- insertion primitives ----------------
|
|
663
638
|
|
|
664
|
-
function buildWrap(id, kindClass, afterPos, createPlaceholder
|
|
665
|
-
const wrap = document.createElement(
|
|
639
|
+
function buildWrap(id, kindClass, afterPos, createPlaceholder) {
|
|
640
|
+
const wrap = document.createElement('div');
|
|
666
641
|
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
667
642
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
668
643
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
@@ -673,15 +648,6 @@ function globalGapFixInit() {
|
|
|
673
648
|
}
|
|
674
649
|
wrap.style.width = '100%';
|
|
675
650
|
|
|
676
|
-
// If we're inserted into a UL/OL list, use an LI wrapper to keep valid markup.
|
|
677
|
-
// Otherwise browsers can re-parent the DIV, which looks like items/ads “remontent”
|
|
678
|
-
// and can break topic list rendering.
|
|
679
|
-
if ((tagName || '').toLowerCase() === 'li') {
|
|
680
|
-
wrap.style.listStyle = 'none';
|
|
681
|
-
wrap.style.padding = '0';
|
|
682
|
-
wrap.style.margin = '0';
|
|
683
|
-
}
|
|
684
|
-
|
|
685
651
|
if (createPlaceholder) {
|
|
686
652
|
const ph = document.createElement('div');
|
|
687
653
|
ph.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
@@ -701,10 +667,7 @@ function globalGapFixInit() {
|
|
|
701
667
|
|
|
702
668
|
insertingIds.add(id);
|
|
703
669
|
try {
|
|
704
|
-
const
|
|
705
|
-
const parentTag = parent && parent.tagName ? parent.tagName.toUpperCase() : '';
|
|
706
|
-
const tagName = (parentTag === 'UL' || parentTag === 'OL') ? 'li' : 'div';
|
|
707
|
-
const wrap = buildWrap(id, kindClass, afterPos, !existingPh, tagName);
|
|
670
|
+
const wrap = buildWrap(id, kindClass, afterPos, !existingPh);
|
|
708
671
|
target.insertAdjacentElement('afterend', wrap);
|
|
709
672
|
|
|
710
673
|
// If placeholder exists elsewhere (including pool), move it into the wrapper.
|
|
@@ -716,9 +679,6 @@ function globalGapFixInit() {
|
|
|
716
679
|
} catch (e) {}
|
|
717
680
|
}
|
|
718
681
|
|
|
719
|
-
// Help creatives render promptly without requiring an actual user scroll.
|
|
720
|
-
scheduleViewportPoke(`afterInsert:${kindClass}:${id}`);
|
|
721
|
-
|
|
722
682
|
return wrap;
|
|
723
683
|
} finally {
|
|
724
684
|
insertingIds.delete(id);
|
|
@@ -1551,21 +1511,16 @@ function buildOrdinalMap(items) {
|
|
|
1551
1511
|
|
|
1552
1512
|
|
|
1553
1513
|
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
// ===== 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) =====
|
|
1558
1515
|
(function () {
|
|
1516
|
+
// Goal: keep ad injection intact. Only repair when we detect "pile-up" of between wraps.
|
|
1559
1517
|
var TOPIC_LI_SEL = 'li[component="category/topic"]';
|
|
1560
1518
|
var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1561
1519
|
var HOST_CLASS = 'nodebb-ezoic-host';
|
|
1562
|
-
var PENDING_CLASS = 'nodebb-ezoic-pending';
|
|
1563
1520
|
|
|
1564
|
-
var ul = null;
|
|
1565
|
-
var mo = null;
|
|
1566
1521
|
var scheduled = false;
|
|
1567
1522
|
var lastRun = 0;
|
|
1568
|
-
var COOLDOWN =
|
|
1523
|
+
var COOLDOWN = 180;
|
|
1569
1524
|
|
|
1570
1525
|
function getTopicList() {
|
|
1571
1526
|
try {
|
|
@@ -1575,41 +1530,29 @@ function buildOrdinalMap(items) {
|
|
|
1575
1530
|
} catch (e) { return null; }
|
|
1576
1531
|
}
|
|
1577
1532
|
|
|
1578
|
-
function
|
|
1579
|
-
|
|
1580
|
-
function isHost(el) {
|
|
1581
|
-
return !!(el && el.nodeType === 1 && el.tagName === 'LI' && el.classList && el.classList.contains(HOST_CLASS));
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
function isBetweenWrap(el) {
|
|
1585
|
-
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));
|
|
1586
1535
|
}
|
|
1587
1536
|
|
|
1588
|
-
function ensureHostForWrap(wrap,
|
|
1589
|
-
// If Ezoic inserts ul > div..., wrap it into a LI to keep a valid list structure (less NodeBB churn).
|
|
1537
|
+
function ensureHostForWrap(wrap, ul) {
|
|
1590
1538
|
try {
|
|
1591
|
-
if (!wrap ||
|
|
1539
|
+
if (!wrap || wrap.nodeType !== 1) return null;
|
|
1540
|
+
if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
|
|
1541
|
+
|
|
1592
1542
|
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1593
1543
|
if (host) return host;
|
|
1594
1544
|
|
|
1595
|
-
|
|
1596
|
-
if (!
|
|
1545
|
+
if (!ul) ul = wrap.closest ? wrap.closest('ul,ol') : null;
|
|
1546
|
+
if (!ul || !(ul.tagName === 'UL' || ul.tagName === 'OL')) return null;
|
|
1597
1547
|
|
|
1598
|
-
if (
|
|
1548
|
+
// Only wrap if direct child of list (invalid / fragile)
|
|
1549
|
+
if (wrap.parentElement === ul) {
|
|
1599
1550
|
host = document.createElement('li');
|
|
1600
1551
|
host.className = HOST_CLASS;
|
|
1601
1552
|
host.setAttribute('role', 'listitem');
|
|
1602
1553
|
host.style.listStyle = 'none';
|
|
1603
1554
|
host.style.width = '100%';
|
|
1604
|
-
|
|
1605
|
-
try {
|
|
1606
|
-
var after = wrap.getAttribute('data-ezoic-after');
|
|
1607
|
-
if (after) host.setAttribute('data-ezoic-after', after);
|
|
1608
|
-
var pin = wrap.getAttribute('data-ezoic-pin');
|
|
1609
|
-
if (pin) host.setAttribute('data-ezoic-pin', pin);
|
|
1610
|
-
} catch (e) {}
|
|
1611
|
-
|
|
1612
|
-
ulEl.insertBefore(host, wrap);
|
|
1555
|
+
ul.insertBefore(host, wrap);
|
|
1613
1556
|
host.appendChild(wrap);
|
|
1614
1557
|
try { wrap.style.width = '100%'; } catch (e) {}
|
|
1615
1558
|
return host;
|
|
@@ -1618,147 +1561,134 @@ function buildOrdinalMap(items) {
|
|
|
1618
1561
|
return null;
|
|
1619
1562
|
}
|
|
1620
1563
|
|
|
1621
|
-
function
|
|
1564
|
+
function previousTopicLi(node) {
|
|
1622
1565
|
try {
|
|
1623
|
-
var
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
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;
|
|
1628
1571
|
}
|
|
1629
|
-
var n = parseInt(v, 10);
|
|
1630
|
-
return isNaN(n) ? null : n;
|
|
1631
|
-
} catch (e) { return null; }
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
function getTopics(ulEl) {
|
|
1635
|
-
try { return ulEl ? ulEl.querySelectorAll(TOPIC_LI_SEL) : []; } catch(e){ return []; }
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
function lastTopic(ulEl, topics) {
|
|
1639
|
-
try {
|
|
1640
|
-
topics = topics || getTopics(ulEl);
|
|
1641
|
-
return topics && topics.length ? topics[topics.length - 1] : null;
|
|
1642
|
-
} catch (e) { return null; }
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
function moveAfter(node, anchor) {
|
|
1646
|
-
try {
|
|
1647
|
-
if (!node || !anchor || !anchor.insertAdjacentElement) return;
|
|
1648
|
-
if (node.previousElementSibling === anchor) return;
|
|
1649
|
-
anchor.insertAdjacentElement('afterend', node);
|
|
1650
1572
|
} catch (e) {}
|
|
1573
|
+
return null;
|
|
1651
1574
|
}
|
|
1652
1575
|
|
|
1653
|
-
function
|
|
1576
|
+
function detectPileUp(ul) {
|
|
1577
|
+
// Pile-up signature: 2+ between wraps/hosts adjacent with no topics between, often near top.
|
|
1654
1578
|
try {
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
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
|
+
}
|
|
1674
1599
|
}
|
|
1675
|
-
|
|
1676
|
-
var anchor = topics[after - 1];
|
|
1677
|
-
if (anchor) moveAfter(host, anchor);
|
|
1678
|
-
host.classList && host.classList.remove(PENDING_CLASS);
|
|
1600
|
+
return maxRun >= 2;
|
|
1679
1601
|
} catch (e) {}
|
|
1602
|
+
return false;
|
|
1680
1603
|
}
|
|
1681
1604
|
|
|
1682
|
-
function
|
|
1683
|
-
|
|
1684
|
-
|
|
1605
|
+
function redistribute(ul) {
|
|
1606
|
+
try {
|
|
1607
|
+
if (!ul) return;
|
|
1608
|
+
|
|
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); });
|
|
1685
1611
|
|
|
1686
|
-
|
|
1612
|
+
// Step 2: only act if we see pile-up (avoid touching infinite scroll during normal flow)
|
|
1613
|
+
if (!detectPileUp(ul)) return;
|
|
1687
1614
|
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
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;
|
|
1693
1621
|
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
});
|
|
1622
|
+
var anchor = previousTopicLi(host);
|
|
1623
|
+
if (!anchor) return; // if none, don't move (prevents yanking to top/bottom)
|
|
1697
1624
|
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1625
|
+
if (host.previousElementSibling !== anchor) {
|
|
1626
|
+
anchor.insertAdjacentElement('afterend', host);
|
|
1627
|
+
}
|
|
1628
|
+
} catch (e) {}
|
|
1701
1629
|
});
|
|
1702
1630
|
} catch (e) {}
|
|
1703
1631
|
}
|
|
1704
1632
|
|
|
1705
|
-
function
|
|
1633
|
+
function schedule(reason) {
|
|
1706
1634
|
var now = Date.now();
|
|
1707
|
-
if (scheduled) return;
|
|
1708
1635
|
if (now - lastRun < COOLDOWN) return;
|
|
1636
|
+
if (scheduled) return;
|
|
1709
1637
|
scheduled = true;
|
|
1710
|
-
lastRun = now;
|
|
1711
1638
|
requestAnimationFrame(function () {
|
|
1712
1639
|
scheduled = false;
|
|
1713
|
-
|
|
1640
|
+
lastRun = Date.now();
|
|
1641
|
+
try {
|
|
1642
|
+
var ul = getTopicList();
|
|
1643
|
+
if (!ul) return;
|
|
1644
|
+
redistribute(ul);
|
|
1645
|
+
} catch (e) {}
|
|
1714
1646
|
});
|
|
1715
1647
|
}
|
|
1716
1648
|
|
|
1717
|
-
function
|
|
1718
|
-
|
|
1719
|
-
if (!ulEl || mo) return;
|
|
1720
|
-
|
|
1721
|
-
mo = new MutationObserver(function () {
|
|
1722
|
-
scheduleReconcile();
|
|
1723
|
-
});
|
|
1649
|
+
function init() {
|
|
1650
|
+
schedule('init');
|
|
1724
1651
|
|
|
1725
|
-
|
|
1726
|
-
|
|
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
|
+
};
|
|
1727
1663
|
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
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) {}
|
|
1732
1678
|
|
|
1679
|
+
// NodeBB events: run after infinite scroll batches
|
|
1733
1680
|
if (window.jQuery) {
|
|
1734
1681
|
try {
|
|
1735
|
-
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
mo = null;
|
|
1739
|
-
initObserver();
|
|
1740
|
-
scheduleReconcile();
|
|
1741
|
-
scheduleViewportPoke('ajaxify.end');
|
|
1742
|
-
setTimeout(scheduleReconcile, 120);
|
|
1743
|
-
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);
|
|
1744
1685
|
});
|
|
1745
1686
|
} catch (e) {}
|
|
1746
1687
|
}
|
|
1747
|
-
|
|
1748
|
-
var tries = 0;
|
|
1749
|
-
var t = setInterval(function () {
|
|
1750
|
-
tries++;
|
|
1751
|
-
if (ensureUL()) {
|
|
1752
|
-
clearInterval(t);
|
|
1753
|
-
initObserver();
|
|
1754
|
-
scheduleReconcile();
|
|
1755
|
-
}
|
|
1756
|
-
if (tries > 30) clearInterval(t);
|
|
1757
|
-
}, 200);
|
|
1758
1688
|
}
|
|
1759
1689
|
|
|
1760
1690
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1761
1691
|
else init();
|
|
1762
1692
|
})();
|
|
1763
|
-
// ===== /V17
|
|
1693
|
+
// ===== /V17 =====
|
|
1764
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
|
-
|