nodebb-plugin-ezoic-infinite 1.6.27 → 1.6.28
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 +181 -108
- package/public/style.css +4 -4
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,93 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
function ezoicEnsureBetweenHost(anchorLi, wrap) {
|
|
5
|
-
try {
|
|
6
|
-
if (!anchorLi || !wrap) return null;
|
|
7
|
-
var ul = anchorLi.parentElement;
|
|
8
|
-
if (!ul || !(ul.tagName === 'UL' || ul.tagName === 'OL')) return null;
|
|
9
|
-
|
|
10
|
-
// already hosted?
|
|
11
|
-
var host = wrap.closest ? wrap.closest('li.nodebb-ezoic-host') : null;
|
|
12
|
-
if (host) return host;
|
|
13
|
-
|
|
14
|
-
host = document.createElement('li');
|
|
15
|
-
host.className = 'nodebb-ezoic-host';
|
|
16
|
-
host.setAttribute('role', 'listitem');
|
|
17
|
-
host.style.listStyle = 'none';
|
|
18
|
-
host.style.width = '100%';
|
|
19
|
-
|
|
20
|
-
// copy after position for later repairs
|
|
21
|
-
try {
|
|
22
|
-
var after = wrap.getAttribute && wrap.getAttribute('data-ezoic-after');
|
|
23
|
-
if (after) host.setAttribute('data-ezoic-after', after);
|
|
24
|
-
} catch (e) {}
|
|
25
|
-
|
|
26
|
-
anchorLi.insertAdjacentElement('afterend', host);
|
|
27
|
-
host.appendChild(wrap);
|
|
28
|
-
try { wrap.style.width = '100%'; } catch (e) {}
|
|
29
|
-
return host;
|
|
30
|
-
} catch (e) {}
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function ezoicDesiredAnchorForAfter(ul, afterNum) {
|
|
35
|
-
try {
|
|
36
|
-
var n = parseInt(afterNum, 10);
|
|
37
|
-
if (!n || n < 1) return null;
|
|
38
|
-
var topics = ul.querySelectorAll('li[component="category/topic"]');
|
|
39
|
-
if (!topics || !topics.length) return null;
|
|
40
|
-
if (n > topics.length) n = topics.length;
|
|
41
|
-
return topics[n-1] || null;
|
|
42
|
-
} catch (e) {}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function ezoicRepairBetweenHosts() {
|
|
47
|
-
// Reposition between hosts if NodeBB moved them (e.g., to the top/bottom) after virtualized rerender.
|
|
48
|
-
try {
|
|
49
|
-
var ul = document.querySelector('li[component="category/topic"]')?.closest('ul,ol');
|
|
50
|
-
if (!ul) return;
|
|
51
|
-
|
|
52
|
-
// 1) fix invalid ul>div wraps
|
|
53
|
-
ul.querySelectorAll(':scope > div.nodebb-ezoic-wrap.ezoic-ad-between').forEach(function(wrap){
|
|
54
|
-
try {
|
|
55
|
-
var prev = wrap.previousElementSibling;
|
|
56
|
-
if (prev && prev.tagName === 'LI') {
|
|
57
|
-
ezoicEnsureBetweenHost(prev, wrap);
|
|
58
|
-
}
|
|
59
|
-
} catch(e){}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// 2) ensure every between wrap is hosted (but don't scan whole document)
|
|
63
|
-
ul.querySelectorAll('div.nodebb-ezoic-wrap.ezoic-ad-between').forEach(function(wrap){
|
|
64
|
-
try {
|
|
65
|
-
var host = wrap.closest ? wrap.closest('li.nodebb-ezoic-host') : null;
|
|
66
|
-
if (!host) {
|
|
67
|
-
var prev = wrap.closest ? wrap.closest('li') : null;
|
|
68
|
-
if (prev) ezoicEnsureBetweenHost(prev, wrap);
|
|
69
|
-
}
|
|
70
|
-
} catch(e){}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// 3) reposition hosts based on data-ezoic-after (stable intent)
|
|
74
|
-
ul.querySelectorAll('li.nodebb-ezoic-host').forEach(function(host){
|
|
75
|
-
try {
|
|
76
|
-
var wrap = host.querySelector && host.querySelector('div.nodebb-ezoic-wrap.ezoic-ad-between');
|
|
77
|
-
if (!wrap) { host.remove(); return; }
|
|
78
|
-
var after = host.getAttribute('data-ezoic-after') || wrap.getAttribute('data-ezoic-after');
|
|
79
|
-
if (!after) return;
|
|
80
|
-
var anchor = ezoicDesiredAnchorForAfter(ul, after);
|
|
81
|
-
if (!anchor) return;
|
|
82
|
-
if (host.previousElementSibling !== anchor) {
|
|
83
|
-
anchor.insertAdjacentElement('afterend', host);
|
|
84
|
-
}
|
|
85
|
-
} catch(e){}
|
|
86
|
-
});
|
|
87
|
-
} catch (e) {}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
4
|
// Track scroll direction to avoid aggressive recycling when the user scrolls upward.
|
|
92
5
|
// Recycling while scrolling up is a common cause of ads "bunching" and a "disappearing too fast" feeling.
|
|
93
6
|
let lastScrollY = 0;
|
|
@@ -755,11 +668,7 @@ function globalGapFixInit() {
|
|
|
755
668
|
insertingIds.add(id);
|
|
756
669
|
try {
|
|
757
670
|
const wrap = buildWrap(id, kindClass, afterPos, !existingPh);
|
|
758
|
-
|
|
759
|
-
var aLi = (target.closest ? (target.closest('li[component="category/topic"]') || target.closest('li')) : null) || (target.tagName==='LI'?target:null);
|
|
760
|
-
if (aLi) { ezoicEnsureBetweenHost(aLi, wrap); return; }
|
|
761
|
-
}
|
|
762
|
-
target.insertAdjacentElement('afterend', wrap);
|
|
671
|
+
target.insertAdjacentElement('afterend', wrap);
|
|
763
672
|
|
|
764
673
|
// If placeholder exists elsewhere (including pool), move it into the wrapper.
|
|
765
674
|
if (existingPh) {
|
|
@@ -1251,11 +1160,7 @@ function buildOrdinalMap(items) {
|
|
|
1251
1160
|
if (!anchorEl || !wrap || !wrap.isConnected) return null;
|
|
1252
1161
|
|
|
1253
1162
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
1254
|
-
|
|
1255
|
-
var aLi = (anchorEl.closest ? (anchorEl.closest('li[component="category/topic"]') || anchorEl.closest('li')) : null) || (anchorEl.tagName==='LI'?anchorEl:null);
|
|
1256
|
-
if (aLi) { ezoicEnsureBetweenHost(aLi, wrap); return; }
|
|
1257
|
-
}
|
|
1258
|
-
anchorEl.insertAdjacentElement('afterend', wrap);
|
|
1163
|
+
anchorEl.insertAdjacentElement('afterend', wrap);
|
|
1259
1164
|
|
|
1260
1165
|
// Ensure minimal layout impact.
|
|
1261
1166
|
try { wrap.style.contain = 'layout style paint'; } catch (e) {}
|
|
@@ -1606,16 +1511,184 @@ function buildOrdinalMap(items) {
|
|
|
1606
1511
|
|
|
1607
1512
|
|
|
1608
1513
|
|
|
1609
|
-
// =====
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1514
|
+
// ===== V17 minimal pile-fix (no insert hooks) =====
|
|
1515
|
+
(function () {
|
|
1516
|
+
// Goal: keep ad injection intact. Only repair when we detect "pile-up" of between wraps.
|
|
1517
|
+
var TOPIC_LI_SEL = 'li[component="category/topic"]';
|
|
1518
|
+
var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1519
|
+
var HOST_CLASS = 'nodebb-ezoic-host';
|
|
1520
|
+
|
|
1521
|
+
var scheduled = false;
|
|
1522
|
+
var lastRun = 0;
|
|
1523
|
+
var COOLDOWN = 180;
|
|
1524
|
+
|
|
1525
|
+
function getTopicList() {
|
|
1526
|
+
try {
|
|
1527
|
+
var li = document.querySelector(TOPIC_LI_SEL);
|
|
1528
|
+
if (!li) return null;
|
|
1529
|
+
return li.closest ? li.closest('ul,ol') : null;
|
|
1530
|
+
} catch (e) { return null; }
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
function isHost(node) {
|
|
1534
|
+
return !!(node && node.nodeType === 1 && node.tagName === 'LI' && node.classList && node.classList.contains(HOST_CLASS));
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
function ensureHostForWrap(wrap, ul) {
|
|
1538
|
+
try {
|
|
1539
|
+
if (!wrap || wrap.nodeType !== 1) return null;
|
|
1540
|
+
if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
|
|
1541
|
+
|
|
1542
|
+
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1543
|
+
if (host) return host;
|
|
1544
|
+
|
|
1545
|
+
if (!ul) ul = wrap.closest ? wrap.closest('ul,ol') : null;
|
|
1546
|
+
if (!ul || !(ul.tagName === 'UL' || ul.tagName === 'OL')) return null;
|
|
1547
|
+
|
|
1548
|
+
// Only wrap if direct child of list (invalid / fragile)
|
|
1549
|
+
if (wrap.parentElement === ul) {
|
|
1550
|
+
host = document.createElement('li');
|
|
1551
|
+
host.className = HOST_CLASS;
|
|
1552
|
+
host.setAttribute('role', 'listitem');
|
|
1553
|
+
host.style.listStyle = 'none';
|
|
1554
|
+
host.style.width = '100%';
|
|
1555
|
+
ul.insertBefore(host, wrap);
|
|
1556
|
+
host.appendChild(wrap);
|
|
1557
|
+
try { wrap.style.width = '100%'; } catch (e) {}
|
|
1558
|
+
return host;
|
|
1559
|
+
}
|
|
1560
|
+
} catch (e) {}
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
function previousTopicLi(node) {
|
|
1565
|
+
try {
|
|
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;
|
|
1571
|
+
}
|
|
1572
|
+
} catch (e) {}
|
|
1573
|
+
return null;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
function detectPileUp(ul) {
|
|
1577
|
+
// Pile-up signature: 2+ between wraps/hosts adjacent with no topics between, often near top.
|
|
1578
|
+
try {
|
|
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
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
return maxRun >= 2;
|
|
1601
|
+
} catch (e) {}
|
|
1602
|
+
return false;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
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); });
|
|
1611
|
+
|
|
1612
|
+
// Step 2: only act if we see pile-up (avoid touching infinite scroll during normal flow)
|
|
1613
|
+
if (!detectPileUp(ul)) return;
|
|
1614
|
+
|
|
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)
|
|
1624
|
+
|
|
1625
|
+
if (host.previousElementSibling !== anchor) {
|
|
1626
|
+
anchor.insertAdjacentElement('afterend', host);
|
|
1627
|
+
}
|
|
1628
|
+
} catch (e) {}
|
|
1629
|
+
});
|
|
1630
|
+
} catch (e) {}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
function schedule(reason) {
|
|
1634
|
+
var now = Date.now();
|
|
1635
|
+
if (now - lastRun < COOLDOWN) return;
|
|
1636
|
+
if (scheduled) return;
|
|
1637
|
+
scheduled = true;
|
|
1638
|
+
requestAnimationFrame(function () {
|
|
1639
|
+
scheduled = false;
|
|
1640
|
+
lastRun = Date.now();
|
|
1641
|
+
try {
|
|
1642
|
+
var ul = getTopicList();
|
|
1643
|
+
if (!ul) return;
|
|
1644
|
+
redistribute(ul);
|
|
1645
|
+
} catch (e) {}
|
|
1617
1646
|
});
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
function init() {
|
|
1650
|
+
schedule('init');
|
|
1651
|
+
|
|
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
|
+
};
|
|
1663
|
+
|
|
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) {}
|
|
1678
|
+
|
|
1679
|
+
// NodeBB events: run after infinite scroll batches
|
|
1680
|
+
if (window.jQuery) {
|
|
1681
|
+
try {
|
|
1682
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
|
|
1683
|
+
setTimeout(function(){ schedule('event'); }, 50);
|
|
1684
|
+
setTimeout(function(){ schedule('event2'); }, 400);
|
|
1685
|
+
});
|
|
1686
|
+
} catch (e) {}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1691
|
+
else init();
|
|
1692
|
+
})();
|
|
1693
|
+
// ===== /V17 =====
|
|
1621
1694
|
|
package/public/style.css
CHANGED
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
/* =====
|
|
85
|
-
li.nodebb-ezoic-host { list-style: none; width: 100%; display:block; }
|
|
86
|
-
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; display:block; }
|
|
87
|
-
/* ===== /
|
|
84
|
+
/* ===== V17 host styling ===== */
|
|
85
|
+
li.nodebb-ezoic-host { list-style: none; width: 100%; display: block; }
|
|
86
|
+
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; display: block; }
|
|
87
|
+
/* ===== /V17 ===== */
|
|
88
88
|
|