nodebb-plugin-ezoic-infinite 1.6.44 → 1.6.46
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 +103 -127
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1646,129 +1646,6 @@ function buildOrdinalMap(items) {
|
|
|
1646
1646
|
});
|
|
1647
1647
|
}
|
|
1648
1648
|
|
|
1649
|
-
// --- Empty ad "poke" helper ---
|
|
1650
|
-
// Some GPT / safeframe renders can stay visually blank until a *real* scroll happens.
|
|
1651
|
-
// We still avoid showAds()/refresh() (can hurt fill), but we mimic the user "micro scroll".
|
|
1652
|
-
var ezEmptySeen = Object.create(null);
|
|
1653
|
-
var ezEmptyPokeTimer = Object.create(null);
|
|
1654
|
-
var ezEmptyAttempts = Object.create(null);
|
|
1655
|
-
|
|
1656
|
-
function inViewportLoose(el) {
|
|
1657
|
-
try {
|
|
1658
|
-
if (!el || !el.getBoundingClientRect) return false;
|
|
1659
|
-
var r = el.getBoundingClientRect();
|
|
1660
|
-
var vh = window.innerHeight || document.documentElement.clientHeight || 0;
|
|
1661
|
-
// within 1.5 viewports from top/bottom
|
|
1662
|
-
return r.bottom > -vh * 0.5 && r.top < vh * 1.5;
|
|
1663
|
-
} catch (e) {
|
|
1664
|
-
return false;
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
function hasAnyIframe(divEl) {
|
|
1669
|
-
try {
|
|
1670
|
-
if (!divEl) return false;
|
|
1671
|
-
return !!divEl.querySelector('iframe');
|
|
1672
|
-
} catch (e) {
|
|
1673
|
-
return false;
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
function microScrollNudge() {
|
|
1678
|
-
try {
|
|
1679
|
-
// Only if the page can actually scroll
|
|
1680
|
-
var maxY = (document.documentElement && document.documentElement.scrollHeight) ? document.documentElement.scrollHeight : 0;
|
|
1681
|
-
if (maxY <= (window.innerHeight || 0) + 2) return;
|
|
1682
|
-
|
|
1683
|
-
var x = window.scrollX || 0;
|
|
1684
|
-
var y = window.scrollY || 0;
|
|
1685
|
-
// Nudge by 1px and restore on the next frame.
|
|
1686
|
-
window.scrollTo(x, y + 1);
|
|
1687
|
-
requestAnimationFrame(function(){
|
|
1688
|
-
try { window.scrollTo(x, y); } catch (e) {}
|
|
1689
|
-
});
|
|
1690
|
-
} catch (e) {}
|
|
1691
|
-
}
|
|
1692
|
-
|
|
1693
|
-
function pokeReflowForDivId(divId) {
|
|
1694
|
-
try {
|
|
1695
|
-
var el = document.getElementById(divId);
|
|
1696
|
-
if (!el) return;
|
|
1697
|
-
if (!inViewportLoose(el)) return;
|
|
1698
|
-
|
|
1699
|
-
// If the slot already has an iframe, don't keep poking.
|
|
1700
|
-
if (hasAnyIframe(el)) return;
|
|
1701
|
-
|
|
1702
|
-
// Force a tiny reflow/paint on the closest wrapper
|
|
1703
|
-
var wrap = el.closest ? (el.closest('.ez-fixed-wrap') || el.parentElement) : el.parentElement;
|
|
1704
|
-
if (!wrap) wrap = el;
|
|
1705
|
-
|
|
1706
|
-
// 1) Force layout
|
|
1707
|
-
try { void wrap.offsetHeight; } catch (e) {}
|
|
1708
|
-
|
|
1709
|
-
// 2) Toggle visibility for one frame (forces paint)
|
|
1710
|
-
var prevVis = wrap.style.visibility;
|
|
1711
|
-
wrap.style.visibility = 'hidden';
|
|
1712
|
-
requestAnimationFrame(function(){
|
|
1713
|
-
wrap.style.visibility = prevVis || '';
|
|
1714
|
-
// 3) Fire synthetic events (cheap)
|
|
1715
|
-
try { window.dispatchEvent(new Event('scroll')); } catch (e) {}
|
|
1716
|
-
try { window.dispatchEvent(new Event('resize')); } catch (e) {}
|
|
1717
|
-
|
|
1718
|
-
// 4) If the slot was "empty", a real scroll often makes it fill immediately.
|
|
1719
|
-
microScrollNudge();
|
|
1720
|
-
});
|
|
1721
|
-
} catch (e) {}
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
function schedulePoke(divId) {
|
|
1725
|
-
try {
|
|
1726
|
-
if (!divId) return;
|
|
1727
|
-
var now = Date.now();
|
|
1728
|
-
// Cooldown per slot to avoid loops
|
|
1729
|
-
if (ezEmptySeen[divId] && now - ezEmptySeen[divId] < 15000) return;
|
|
1730
|
-
// New window: reset attempts
|
|
1731
|
-
ezEmptyAttempts[divId] = 0;
|
|
1732
|
-
ezEmptySeen[divId] = now;
|
|
1733
|
-
|
|
1734
|
-
// Cap attempts per slot in a short window
|
|
1735
|
-
ezEmptyAttempts[divId] = (ezEmptyAttempts[divId] || 0) + 1;
|
|
1736
|
-
if (ezEmptyAttempts[divId] > 3) return;
|
|
1737
|
-
|
|
1738
|
-
if (ezEmptyPokeTimer[divId]) return;
|
|
1739
|
-
// Multi-poke: fast + a couple of retries (covers slow GPT / lazy paint)
|
|
1740
|
-
ezEmptyPokeTimer[divId] = setTimeout(function(){
|
|
1741
|
-
ezEmptyPokeTimer[divId] = null;
|
|
1742
|
-
pokeReflowForDivId(divId);
|
|
1743
|
-
setTimeout(function(){ pokeReflowForDivId(divId); }, 350);
|
|
1744
|
-
setTimeout(function(){ pokeReflowForDivId(divId); }, 1200);
|
|
1745
|
-
}, 80);
|
|
1746
|
-
} catch (e) {}
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
function initGptEmptyPoke() {
|
|
1750
|
-
try {
|
|
1751
|
-
if (!window.googletag || !googletag.cmd || !googletag.pubads) return;
|
|
1752
|
-
googletag.cmd.push(function(){
|
|
1753
|
-
try {
|
|
1754
|
-
var pub = googletag.pubads();
|
|
1755
|
-
if (!pub || !pub.addEventListener) return;
|
|
1756
|
-
pub.addEventListener('slotRenderEnded', function(e){
|
|
1757
|
-
try {
|
|
1758
|
-
if (!e) return;
|
|
1759
|
-
// divId is the GPT container id for the slot (e.g. div-gpt-ad-...)
|
|
1760
|
-
var divId = e.slot && e.slot.getSlotElementId ? e.slot.getSlotElementId() : (e.slotElementId || '');
|
|
1761
|
-
if (e.isEmpty) {
|
|
1762
|
-
if (divId) console.log('[EZ EMPTY]', divId);
|
|
1763
|
-
schedulePoke(divId);
|
|
1764
|
-
}
|
|
1765
|
-
} catch (err) {}
|
|
1766
|
-
});
|
|
1767
|
-
} catch (err2) {}
|
|
1768
|
-
});
|
|
1769
|
-
} catch (e) {}
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
1649
|
function init() {
|
|
1773
1650
|
schedule('init');
|
|
1774
1651
|
|
|
@@ -1808,10 +1685,6 @@ function buildOrdinalMap(items) {
|
|
|
1808
1685
|
});
|
|
1809
1686
|
} catch (e) {}
|
|
1810
1687
|
}
|
|
1811
|
-
|
|
1812
|
-
// Listen for GPT empty renders and apply a cheap reflow "poke".
|
|
1813
|
-
// This is intentionally non-invasive (no showAds/refresh) to preserve fill.
|
|
1814
|
-
initGptEmptyPoke();
|
|
1815
1688
|
}
|
|
1816
1689
|
|
|
1817
1690
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
@@ -1819,3 +1692,106 @@ function buildOrdinalMap(items) {
|
|
|
1819
1692
|
})();
|
|
1820
1693
|
// ===== /V17 =====
|
|
1821
1694
|
|
|
1695
|
+
|
|
1696
|
+
// ===== V17.17 TOPFIX (minimal, non-invasive) =====
|
|
1697
|
+
// Goal: prevent "between" ad wrappers from being inserted at the very top of the topic list
|
|
1698
|
+
// (before any real topic LI), without moving or re-wrapping existing GPT containers.
|
|
1699
|
+
// We only:
|
|
1700
|
+
// 1) If a between wrapper is inserted as the first child and a topic LI exists, move its host
|
|
1701
|
+
// to after the first topic LI (one-time).
|
|
1702
|
+
// 2) If no topic LI exists yet, mark it with data-ezoic-after="1" and leave it in place.
|
|
1703
|
+
// This avoids the heavier "pending pool" logic that can reduce fill.
|
|
1704
|
+
(function(){
|
|
1705
|
+
'use strict';
|
|
1706
|
+
|
|
1707
|
+
var UL_SEL = 'ul.topics, ul.topic-list, ul.category-list';
|
|
1708
|
+
var BETWEEN_WRAP_SEL = '.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1709
|
+
var HOST_CLASS = 'ezoic-ad-host';
|
|
1710
|
+
|
|
1711
|
+
function getUl(){
|
|
1712
|
+
return document.querySelector(UL_SEL);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
function firstTopicLi(ul){
|
|
1716
|
+
if (!ul) return null;
|
|
1717
|
+
// Consider any LI that is not our ad host as a topic LI
|
|
1718
|
+
var lis = ul.querySelectorAll(':scope > li');
|
|
1719
|
+
for (var i=0;i<lis.length;i++){
|
|
1720
|
+
if (!lis[i].classList.contains(HOST_CLASS)) return lis[i];
|
|
1721
|
+
}
|
|
1722
|
+
return null;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
function hostFromWrap(wrap){
|
|
1726
|
+
if (!wrap) return null;
|
|
1727
|
+
// expected structure: li.ezoic-ad-host > (something) > span.nodebb-ezoic-wrap
|
|
1728
|
+
var host = wrap.closest('li');
|
|
1729
|
+
if (host && host.classList && host.classList.contains(HOST_CLASS)) return host;
|
|
1730
|
+
return null;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
function handleWrapInserted(ul, wrap){
|
|
1734
|
+
try {
|
|
1735
|
+
if (!ul || !wrap) return;
|
|
1736
|
+
var host = hostFromWrap(wrap);
|
|
1737
|
+
if (!host) return;
|
|
1738
|
+
|
|
1739
|
+
// Only act when the host is at the very top (before any topic LI)
|
|
1740
|
+
if (host.parentElement !== ul) return;
|
|
1741
|
+
if (ul.firstElementChild !== host) return;
|
|
1742
|
+
|
|
1743
|
+
var anchor = firstTopicLi(ul);
|
|
1744
|
+
if (anchor) {
|
|
1745
|
+
// One-time move to just after first topic LI
|
|
1746
|
+
anchor.insertAdjacentElement('afterend', host);
|
|
1747
|
+
} else {
|
|
1748
|
+
// No anchor yet: mark to discourage top placement; do not move later
|
|
1749
|
+
try { wrap.setAttribute('data-ezoic-after', '1'); } catch(e) {}
|
|
1750
|
+
}
|
|
1751
|
+
} catch (e) {}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
function scanExisting(ul){
|
|
1755
|
+
try {
|
|
1756
|
+
var wraps = ul ? ul.querySelectorAll(BETWEEN_WRAP_SEL) : [];
|
|
1757
|
+
wraps.forEach(function(w){ handleWrapInserted(ul, w); });
|
|
1758
|
+
} catch(e) {}
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
function init(){
|
|
1762
|
+
var ul = getUl();
|
|
1763
|
+
if (!ul) return;
|
|
1764
|
+
|
|
1765
|
+
scanExisting(ul);
|
|
1766
|
+
|
|
1767
|
+
if (typeof MutationObserver === 'undefined') return;
|
|
1768
|
+
var mo = new MutationObserver(function(muts){
|
|
1769
|
+
for (var i=0;i<muts.length;i++){
|
|
1770
|
+
var m = muts[i];
|
|
1771
|
+
if (!m.addedNodes) continue;
|
|
1772
|
+
for (var j=0;j<m.addedNodes.length;j++){
|
|
1773
|
+
var n = m.addedNodes[j];
|
|
1774
|
+
if (!n || n.nodeType !== 1) continue;
|
|
1775
|
+
|
|
1776
|
+
// direct wrap
|
|
1777
|
+
if (n.matches && n.matches(BETWEEN_WRAP_SEL)) {
|
|
1778
|
+
handleWrapInserted(ul, n);
|
|
1779
|
+
continue;
|
|
1780
|
+
}
|
|
1781
|
+
// wrap somewhere inside
|
|
1782
|
+
var inner = n.querySelector ? n.querySelector(BETWEEN_WRAP_SEL) : null;
|
|
1783
|
+
if (inner) handleWrapInserted(ul, inner);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
});
|
|
1787
|
+
|
|
1788
|
+
mo.observe(ul, { childList: true, subtree: true });
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
if (document.readyState === 'loading') {
|
|
1792
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
1793
|
+
} else {
|
|
1794
|
+
init();
|
|
1795
|
+
}
|
|
1796
|
+
})();
|
|
1797
|
+
// ===== /V17.17 TOPFIX =====
|