nodebb-plugin-ezoic-infinite 1.6.48 → 1.6.50
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 +178 -242
- package/public/style.css +17 -0
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -63,6 +63,31 @@
|
|
|
63
63
|
// Production build: debug disabled
|
|
64
64
|
function dbg() {}
|
|
65
65
|
|
|
66
|
+
// Some ad providers (notably Google Safeframe) can defer creative rendering until a
|
|
67
|
+
// scroll/resize tick is observed. On SPA/infinite-scroll pages we can end up with
|
|
68
|
+
// valid iframes that stay visually empty until the user nudges the scroll.
|
|
69
|
+
// We gently "poke" observers by dispatching scroll/resize events (debounced).
|
|
70
|
+
let _ezPokeScheduled = false;
|
|
71
|
+
function scheduleViewportPoke(reason) {
|
|
72
|
+
if (_ezPokeScheduled) return;
|
|
73
|
+
_ezPokeScheduled = true;
|
|
74
|
+
try {
|
|
75
|
+
requestAnimationFrame(() => {
|
|
76
|
+
_ezPokeScheduled = false;
|
|
77
|
+
try {
|
|
78
|
+
window.dispatchEvent(new Event('scroll'));
|
|
79
|
+
window.dispatchEvent(new Event('resize'));
|
|
80
|
+
} catch (e) {}
|
|
81
|
+
try {
|
|
82
|
+
document.dispatchEvent(new Event('scroll'));
|
|
83
|
+
} catch (e) {}
|
|
84
|
+
dbg('viewportPoke', reason || '');
|
|
85
|
+
});
|
|
86
|
+
} catch (e) {
|
|
87
|
+
_ezPokeScheduled = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
66
91
|
// Ezoic (and some partner scripts) can be very noisy in console on SPA/Ajaxify setups.
|
|
67
92
|
// These warnings are not actionable for end-users and can flood the console.
|
|
68
93
|
// We selectively silence the known spam patterns while keeping other warnings intact.
|
|
@@ -636,8 +661,8 @@ function globalGapFixInit() {
|
|
|
636
661
|
|
|
637
662
|
// ---------------- insertion primitives ----------------
|
|
638
663
|
|
|
639
|
-
function buildWrap(id, kindClass, afterPos, createPlaceholder) {
|
|
640
|
-
const wrap = document.createElement('div');
|
|
664
|
+
function buildWrap(id, kindClass, afterPos, createPlaceholder, tagName) {
|
|
665
|
+
const wrap = document.createElement(tagName || 'div');
|
|
641
666
|
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
642
667
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
643
668
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
@@ -648,6 +673,15 @@ function globalGapFixInit() {
|
|
|
648
673
|
}
|
|
649
674
|
wrap.style.width = '100%';
|
|
650
675
|
|
|
676
|
+
// If we're inserted into a UL/OL list, use an LI wrapper to keep valid markup.
|
|
677
|
+
// Otherwise browsers can re-parent the DIV, which looks like items/ads “remontent”
|
|
678
|
+
// and can break topic list rendering.
|
|
679
|
+
if ((tagName || '').toLowerCase() === 'li') {
|
|
680
|
+
wrap.style.listStyle = 'none';
|
|
681
|
+
wrap.style.padding = '0';
|
|
682
|
+
wrap.style.margin = '0';
|
|
683
|
+
}
|
|
684
|
+
|
|
651
685
|
if (createPlaceholder) {
|
|
652
686
|
const ph = document.createElement('div');
|
|
653
687
|
ph.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
@@ -667,7 +701,10 @@ function globalGapFixInit() {
|
|
|
667
701
|
|
|
668
702
|
insertingIds.add(id);
|
|
669
703
|
try {
|
|
670
|
-
const
|
|
704
|
+
const parent = target.parentElement;
|
|
705
|
+
const parentTag = parent && parent.tagName ? parent.tagName.toUpperCase() : '';
|
|
706
|
+
const tagName = (parentTag === 'UL' || parentTag === 'OL') ? 'li' : 'div';
|
|
707
|
+
const wrap = buildWrap(id, kindClass, afterPos, !existingPh, tagName);
|
|
671
708
|
target.insertAdjacentElement('afterend', wrap);
|
|
672
709
|
|
|
673
710
|
// If placeholder exists elsewhere (including pool), move it into the wrapper.
|
|
@@ -679,6 +716,9 @@ function globalGapFixInit() {
|
|
|
679
716
|
} catch (e) {}
|
|
680
717
|
}
|
|
681
718
|
|
|
719
|
+
// Help creatives render promptly without requiring an actual user scroll.
|
|
720
|
+
scheduleViewportPoke(`afterInsert:${kindClass}:${id}`);
|
|
721
|
+
|
|
682
722
|
return wrap;
|
|
683
723
|
} finally {
|
|
684
724
|
insertingIds.delete(id);
|
|
@@ -1511,16 +1551,21 @@ function buildOrdinalMap(items) {
|
|
|
1511
1551
|
|
|
1512
1552
|
|
|
1513
1553
|
|
|
1514
|
-
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
|
|
1557
|
+
// ===== V17.8: No pile-up by design — keep "future" between slots pending/collapsed until their anchor topic exists =====
|
|
1515
1558
|
(function () {
|
|
1516
|
-
// Goal: keep ad injection intact. Only repair when we detect "pile-up" of between wraps.
|
|
1517
1559
|
var TOPIC_LI_SEL = 'li[component="category/topic"]';
|
|
1518
1560
|
var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1519
1561
|
var HOST_CLASS = 'nodebb-ezoic-host';
|
|
1562
|
+
var PENDING_CLASS = 'nodebb-ezoic-pending';
|
|
1520
1563
|
|
|
1564
|
+
var ul = null;
|
|
1565
|
+
var mo = null;
|
|
1521
1566
|
var scheduled = false;
|
|
1522
1567
|
var lastRun = 0;
|
|
1523
|
-
var COOLDOWN =
|
|
1568
|
+
var COOLDOWN = 80;
|
|
1524
1569
|
|
|
1525
1570
|
function getTopicList() {
|
|
1526
1571
|
try {
|
|
@@ -1530,29 +1575,41 @@ function buildOrdinalMap(items) {
|
|
|
1530
1575
|
} catch (e) { return null; }
|
|
1531
1576
|
}
|
|
1532
1577
|
|
|
1533
|
-
function
|
|
1534
|
-
|
|
1578
|
+
function ensureUL() { ul = ul || getTopicList(); return ul; }
|
|
1579
|
+
|
|
1580
|
+
function isHost(el) {
|
|
1581
|
+
return !!(el && el.nodeType === 1 && el.tagName === 'LI' && el.classList && el.classList.contains(HOST_CLASS));
|
|
1535
1582
|
}
|
|
1536
1583
|
|
|
1537
|
-
function
|
|
1538
|
-
try {
|
|
1539
|
-
|
|
1540
|
-
if (!(wrap.matches && wrap.matches(BETWEEN_WRAP_SEL))) return null;
|
|
1584
|
+
function isBetweenWrap(el) {
|
|
1585
|
+
try { return !!(el && el.nodeType === 1 && el.matches && el.matches(BETWEEN_WRAP_SEL)); } catch(e){ return false; }
|
|
1586
|
+
}
|
|
1541
1587
|
|
|
1588
|
+
function ensureHostForWrap(wrap, ulEl) {
|
|
1589
|
+
// If Ezoic inserts ul > div..., wrap it into a LI to keep a valid list structure (less NodeBB churn).
|
|
1590
|
+
try {
|
|
1591
|
+
if (!wrap || !isBetweenWrap(wrap)) return null;
|
|
1542
1592
|
var host = wrap.closest ? wrap.closest('li.' + HOST_CLASS) : null;
|
|
1543
1593
|
if (host) return host;
|
|
1544
1594
|
|
|
1545
|
-
|
|
1546
|
-
if (!
|
|
1595
|
+
ulEl = ulEl || (wrap.closest ? wrap.closest('ul,ol') : null);
|
|
1596
|
+
if (!ulEl || !(ulEl.tagName === 'UL' || ulEl.tagName === 'OL')) return null;
|
|
1547
1597
|
|
|
1548
|
-
|
|
1549
|
-
if (wrap.parentElement === ul) {
|
|
1598
|
+
if (wrap.parentElement === ulEl) {
|
|
1550
1599
|
host = document.createElement('li');
|
|
1551
1600
|
host.className = HOST_CLASS;
|
|
1552
1601
|
host.setAttribute('role', 'listitem');
|
|
1553
1602
|
host.style.listStyle = 'none';
|
|
1554
1603
|
host.style.width = '100%';
|
|
1555
|
-
|
|
1604
|
+
|
|
1605
|
+
try {
|
|
1606
|
+
var after = wrap.getAttribute('data-ezoic-after');
|
|
1607
|
+
if (after) host.setAttribute('data-ezoic-after', after);
|
|
1608
|
+
var pin = wrap.getAttribute('data-ezoic-pin');
|
|
1609
|
+
if (pin) host.setAttribute('data-ezoic-pin', pin);
|
|
1610
|
+
} catch (e) {}
|
|
1611
|
+
|
|
1612
|
+
ulEl.insertBefore(host, wrap);
|
|
1556
1613
|
host.appendChild(wrap);
|
|
1557
1614
|
try { wrap.style.width = '100%'; } catch (e) {}
|
|
1558
1615
|
return host;
|
|
@@ -1561,268 +1618,147 @@ function buildOrdinalMap(items) {
|
|
|
1561
1618
|
return null;
|
|
1562
1619
|
}
|
|
1563
1620
|
|
|
1564
|
-
function
|
|
1621
|
+
function getAfter(el) {
|
|
1565
1622
|
try {
|
|
1566
|
-
var
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1623
|
+
var v = null;
|
|
1624
|
+
if (el && el.getAttribute) v = el.getAttribute('data-ezoic-after');
|
|
1625
|
+
if (!v && el && el.querySelector) {
|
|
1626
|
+
var w = el.querySelector(BETWEEN_WRAP_SEL);
|
|
1627
|
+
if (w) v = w.getAttribute('data-ezoic-after');
|
|
1571
1628
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1629
|
+
var n = parseInt(v, 10);
|
|
1630
|
+
return isNaN(n) ? null : n;
|
|
1631
|
+
} catch (e) { return null; }
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
function getTopics(ulEl) {
|
|
1635
|
+
try { return ulEl ? ulEl.querySelectorAll(TOPIC_LI_SEL) : []; } catch(e){ return []; }
|
|
1574
1636
|
}
|
|
1575
1637
|
|
|
1576
|
-
function
|
|
1577
|
-
// Pile-up signature: 2+ between wraps/hosts adjacent with no topics between, often near top.
|
|
1638
|
+
function lastTopic(ulEl, topics) {
|
|
1578
1639
|
try {
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
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;
|
|
1640
|
+
topics = topics || getTopics(ulEl);
|
|
1641
|
+
return topics && topics.length ? topics[topics.length - 1] : null;
|
|
1642
|
+
} catch (e) { return null; }
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
function moveAfter(node, anchor) {
|
|
1646
|
+
try {
|
|
1647
|
+
if (!node || !anchor || !anchor.insertAdjacentElement) return;
|
|
1648
|
+
if (node.previousElementSibling === anchor) return;
|
|
1649
|
+
anchor.insertAdjacentElement('afterend', node);
|
|
1601
1650
|
} catch (e) {}
|
|
1602
|
-
return false;
|
|
1603
1651
|
}
|
|
1604
1652
|
|
|
1605
|
-
function
|
|
1653
|
+
function placeOrPend(host, ulEl, topics) {
|
|
1606
1654
|
try {
|
|
1607
|
-
if (!
|
|
1655
|
+
if (!host) return;
|
|
1656
|
+
ulEl = ulEl || ensureUL();
|
|
1657
|
+
if (!ulEl) return;
|
|
1608
1658
|
|
|
1609
|
-
|
|
1610
|
-
ul.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function(w){ ensureHostForWrap(w, ul); });
|
|
1659
|
+
topics = topics || getTopics(ulEl);
|
|
1611
1660
|
|
|
1612
|
-
|
|
1613
|
-
if (!
|
|
1661
|
+
var after = getAfter(host);
|
|
1662
|
+
if (!after) {
|
|
1663
|
+
var lt = lastTopic(ulEl, topics);
|
|
1664
|
+
if (lt) moveAfter(host, lt);
|
|
1665
|
+
host.classList && host.classList.remove(PENDING_CLASS);
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1614
1668
|
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1669
|
+
if (after > topics.length) {
|
|
1670
|
+
var lt2 = lastTopic(ulEl, topics);
|
|
1671
|
+
if (lt2) moveAfter(host, lt2);
|
|
1672
|
+
host.classList && host.classList.add(PENDING_CLASS);
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1621
1675
|
|
|
1622
|
-
|
|
1623
|
-
|
|
1676
|
+
var anchor = topics[after - 1];
|
|
1677
|
+
if (anchor) moveAfter(host, anchor);
|
|
1678
|
+
host.classList && host.classList.remove(PENDING_CLASS);
|
|
1679
|
+
} catch (e) {}
|
|
1680
|
+
}
|
|
1624
1681
|
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1682
|
+
function reconcile() {
|
|
1683
|
+
var ulEl = ensureUL();
|
|
1684
|
+
if (!ulEl) return;
|
|
1685
|
+
|
|
1686
|
+
var topics = getTopics(ulEl);
|
|
1687
|
+
|
|
1688
|
+
try {
|
|
1689
|
+
ulEl.querySelectorAll(':scope > ' + BETWEEN_WRAP_SEL).forEach(function (w) {
|
|
1690
|
+
var h = ensureHostForWrap(w, ulEl);
|
|
1691
|
+
if (h) placeOrPend(h, ulEl, topics);
|
|
1692
|
+
});
|
|
1693
|
+
|
|
1694
|
+
ulEl.querySelectorAll('li.' + HOST_CLASS).forEach(function (h) {
|
|
1695
|
+
placeOrPend(h, ulEl, topics);
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
ulEl.querySelectorAll(BETWEEN_WRAP_SEL).forEach(function (w) {
|
|
1699
|
+
var h = ensureHostForWrap(w, ulEl) || (w.closest ? w.closest('li.' + HOST_CLASS) : null);
|
|
1700
|
+
if (h) placeOrPend(h, ulEl, topics);
|
|
1629
1701
|
});
|
|
1630
1702
|
} catch (e) {}
|
|
1631
1703
|
}
|
|
1632
1704
|
|
|
1633
|
-
function
|
|
1705
|
+
function scheduleReconcile() {
|
|
1634
1706
|
var now = Date.now();
|
|
1635
|
-
if (now - lastRun < COOLDOWN) return;
|
|
1636
1707
|
if (scheduled) return;
|
|
1708
|
+
if (now - lastRun < COOLDOWN) return;
|
|
1637
1709
|
scheduled = true;
|
|
1710
|
+
lastRun = now;
|
|
1638
1711
|
requestAnimationFrame(function () {
|
|
1639
1712
|
scheduled = false;
|
|
1640
|
-
|
|
1641
|
-
try {
|
|
1642
|
-
var ul = getTopicList();
|
|
1643
|
-
if (!ul) return;
|
|
1644
|
-
redistribute(ul);
|
|
1645
|
-
} catch (e) {}
|
|
1713
|
+
reconcile();
|
|
1646
1714
|
});
|
|
1647
1715
|
}
|
|
1648
1716
|
|
|
1649
|
-
function
|
|
1650
|
-
|
|
1717
|
+
function initObserver() {
|
|
1718
|
+
var ulEl = ensureUL();
|
|
1719
|
+
if (!ulEl || mo) return;
|
|
1651
1720
|
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
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
|
-
};
|
|
1721
|
+
mo = new MutationObserver(function () {
|
|
1722
|
+
scheduleReconcile();
|
|
1723
|
+
});
|
|
1663
1724
|
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
try { mo2.disconnect(); } catch(e){}
|
|
1672
|
-
}
|
|
1673
|
-
});
|
|
1674
|
-
mo2.observe(document.documentElement || document.body, { childList: true, subtree: true });
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
-
} catch (e) {}
|
|
1725
|
+
mo.observe(ulEl, { childList: true, subtree: true });
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
function init() {
|
|
1729
|
+
scheduleViewportPoke('init');
|
|
1730
|
+
initObserver();
|
|
1731
|
+
scheduleReconcile();
|
|
1678
1732
|
|
|
1679
|
-
// NodeBB events: run after infinite scroll batches
|
|
1680
1733
|
if (window.jQuery) {
|
|
1681
1734
|
try {
|
|
1682
|
-
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function(){
|
|
1683
|
-
|
|
1684
|
-
|
|
1735
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function () {
|
|
1736
|
+
ul = null;
|
|
1737
|
+
try { if (mo) mo.disconnect(); } catch (e) {}
|
|
1738
|
+
mo = null;
|
|
1739
|
+
initObserver();
|
|
1740
|
+
scheduleReconcile();
|
|
1741
|
+
scheduleViewportPoke('ajaxify.end');
|
|
1742
|
+
setTimeout(scheduleReconcile, 120);
|
|
1743
|
+
setTimeout(scheduleReconcile, 500);
|
|
1685
1744
|
});
|
|
1686
1745
|
} catch (e) {}
|
|
1687
1746
|
}
|
|
1747
|
+
|
|
1748
|
+
var tries = 0;
|
|
1749
|
+
var t = setInterval(function () {
|
|
1750
|
+
tries++;
|
|
1751
|
+
if (ensureUL()) {
|
|
1752
|
+
clearInterval(t);
|
|
1753
|
+
initObserver();
|
|
1754
|
+
scheduleReconcile();
|
|
1755
|
+
}
|
|
1756
|
+
if (tries > 30) clearInterval(t);
|
|
1757
|
+
}, 200);
|
|
1688
1758
|
}
|
|
1689
1759
|
|
|
1690
1760
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1691
1761
|
else init();
|
|
1692
1762
|
})();
|
|
1693
|
-
// ===== /V17 =====
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
// ===== V17 empty-refresh (no move) =====
|
|
1698
|
-
// Goal: if a between-ad is visible but still has no ad iframe, gently poke Ezoic/GPT to render.
|
|
1699
|
-
(function () {
|
|
1700
|
-
var BETWEEN_WRAP_SEL = 'div.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1701
|
-
var seen = new WeakMap();
|
|
1702
|
-
var io = null;
|
|
1703
|
-
// NOTE: Calling ezstandalone.showAds() was triggering extra bidder requests
|
|
1704
|
-
// (and in your setup, surfacing ServiceWorker/CORS failures) which made
|
|
1705
|
-
// fills slower and created visible repositioning. We only do a tiny scroll
|
|
1706
|
-
// nudge here, because manual micro-scroll reliably triggers a fill.
|
|
1707
|
-
var microScrollCooldownUntil = 0;
|
|
1708
|
-
|
|
1709
|
-
function now() { return Date.now ? Date.now() : +new Date(); }
|
|
1710
|
-
|
|
1711
|
-
function getSlotId(wrap) {
|
|
1712
|
-
try {
|
|
1713
|
-
var gpt = wrap.querySelector('[id^="div-gpt-ad-"]');
|
|
1714
|
-
return gpt ? gpt.id : null;
|
|
1715
|
-
} catch (e) { return null; }
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
function hasAnyCreative(wrap) {
|
|
1719
|
-
try {
|
|
1720
|
-
// Any iframe (safeframe or normal) inside the wrap counts as rendered.
|
|
1721
|
-
if (wrap.querySelector('iframe')) return true;
|
|
1722
|
-
// Some formats use ins/amp/other nodes.
|
|
1723
|
-
if (wrap.querySelector('amp-ad, amp-embed, ins.adsbygoogle')) return true;
|
|
1724
|
-
} catch (e) {}
|
|
1725
|
-
return false;
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
function microScrollNudge() {
|
|
1729
|
-
var t = now();
|
|
1730
|
-
if (t < microScrollCooldownUntil) return;
|
|
1731
|
-
microScrollCooldownUntil = t + 1200;
|
|
1732
|
-
|
|
1733
|
-
// Prefer a real 1px scroll + restore: this matches the observed manual fix.
|
|
1734
|
-
try {
|
|
1735
|
-
var y = window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
1736
|
-
window.scrollTo(0, y + 1);
|
|
1737
|
-
(window.requestAnimationFrame || setTimeout)(function () {
|
|
1738
|
-
try { window.scrollTo(0, y); } catch (e) {}
|
|
1739
|
-
try { window.dispatchEvent(new Event('scroll')); } catch (e) {}
|
|
1740
|
-
}, 16);
|
|
1741
|
-
return;
|
|
1742
|
-
} catch (e) {}
|
|
1743
|
-
|
|
1744
|
-
// Fallback: nudge listeners.
|
|
1745
|
-
try { window.dispatchEvent(new Event('scroll')); } catch (e) {}
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
function scheduleCheck(wrap) {
|
|
1749
|
-
try {
|
|
1750
|
-
if (!wrap || wrap.nodeType !== 1) return;
|
|
1751
|
-
if (hasAnyCreative(wrap)) return;
|
|
1752
|
-
|
|
1753
|
-
var meta = seen.get(wrap);
|
|
1754
|
-
var t = now();
|
|
1755
|
-
if (!meta) {
|
|
1756
|
-
meta = { firstSeen: t, checks: 0 };
|
|
1757
|
-
seen.set(wrap, meta);
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
// Give the normal pipeline time to render.
|
|
1761
|
-
if (meta.checks === 0) {
|
|
1762
|
-
setTimeout(function () { scheduleCheck(wrap); }, 700);
|
|
1763
|
-
meta.checks++;
|
|
1764
|
-
return;
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
if (hasAnyCreative(wrap)) return;
|
|
1768
|
-
|
|
1769
|
-
// Still empty -> poke (micro-scroll only; no showAds/refresh calls).
|
|
1770
|
-
var id = getSlotId(wrap) || 'none';
|
|
1771
|
-
try { console.log('[EZ EMPTY]', id); } catch (e) {}
|
|
1772
|
-
microScrollNudge();
|
|
1773
|
-
|
|
1774
|
-
// Re-check once after the nudge, but do not loop.
|
|
1775
|
-
setTimeout(function () {
|
|
1776
|
-
if (!wrap.isConnected) return;
|
|
1777
|
-
if (hasAnyCreative(wrap)) return;
|
|
1778
|
-
// Still empty: leave it to the native pipeline; avoid repeated nudges.
|
|
1779
|
-
}, 900);
|
|
1780
|
-
} catch (e) {}
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
function ensureIO() {
|
|
1784
|
-
if (io) return;
|
|
1785
|
-
if (!('IntersectionObserver' in window)) return;
|
|
1763
|
+
// ===== /V17.8 =====
|
|
1786
1764
|
|
|
1787
|
-
io = new IntersectionObserver(function (entries) {
|
|
1788
|
-
for (var i = 0; i < entries.length; i++) {
|
|
1789
|
-
var e = entries[i];
|
|
1790
|
-
if (!e.isIntersecting) continue;
|
|
1791
|
-
scheduleCheck(e.target);
|
|
1792
|
-
}
|
|
1793
|
-
}, { root: null, rootMargin: '900px 0px 900px 0px', threshold: 0.01 });
|
|
1794
|
-
|
|
1795
|
-
// Observe existing wraps.
|
|
1796
|
-
try {
|
|
1797
|
-
var wraps = document.querySelectorAll(BETWEEN_WRAP_SEL);
|
|
1798
|
-
for (var j = 0; j < wraps.length; j++) io.observe(wraps[j]);
|
|
1799
|
-
} catch (e) {}
|
|
1800
|
-
|
|
1801
|
-
// Observe new wraps.
|
|
1802
|
-
try {
|
|
1803
|
-
var mo = new MutationObserver(function (muts) {
|
|
1804
|
-
for (var k = 0; k < muts.length; k++) {
|
|
1805
|
-
var m = muts[k];
|
|
1806
|
-
if (!m.addedNodes) continue;
|
|
1807
|
-
for (var n = 0; n < m.addedNodes.length; n++) {
|
|
1808
|
-
var node = m.addedNodes[n];
|
|
1809
|
-
if (!node || node.nodeType !== 1) continue;
|
|
1810
|
-
if (node.matches && node.matches(BETWEEN_WRAP_SEL)) {
|
|
1811
|
-
io.observe(node);
|
|
1812
|
-
} else if (node.querySelectorAll) {
|
|
1813
|
-
var inner = node.querySelectorAll(BETWEEN_WRAP_SEL);
|
|
1814
|
-
for (var q = 0; q < inner.length; q++) io.observe(inner[q]);
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
});
|
|
1819
|
-
mo.observe(document.body, { childList: true, subtree: true });
|
|
1820
|
-
} catch (e) {}
|
|
1821
|
-
}
|
|
1822
|
-
|
|
1823
|
-
if (document.readyState === 'loading') {
|
|
1824
|
-
document.addEventListener('DOMContentLoaded', ensureIO, { once: true });
|
|
1825
|
-
} else {
|
|
1826
|
-
ensureIO();
|
|
1827
|
-
}
|
|
1828
|
-
})();
|
package/public/style.css
CHANGED
|
@@ -86,3 +86,20 @@ li.nodebb-ezoic-host { list-style: none; width: 100%; display: block; }
|
|
|
86
86
|
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; display: block; }
|
|
87
87
|
/* ===== /V17 ===== */
|
|
88
88
|
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
/* ===== V17.8 pending slots (prevents pile-up top/bottom) ===== */
|
|
92
|
+
li.nodebb-ezoic-host { list-style:none; width:100%; display:block; }
|
|
93
|
+
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width:100%; display:block; }
|
|
94
|
+
|
|
95
|
+
/* slots whose anchor topic isn't loaded yet */
|
|
96
|
+
li.nodebb-ezoic-host.nodebb-ezoic-pending{
|
|
97
|
+
max-height: 0 !important;
|
|
98
|
+
margin: 0 !important;
|
|
99
|
+
padding: 0 !important;
|
|
100
|
+
overflow: hidden !important;
|
|
101
|
+
opacity: 0 !important;
|
|
102
|
+
pointer-events: none !important;
|
|
103
|
+
}
|
|
104
|
+
/* ===== /V17.8 ===== */
|
|
105
|
+
|