nodebb-plugin-ezoic-infinite 1.6.17 → 1.6.18
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 +161 -147
- package/public/style.css +2 -2
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,116 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
function ezoicAnchorToken() {
|
|
5
|
-
// cheap unique-ish token
|
|
6
|
-
try { return String(Date.now()) + '-' + Math.random().toString(16).slice(2); } catch (e) { return String(Date.now()); }
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function ezoicInsertAfterWithToken(target, wrap, kindClass) {
|
|
10
|
-
try {
|
|
11
|
-
if (!target || !wrap) return;
|
|
12
|
-
|
|
13
|
-
// Tag the anchor LI (topic item) with a stable token the first time we attach a between ad to it.
|
|
14
|
-
// If NodeBB re-renders/virtualizes and recreates the LI, this tag disappears => we can drop orphan ads.
|
|
15
|
-
var anchorLi = null;
|
|
16
|
-
try { anchorLi = target.closest ? target.closest('li') : null; } catch (e) {}
|
|
17
|
-
if (!anchorLi && target.tagName === 'LI') anchorLi = target;
|
|
18
|
-
|
|
19
|
-
if (kindClass === 'ezoic-ad-between' && anchorLi) {
|
|
20
|
-
var token = anchorLi.getAttribute('data-ezoic-anchor-token');
|
|
21
|
-
if (!token) {
|
|
22
|
-
token = ezoicAnchorToken();
|
|
23
|
-
try { anchorLi.setAttribute('data-ezoic-anchor-token', token); } catch (e) {}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// If inside UL/OL, keep valid DOM with LI host, but keep wrap as DIV (compat).
|
|
27
|
-
var p = anchorLi.parentElement;
|
|
28
|
-
if (p && (p.tagName === 'UL' || p.tagName === 'OL')) {
|
|
29
|
-
var host = document.createElement('li');
|
|
30
|
-
host.className = 'nodebb-ezoic-host';
|
|
31
|
-
host.setAttribute('role', 'listitem');
|
|
32
|
-
host.style.listStyle = 'none';
|
|
33
|
-
host.style.width = '100%';
|
|
34
|
-
try { host.setAttribute('data-ezoic-anchor-token', token); } catch (e) {}
|
|
35
|
-
|
|
36
|
-
if (anchorLi.insertAdjacentElement) anchorLi.insertAdjacentElement('afterend', host);
|
|
37
|
-
else if (p.insertBefore) p.insertBefore(host, anchorLi.nextSibling);
|
|
38
|
-
|
|
39
|
-
try { host.appendChild(wrap); } catch (e) {}
|
|
40
|
-
try { wrap.style.width = '100%'; } catch (e) {}
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Non-list parent: just insert sibling but still tag wrap so we can reconcile
|
|
45
|
-
try { wrap.setAttribute('data-ezoic-anchor-token', token); } catch (e) {}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (target.insertAdjacentElement) ezoicInsertAfterWithToken(target, wrap, kindClass);
|
|
49
|
-
else if (target.parentNode) target.parentNode.insertBefore(wrap, target.nextSibling);
|
|
50
|
-
} catch (e) {}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function ezoicRepairBetweenWrapsToHost() {
|
|
54
|
-
// Repair any invalid UL children and ensure hosts/wraps carry token if possible.
|
|
55
|
-
try {
|
|
56
|
-
var bad = document.querySelectorAll('ul > div.nodebb-ezoic-wrap.ezoic-ad-between, ol > div.nodebb-ezoic-wrap.ezoic-ad-between');
|
|
57
|
-
bad.forEach(function (wrap) {
|
|
58
|
-
try {
|
|
59
|
-
var ul = wrap.parentElement;
|
|
60
|
-
if (!ul) return;
|
|
61
|
-
var prevLi = wrap.previousElementSibling && wrap.previousElementSibling.tagName === 'LI' ? wrap.previousElementSibling : null;
|
|
62
|
-
var token = prevLi ? prevLi.getAttribute('data-ezoic-anchor-token') : null;
|
|
63
|
-
if (!token && prevLi) {
|
|
64
|
-
token = ezoicAnchorToken();
|
|
65
|
-
prevLi.setAttribute('data-ezoic-anchor-token', token);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
var host = document.createElement('li');
|
|
69
|
-
host.className = 'nodebb-ezoic-host';
|
|
70
|
-
host.setAttribute('role', 'listitem');
|
|
71
|
-
host.style.listStyle = 'none';
|
|
72
|
-
host.style.width = '100%';
|
|
73
|
-
if (token) host.setAttribute('data-ezoic-anchor-token', token);
|
|
74
|
-
|
|
75
|
-
ul.insertBefore(host, wrap);
|
|
76
|
-
host.appendChild(wrap);
|
|
77
|
-
wrap.style.width = '100%';
|
|
78
|
-
} catch (e) {}
|
|
79
|
-
});
|
|
80
|
-
} catch (e) {}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function ezoicReconcileOrDropHostsByToken(scope) {
|
|
84
|
-
// Core fix: if NodeBB virtualizes and anchors disappear, DROP hosts so they can't pile up at top.
|
|
85
|
-
// If anchor exists, ensure host sits right after it.
|
|
86
|
-
try {
|
|
87
|
-
var root = scope || document;
|
|
88
|
-
var hosts = root.querySelectorAll('li.nodebb-ezoic-host[data-ezoic-anchor-token]');
|
|
89
|
-
if (!hosts || !hosts.length) return;
|
|
90
|
-
|
|
91
|
-
hosts.forEach(function(host){
|
|
92
|
-
try {
|
|
93
|
-
var token = host.getAttribute('data-ezoic-anchor-token');
|
|
94
|
-
if (!token) return;
|
|
95
|
-
var listEl = host.parentElement;
|
|
96
|
-
if (!listEl) return;
|
|
97
|
-
|
|
98
|
-
// Find the exact anchor LI that still carries the token.
|
|
99
|
-
var anchor = listEl.querySelector('li[data-ezoic-anchor-token="' + token + '"]');
|
|
100
|
-
if (!anchor) {
|
|
101
|
-
// anchor not in DOM => virtualized/re-rendered: remove host to avoid stacking
|
|
102
|
-
host.remove();
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
if (host.previousElementSibling !== anchor) {
|
|
106
|
-
anchor.insertAdjacentElement('afterend', host);
|
|
107
|
-
}
|
|
108
|
-
} catch (e) {}
|
|
109
|
-
});
|
|
110
|
-
} catch (e) {}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
4
|
// Track scroll direction to avoid aggressive recycling when the user scrolls upward.
|
|
115
5
|
// Recycling while scrolling up is a common cause of ads "bunching" and a "disappearing too fast" feeling.
|
|
116
6
|
let lastScrollY = 0;
|
|
@@ -778,7 +668,7 @@ function globalGapFixInit() {
|
|
|
778
668
|
insertingIds.add(id);
|
|
779
669
|
try {
|
|
780
670
|
const wrap = buildWrap(id, kindClass, afterPos, !existingPh);
|
|
781
|
-
|
|
671
|
+
target.insertAdjacentElement('afterend', wrap);
|
|
782
672
|
|
|
783
673
|
// If placeholder exists elsewhere (including pool), move it into the wrapper.
|
|
784
674
|
if (existingPh) {
|
|
@@ -1270,7 +1160,7 @@ function buildOrdinalMap(items) {
|
|
|
1270
1160
|
if (!anchorEl || !wrap || !wrap.isConnected) return null;
|
|
1271
1161
|
|
|
1272
1162
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
1273
|
-
|
|
1163
|
+
anchorEl.insertAdjacentElement('afterend', wrap);
|
|
1274
1164
|
|
|
1275
1165
|
// Ensure minimal layout impact.
|
|
1276
1166
|
try { wrap.style.contain = 'layout style paint'; } catch (e) {}
|
|
@@ -1620,49 +1510,173 @@ function buildOrdinalMap(items) {
|
|
|
1620
1510
|
})();
|
|
1621
1511
|
|
|
1622
1512
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1513
|
+
|
|
1514
|
+
// ===== V14.1 Hook any between-wrap and tether to nearest topic LI =====
|
|
1515
|
+
(function () {
|
|
1516
|
+
var BETWEEN_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1517
|
+
var HOST_CLASS = 'nodebb-ezoic-host';
|
|
1518
|
+
var TOKEN_ATTR = 'data-ezoic-anchor-token';
|
|
1519
|
+
var pendingSweep = false;
|
|
1520
|
+
var lastSweep = 0;
|
|
1521
|
+
var COOLDOWN = 120;
|
|
1522
|
+
|
|
1523
|
+
function token() {
|
|
1524
|
+
try { return String(Date.now()) + '-' + Math.random().toString(16).slice(2); } catch (e) { return String(Date.now()); }
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
function closestTopicLi(node) {
|
|
1528
|
+
// Find the topic <li> that this wrap should be associated with:
|
|
1529
|
+
// prefer previous sibling <li> that is NOT a host, else climb to nearest previous in the list.
|
|
1530
|
+
try {
|
|
1531
|
+
if (!node) return null;
|
|
1532
|
+
var cur = node;
|
|
1533
|
+
// If wrap is inside a host li, start from host
|
|
1534
|
+
if (cur.closest) {
|
|
1535
|
+
var host = cur.closest('li.' + HOST_CLASS);
|
|
1536
|
+
if (host) cur = host;
|
|
1537
|
+
}
|
|
1538
|
+
// Find list container
|
|
1539
|
+
var ul = cur.parentElement;
|
|
1540
|
+
while (ul && !(ul.tagName === 'UL' || ul.tagName === 'OL')) ul = ul.parentElement;
|
|
1541
|
+
if (!ul) return null;
|
|
1542
|
+
|
|
1543
|
+
// Start from current element position within ul
|
|
1544
|
+
var prev = cur.previousElementSibling;
|
|
1545
|
+
while (prev) {
|
|
1546
|
+
if (prev.tagName === 'LI' && !(prev.classList && prev.classList.contains(HOST_CLASS))) return prev;
|
|
1547
|
+
prev = prev.previousElementSibling;
|
|
1548
|
+
}
|
|
1549
|
+
} catch (e) {}
|
|
1550
|
+
return null;
|
|
1636
1551
|
}
|
|
1637
1552
|
|
|
1638
|
-
|
|
1639
|
-
|
|
1553
|
+
function ensureHostForWrap(wrap) {
|
|
1554
|
+
try {
|
|
1555
|
+
if (!wrap || wrap.nodeType !== 1) return;
|
|
1556
|
+
if (!(wrap.matches && wrap.matches(BETWEEN_SEL))) return;
|
|
1557
|
+
|
|
1558
|
+
// Already in a host?
|
|
1559
|
+
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1560
|
+
var ul = wrap.parentElement;
|
|
1561
|
+
while (ul && !(ul.tagName === 'UL' || ul.tagName === 'OL')) ul = ul.parentElement;
|
|
1562
|
+
if (!ul) return;
|
|
1563
|
+
|
|
1564
|
+
// Ensure wrap is inside UL only via LI host (never direct ul>div)
|
|
1565
|
+
if (!host) {
|
|
1566
|
+
host = document.createElement('li');
|
|
1567
|
+
host.className = HOST_CLASS;
|
|
1568
|
+
host.setAttribute('role', 'listitem');
|
|
1569
|
+
host.style.listStyle = 'none';
|
|
1570
|
+
host.style.width = '100%';
|
|
1571
|
+
// Insert host at wrap position
|
|
1572
|
+
ul.insertBefore(host, wrap);
|
|
1573
|
+
host.appendChild(wrap);
|
|
1574
|
+
} else if (host.parentElement !== ul) {
|
|
1575
|
+
// move host into correct list container if needed
|
|
1576
|
+
ul.appendChild(host);
|
|
1577
|
+
}
|
|
1640
1578
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1579
|
+
// Tether to nearest topic li and tag both with same token
|
|
1580
|
+
var anchorLi = closestTopicLi(host) || closestTopicLi(wrap);
|
|
1581
|
+
if (anchorLi) {
|
|
1582
|
+
var t = anchorLi.getAttribute(TOKEN_ATTR);
|
|
1583
|
+
if (!t) {
|
|
1584
|
+
t = token();
|
|
1585
|
+
anchorLi.setAttribute(TOKEN_ATTR, t);
|
|
1586
|
+
}
|
|
1587
|
+
host.setAttribute(TOKEN_ATTR, t);
|
|
1588
|
+
// keep host just after anchor
|
|
1589
|
+
if (host.previousElementSibling !== anchorLi) {
|
|
1590
|
+
anchorLi.insertAdjacentElement('afterend', host);
|
|
1591
|
+
}
|
|
1592
|
+
} else {
|
|
1593
|
+
// No anchor -> it's dangerous; mark host so we can drop it later
|
|
1594
|
+
host.setAttribute('data-ezoic-orphan', '1');
|
|
1595
|
+
}
|
|
1596
|
+
} catch (e) {}
|
|
1647
1597
|
}
|
|
1648
1598
|
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1599
|
+
function sweepAll() {
|
|
1600
|
+
var now = Date.now();
|
|
1601
|
+
if (now - lastSweep < COOLDOWN) return;
|
|
1602
|
+
lastSweep = now;
|
|
1603
|
+
|
|
1604
|
+
try {
|
|
1605
|
+
// 1) repair direct ul>div wraps
|
|
1606
|
+
var bad = document.querySelectorAll('ul > ' + BETWEEN_SEL + ', ol > ' + BETWEEN_SEL);
|
|
1607
|
+
bad.forEach(ensureHostForWrap);
|
|
1608
|
+
|
|
1609
|
+
// 2) ensure any between wrap anywhere is hosted+tethered
|
|
1610
|
+
var wraps = document.querySelectorAll(BETWEEN_SEL);
|
|
1611
|
+
wraps.forEach(ensureHostForWrap);
|
|
1612
|
+
|
|
1613
|
+
// 3) reconcile/drop hosts that lost their anchor (virtualized)
|
|
1614
|
+
var hosts = document.querySelectorAll('li.' + HOST_CLASS + '[' + TOKEN_ATTR + ']');
|
|
1615
|
+
hosts.forEach(function(host){
|
|
1616
|
+
try {
|
|
1617
|
+
var ul = host.parentElement;
|
|
1618
|
+
if (!ul) { host.remove(); return; }
|
|
1619
|
+
var t = host.getAttribute(TOKEN_ATTR);
|
|
1620
|
+
if (!t) { host.remove(); return; }
|
|
1621
|
+
var anchor = ul.querySelector('li[' + TOKEN_ATTR + '="' + t + '"]');
|
|
1622
|
+
if (!anchor) {
|
|
1623
|
+
host.remove();
|
|
1624
|
+
return;
|
|
1657
1625
|
}
|
|
1658
|
-
|
|
1626
|
+
if (host.previousElementSibling !== anchor) {
|
|
1627
|
+
anchor.insertAdjacentElement('afterend', host);
|
|
1628
|
+
}
|
|
1629
|
+
} catch (e) {}
|
|
1659
1630
|
});
|
|
1660
|
-
|
|
1661
|
-
|
|
1631
|
+
|
|
1632
|
+
// 4) drop explicit orphans
|
|
1633
|
+
var orphans = document.querySelectorAll('li.' + HOST_CLASS + '[data-ezoic-orphan="1"]');
|
|
1634
|
+
orphans.forEach(function(h){ try { h.remove(); } catch(e) {} });
|
|
1635
|
+
} catch (e) {}
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
function scheduleSweep() {
|
|
1639
|
+
if (pendingSweep) return;
|
|
1640
|
+
pendingSweep = true;
|
|
1641
|
+
requestAnimationFrame(function(){
|
|
1642
|
+
pendingSweep = false;
|
|
1643
|
+
sweepAll();
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
function installObservers() {
|
|
1648
|
+
// Observe the document for any between-wrap insertion/moves
|
|
1649
|
+
try {
|
|
1650
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
1651
|
+
var mo = new MutationObserver(function(muts){
|
|
1652
|
+
for (var i=0;i<muts.length;i++){
|
|
1653
|
+
var m = muts[i];
|
|
1654
|
+
if (m.addedNodes && m.addedNodes.length) { scheduleSweep(); break; }
|
|
1655
|
+
if (m.removedNodes && m.removedNodes.length) { scheduleSweep(); break; }
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
mo.observe(document.documentElement || document.body, { childList: true, subtree: true });
|
|
1659
|
+
}
|
|
1660
|
+
} catch (e) {}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
function init() {
|
|
1664
|
+
scheduleSweep();
|
|
1665
|
+
window.addEventListener('scroll', scheduleSweep, { passive: true });
|
|
1666
|
+
window.addEventListener('resize', scheduleSweep, { passive: true });
|
|
1667
|
+
|
|
1668
|
+
if (window.jQuery) {
|
|
1669
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
|
|
1670
|
+
setTimeout(scheduleSweep, 0);
|
|
1671
|
+
setTimeout(scheduleSweep, 250);
|
|
1672
|
+
setTimeout(scheduleSweep, 900);
|
|
1662
1673
|
});
|
|
1663
1674
|
}
|
|
1664
|
-
|
|
1675
|
+
installObservers();
|
|
1676
|
+
}
|
|
1665
1677
|
|
|
1666
|
-
|
|
1678
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1679
|
+
else init();
|
|
1667
1680
|
})();
|
|
1668
|
-
// ===== /V14 =====
|
|
1681
|
+
// ===== /V14.1 =====
|
|
1682
|
+
|