nodebb-plugin-ezoic-infinite 1.6.27 → 1.6.29
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 +173 -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,176 @@ function buildOrdinalMap(items) {
|
|
|
1606
1511
|
|
|
1607
1512
|
|
|
1608
1513
|
|
|
1609
|
-
// =====
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1514
|
+
// ===== V17.1 upscroll-only top pile-up fixer (keeps injection intact) =====
|
|
1515
|
+
(function () {
|
|
1516
|
+
var TOPIC_LI_SEL = 'li[component="category/topic"]';
|
|
1517
|
+
var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1518
|
+
var HOST_CLASS = 'nodebb-ezoic-host';
|
|
1519
|
+
|
|
1520
|
+
var scheduled = false;
|
|
1521
|
+
var lastRun = 0;
|
|
1522
|
+
var COOLDOWN = 140;
|
|
1523
|
+
|
|
1524
|
+
var lastY = window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
1525
|
+
|
|
1526
|
+
function getY() {
|
|
1527
|
+
return window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
function getTopicList() {
|
|
1531
|
+
try {
|
|
1532
|
+
var li = document.querySelector(TOPIC_LI_SEL);
|
|
1533
|
+
if (!li) return null;
|
|
1534
|
+
return li.closest ? li.closest('ul,ol') : null;
|
|
1535
|
+
} catch (e) { return null; }
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function isHost(node) {
|
|
1539
|
+
return !!(node && node.nodeType === 1 && node.tagName === 'LI' && node.classList && node.classList.contains(HOST_CLASS));
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
function ensureHostForWrap(wrap, ul) {
|
|
1543
|
+
// only wrap invalid ul>div cases; do not interfere with normal injection
|
|
1544
|
+
try {
|
|
1545
|
+
if (!wrap || wrap.nodeType !== 1) return null;
|
|
1546
|
+
if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
|
|
1547
|
+
|
|
1548
|
+
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1549
|
+
if (host) return host;
|
|
1550
|
+
|
|
1551
|
+
if (!ul) ul = wrap.closest ? wrap.closest('ul,ol') : null;
|
|
1552
|
+
if (!ul || !(ul.tagName === 'UL' || ul.tagName === 'OL')) return null;
|
|
1553
|
+
|
|
1554
|
+
if (wrap.parentElement === ul) {
|
|
1555
|
+
host = document.createElement('li');
|
|
1556
|
+
host.className = HOST_CLASS;
|
|
1557
|
+
host.setAttribute('role', 'listitem');
|
|
1558
|
+
host.style.listStyle = 'none';
|
|
1559
|
+
host.style.width = '100%';
|
|
1560
|
+
ul.insertBefore(host, wrap);
|
|
1561
|
+
host.appendChild(wrap);
|
|
1562
|
+
try { wrap.style.width = '100%'; } catch (e) {}
|
|
1563
|
+
return host;
|
|
1564
|
+
}
|
|
1565
|
+
} catch (e) {}
|
|
1566
|
+
return null;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
function previousTopicLi(node) {
|
|
1570
|
+
try {
|
|
1571
|
+
var prev = node.previousElementSibling;
|
|
1572
|
+
while (prev) {
|
|
1573
|
+
if (prev.matches && prev.matches(TOPIC_LI_SEL)) return prev;
|
|
1574
|
+
prev = prev.previousElementSibling;
|
|
1575
|
+
}
|
|
1576
|
+
} catch (e) {}
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
function detectTopPileUp(ul) {
|
|
1581
|
+
// Detect a pile-up near the top: within first ~40 children, find 2+ between ads without topics between.
|
|
1582
|
+
try {
|
|
1583
|
+
var kids = ul.children;
|
|
1584
|
+
var limit = Math.min(kids.length, 40);
|
|
1585
|
+
var run = 0;
|
|
1586
|
+
var maxRun = 0;
|
|
1587
|
+
for (var i=0;i<limit;i++){
|
|
1588
|
+
var el = kids[i];
|
|
1589
|
+
var isBetween = false;
|
|
1590
|
+
if (isHost(el)) isBetween = !!(el.querySelector && el.querySelector(BETWEEN_WRAP_SEL));
|
|
1591
|
+
else if (el.matches && el.matches(BETWEEN_WRAP_SEL)) isBetween = true;
|
|
1592
|
+
|
|
1593
|
+
if (isBetween) { run++; if (run>maxRun) maxRun=run; }
|
|
1594
|
+
else if (el.matches && el.matches(TOPIC_LI_SEL)) { run=0; }
|
|
1595
|
+
else { run=0; }
|
|
1596
|
+
}
|
|
1597
|
+
return maxRun >= 2;
|
|
1598
|
+
} catch (e) {}
|
|
1599
|
+
return false;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
function redistributeTopPile(ul) {
|
|
1603
|
+
try {
|
|
1604
|
+
if (!ul) return;
|
|
1605
|
+
|
|
1606
|
+
// Step 0: wrap invalid top-level between divs
|
|
1607
|
+
try { ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); }); } catch(e){}
|
|
1608
|
+
|
|
1609
|
+
if (!detectTopPileUp(ul)) return;
|
|
1610
|
+
|
|
1611
|
+
// Only move hosts that are currently in the top region (first 60 list children).
|
|
1612
|
+
var kids = ul.children;
|
|
1613
|
+
var limit = Math.min(kids.length, 60);
|
|
1614
|
+
for (var i=0;i<limit;i++){
|
|
1615
|
+
var el = kids[i];
|
|
1616
|
+
if (!isHost(el)) continue;
|
|
1617
|
+
var host = el;
|
|
1618
|
+
var wrap = host.querySelector && host.querySelector(BETWEEN_WRAP_SEL);
|
|
1619
|
+
if (!wrap) continue;
|
|
1620
|
+
|
|
1621
|
+
var anchor = previousTopicLi(host);
|
|
1622
|
+
if (!anchor) continue;
|
|
1623
|
+
if (host.previousElementSibling !== anchor) {
|
|
1624
|
+
anchor.insertAdjacentElement('afterend', host);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
} catch (e) {}
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
function schedule(reason) {
|
|
1631
|
+
var now = Date.now();
|
|
1632
|
+
if (now - lastRun < COOLDOWN) return;
|
|
1633
|
+
if (scheduled) return;
|
|
1634
|
+
scheduled = true;
|
|
1635
|
+
requestAnimationFrame(function(){
|
|
1636
|
+
scheduled = false;
|
|
1637
|
+
lastRun = Date.now();
|
|
1638
|
+
try {
|
|
1639
|
+
var ul = getTopicList();
|
|
1640
|
+
if (!ul) return;
|
|
1641
|
+
redistributeTopPile(ul);
|
|
1642
|
+
} catch (e) {}
|
|
1617
1643
|
});
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
function onScroll() {
|
|
1647
|
+
var y = getY();
|
|
1648
|
+
var dy = y - lastY;
|
|
1649
|
+
lastY = y;
|
|
1650
|
+
|
|
1651
|
+
// Only act on upscroll (when the pile-up manifests)
|
|
1652
|
+
if (dy < -6) schedule('upscroll');
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
function init() {
|
|
1656
|
+
// Initial repair does nothing unless pile-up already present.
|
|
1657
|
+
schedule('init');
|
|
1658
|
+
|
|
1659
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
1660
|
+
|
|
1661
|
+
// MutationObserver on list (guarded; redistribution only runs on upscroll)
|
|
1662
|
+
try {
|
|
1663
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
1664
|
+
var ul = getTopicList();
|
|
1665
|
+
if (ul) {
|
|
1666
|
+
var mo = new MutationObserver(function(){ /* no immediate redistribution */ });
|
|
1667
|
+
mo.observe(ul, { childList:true, subtree:true });
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
} catch (e) {}
|
|
1671
|
+
|
|
1672
|
+
if (window.jQuery) {
|
|
1673
|
+
try {
|
|
1674
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
|
|
1675
|
+
// don't touch during downscroll load; just re-evaluate when user scrolls up
|
|
1676
|
+
setTimeout(function(){ schedule('event'); }, 0);
|
|
1677
|
+
});
|
|
1678
|
+
} catch(e){}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1683
|
+
else init();
|
|
1684
|
+
})();
|
|
1685
|
+
// ===== /V17.1 =====
|
|
1621
1686
|
|
package/public/style.css
CHANGED
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
/* =====
|
|
85
|
-
li.nodebb-ezoic-host { list-style:
|
|
86
|
-
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width:
|
|
87
|
-
/* ===== /
|
|
84
|
+
/* ===== V17.1 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.1 ===== */
|
|
88
88
|
|