nodebb-plugin-ezoic-infinite 1.6.16 → 1.6.17
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 +151 -96
- package/public/style.css +3 -7
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,6 +1,116 @@
|
|
|
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
|
+
|
|
4
114
|
// Track scroll direction to avoid aggressive recycling when the user scrolls upward.
|
|
5
115
|
// Recycling while scrolling up is a common cause of ads "bunching" and a "disappearing too fast" feeling.
|
|
6
116
|
let lastScrollY = 0;
|
|
@@ -668,7 +778,7 @@ function globalGapFixInit() {
|
|
|
668
778
|
insertingIds.add(id);
|
|
669
779
|
try {
|
|
670
780
|
const wrap = buildWrap(id, kindClass, afterPos, !existingPh);
|
|
671
|
-
target
|
|
781
|
+
ezoicInsertAfterWithToken(target, wrap, kindClass);
|
|
672
782
|
|
|
673
783
|
// If placeholder exists elsewhere (including pool), move it into the wrapper.
|
|
674
784
|
if (existingPh) {
|
|
@@ -1160,7 +1270,7 @@ function buildOrdinalMap(items) {
|
|
|
1160
1270
|
if (!anchorEl || !wrap || !wrap.isConnected) return null;
|
|
1161
1271
|
|
|
1162
1272
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
1163
|
-
anchorEl
|
|
1273
|
+
ezoicInsertAfterWithToken(anchorEl, wrap, kindClass);
|
|
1164
1274
|
|
|
1165
1275
|
// Ensure minimal layout impact.
|
|
1166
1276
|
try { wrap.style.contain = 'layout style paint'; } catch (e) {}
|
|
@@ -1510,104 +1620,49 @@ function buildOrdinalMap(items) {
|
|
|
1510
1620
|
})();
|
|
1511
1621
|
|
|
1512
1622
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
n.style.setProperty('top', 'auto', 'important');
|
|
1527
|
-
n.style.setProperty('bottom', 'auto', 'important');
|
|
1528
|
-
n.style.setProperty('left', 'auto', 'important');
|
|
1529
|
-
n.style.setProperty('right', 'auto', 'important');
|
|
1530
|
-
// if they force translate/transform for sticky, remove it
|
|
1531
|
-
n.style.setProperty('transform', 'none', 'important');
|
|
1532
|
-
n.style.setProperty('will-change', 'auto', 'important');
|
|
1533
|
-
// also remove class to reduce re-application
|
|
1534
|
-
if (n.classList) n.classList.remove('ezads-sticky-intradiv');
|
|
1535
|
-
} catch (e) {}
|
|
1536
|
-
// If inline style string contains "sticky", scrub it (some libs reparse cssText)
|
|
1537
|
-
try {
|
|
1538
|
-
var st = n.getAttribute && n.getAttribute('style');
|
|
1539
|
-
if (st && (st.indexOf('sticky') !== -1)) {
|
|
1540
|
-
var cleaned = st.replace(/position\s*:\s*sticky\s*!important\s*;?/gi, 'position: static !important;')
|
|
1541
|
-
.replace(/position\s*:\s*sticky\s*;?/gi, 'position: static;')
|
|
1542
|
-
.replace(/top\s*:\s*0px\s*!important\s*;?/gi, 'top: auto !important;')
|
|
1543
|
-
.replace(/top\s*:\s*0px\s*;?/gi, 'top: auto;');
|
|
1544
|
-
n.setAttribute('style', cleaned);
|
|
1545
|
-
}
|
|
1546
|
-
} catch (e) {}
|
|
1547
|
-
});
|
|
1548
|
-
} catch (e) {}
|
|
1623
|
+
// ===== V14 token reconcile scheduler =====
|
|
1624
|
+
(function(){
|
|
1625
|
+
var pending=false, last=0, COOLDOWN=140;
|
|
1626
|
+
function schedule(scope){
|
|
1627
|
+
var now=Date.now();
|
|
1628
|
+
if (now-last<COOLDOWN) return;
|
|
1629
|
+
if (pending) return;
|
|
1630
|
+
pending=true;
|
|
1631
|
+
requestAnimationFrame(function(){
|
|
1632
|
+
pending=false; last=Date.now();
|
|
1633
|
+
try { ezoicRepairBetweenWrapsToHost(); } catch(e) {}
|
|
1634
|
+
try { ezoicReconcileOrDropHostsByToken(scope); } catch(e) {}
|
|
1635
|
+
});
|
|
1549
1636
|
}
|
|
1550
1637
|
|
|
1551
|
-
function
|
|
1552
|
-
|
|
1553
|
-
killStickyIn(document);
|
|
1554
|
-
|
|
1555
|
-
// Throttled scroll pass (in case scripts reapply on scroll)
|
|
1556
|
-
var t = null;
|
|
1557
|
-
window.addEventListener('scroll', function(){
|
|
1558
|
-
if (t) return;
|
|
1559
|
-
t = setTimeout(function(){ t = null; killStickyIn(document); }, 120);
|
|
1560
|
-
}, { passive: true });
|
|
1638
|
+
window.addEventListener('scroll', function(){ schedule(document); }, { passive:true });
|
|
1639
|
+
window.addEventListener('resize', function(){ schedule(document); }, { passive:true });
|
|
1561
1640
|
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
if (m.type === 'attributes') {
|
|
1570
|
-
// style/class changed
|
|
1571
|
-
if (m.attributeName === 'style' || m.attributeName === 'class') {
|
|
1572
|
-
killStickyIn(m.target && m.target.closest ? (m.target.closest('.nodebb-ezoic-wrap') || document) : document);
|
|
1573
|
-
}
|
|
1574
|
-
} else if (m.addedNodes && m.addedNodes.length) {
|
|
1575
|
-
for (var j=0;j<m.addedNodes.length;j++){
|
|
1576
|
-
var n = m.addedNodes[j];
|
|
1577
|
-
if (!n || n.nodeType !== 1) continue;
|
|
1578
|
-
if (n.matches && (n.matches('.nodebb-ezoic-wrap') || n.matches('.ezads-sticky-intradiv'))) {
|
|
1579
|
-
killStickyIn(n);
|
|
1580
|
-
} else if (n.querySelector) {
|
|
1581
|
-
if (n.querySelector('.nodebb-ezoic-wrap, .ezads-sticky-intradiv')) killStickyIn(n);
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
} catch (e) {}
|
|
1587
|
-
});
|
|
1588
|
-
mo.observe(document.documentElement || document.body, {
|
|
1589
|
-
subtree: true,
|
|
1590
|
-
childList: true,
|
|
1591
|
-
attributes: true,
|
|
1592
|
-
attributeFilter: ['style','class']
|
|
1593
|
-
});
|
|
1594
|
-
}
|
|
1595
|
-
} catch (e) {}
|
|
1641
|
+
if (window.jQuery) {
|
|
1642
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
|
|
1643
|
+
setTimeout(function(){ schedule(document); }, 0);
|
|
1644
|
+
setTimeout(function(){ schedule(document); }, 220);
|
|
1645
|
+
setTimeout(function(){ schedule(document); }, 800);
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1596
1648
|
|
|
1597
|
-
|
|
1598
|
-
if (
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
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) || (m.removedNodes && m.removedNodes.length)) {
|
|
1655
|
+
schedule(m.target);
|
|
1656
|
+
break;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
document.querySelectorAll('ul,ol').forEach(function(list){
|
|
1661
|
+
try { mo.observe(list, {childList:true}); } catch(e) {}
|
|
1662
|
+
});
|
|
1606
1663
|
}
|
|
1607
|
-
}
|
|
1664
|
+
} catch(e){}
|
|
1608
1665
|
|
|
1609
|
-
|
|
1610
|
-
else install();
|
|
1666
|
+
setTimeout(function(){ schedule(document); }, 0);
|
|
1611
1667
|
})();
|
|
1612
|
-
// ===== /
|
|
1613
|
-
|
|
1668
|
+
// ===== /V14 =====
|
package/public/style.css
CHANGED
|
@@ -81,11 +81,7 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
/* =====
|
|
85
|
-
.nodebb-ezoic-
|
|
86
|
-
|
|
87
|
-
top: auto !important;
|
|
88
|
-
transform: none !important;
|
|
89
|
-
}
|
|
90
|
-
/* ===== /V13 ===== */
|
|
84
|
+
/* ===== V14 token host styles ===== */
|
|
85
|
+
li.nodebb-ezoic-host { list-style: none; width: 100%; }
|
|
86
|
+
/* ===== /V14 ===== */
|
|
91
87
|
|