nodebb-plugin-ezoic-infinite 1.5.41 → 1.5.43
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 +119 -2
- package/public/style.css +23 -0
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -136,6 +136,7 @@ function mutationHasRelevantAddedNodes(mutations) {
|
|
|
136
136
|
// observers / schedulers
|
|
137
137
|
domObs: null,
|
|
138
138
|
tightenObs: null,
|
|
139
|
+
fillObs: null,
|
|
139
140
|
io: null,
|
|
140
141
|
runQueued: false,
|
|
141
142
|
|
|
@@ -542,7 +543,7 @@ function pruneOrphanWraps(kindClass, items) {
|
|
|
542
543
|
}
|
|
543
544
|
function buildWrap(id, kindClass, afterPos) {
|
|
544
545
|
const wrap = document.createElement('div');
|
|
545
|
-
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
546
|
+
wrap.className = `${WRAP_CLASS} ${kindClass} ez-pending`;
|
|
546
547
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
547
548
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
548
549
|
wrap.style.width = '100%';
|
|
@@ -557,6 +558,103 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
557
558
|
return wrap;
|
|
558
559
|
}
|
|
559
560
|
|
|
561
|
+
// ---------- Fill detection & collapse handling (lightweight) ----------
|
|
562
|
+
// If ad fill is slow, showing a big empty slot is visually jarring. We keep
|
|
563
|
+
// our injected wrapper collapsed (ez-pending) until a creative is present,
|
|
564
|
+
// then mark it ez-ready.
|
|
565
|
+
|
|
566
|
+
function wrapHasFilledCreative(wrap) {
|
|
567
|
+
try {
|
|
568
|
+
if (!wrap || !wrap.isConnected) return false;
|
|
569
|
+
// Safeframe container (most common)
|
|
570
|
+
const c = wrap.querySelector('div[id$="__container__"]');
|
|
571
|
+
if (c && c.offsetHeight > 10) return true;
|
|
572
|
+
// Any iframe with non-trivial height
|
|
573
|
+
const f = wrap.querySelector('iframe');
|
|
574
|
+
if (!f) return false;
|
|
575
|
+
if (f.getAttribute('data-load-complete') === 'true') return true;
|
|
576
|
+
if (f.offsetHeight > 10) return true;
|
|
577
|
+
return false;
|
|
578
|
+
} catch (e) {}
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function markWrapFilledIfNeeded(wrap) {
|
|
583
|
+
try {
|
|
584
|
+
if (!wrap || !wrap.isConnected) return;
|
|
585
|
+
if (!wrap.classList || !wrap.classList.contains(WRAP_CLASS)) return;
|
|
586
|
+
// Only our injected wrappers are DIVs with data-ezoic-wrapid.
|
|
587
|
+
if (wrap.tagName !== 'DIV') return;
|
|
588
|
+
if (!wrap.getAttribute('data-ezoic-wrapid')) return;
|
|
589
|
+
|
|
590
|
+
if (wrapHasFilledCreative(wrap)) {
|
|
591
|
+
wrap.classList.remove('ez-pending');
|
|
592
|
+
wrap.classList.add('ez-ready');
|
|
593
|
+
}
|
|
594
|
+
} catch (e) {}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function ensureFillObserver() {
|
|
598
|
+
if (state.fillObs) return;
|
|
599
|
+
|
|
600
|
+
let raf = 0;
|
|
601
|
+
const pending = new Set();
|
|
602
|
+
const schedule = (wrap) => {
|
|
603
|
+
if (!wrap) return;
|
|
604
|
+
pending.add(wrap);
|
|
605
|
+
if (raf) return;
|
|
606
|
+
raf = requestAnimationFrame(() => {
|
|
607
|
+
raf = 0;
|
|
608
|
+
for (const w of pending) markWrapFilledIfNeeded(w);
|
|
609
|
+
pending.clear();
|
|
610
|
+
});
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
const closestWrap = (node) => {
|
|
614
|
+
try {
|
|
615
|
+
if (!node || node.nodeType !== 1) return null;
|
|
616
|
+
const el = /** @type {Element} */ (node);
|
|
617
|
+
if (el.tagName === 'DIV' && el.classList && el.classList.contains(WRAP_CLASS) && el.getAttribute('data-ezoic-wrapid')) return el;
|
|
618
|
+
if (el.closest) return el.closest(`div.${WRAP_CLASS}[data-ezoic-wrapid]`);
|
|
619
|
+
} catch (e) {}
|
|
620
|
+
return null;
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
state.fillObs = new MutationObserver((mutations) => {
|
|
624
|
+
try {
|
|
625
|
+
for (const m of mutations) {
|
|
626
|
+
if (m.type === 'attributes') {
|
|
627
|
+
const w = closestWrap(m.target);
|
|
628
|
+
if (w) schedule(w);
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
if (!m.addedNodes || !m.addedNodes.length) continue;
|
|
632
|
+
for (const n of m.addedNodes) {
|
|
633
|
+
const w = closestWrap(n);
|
|
634
|
+
if (w) schedule(w);
|
|
635
|
+
if (n && n.nodeType === 1 && n.querySelectorAll) {
|
|
636
|
+
n.querySelectorAll(`div.${WRAP_CLASS}[data-ezoic-wrapid]`).forEach(schedule);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
} catch (e) {}
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
try {
|
|
644
|
+
state.fillObs.observe(document.documentElement, {
|
|
645
|
+
subtree: true,
|
|
646
|
+
childList: true,
|
|
647
|
+
attributes: true,
|
|
648
|
+
attributeFilter: ['style', 'class', 'data-load-complete', 'height', 'src'],
|
|
649
|
+
});
|
|
650
|
+
} catch (e) {}
|
|
651
|
+
|
|
652
|
+
// Kick once for already-present wraps
|
|
653
|
+
try {
|
|
654
|
+
document.querySelectorAll(`div.${WRAP_CLASS}[data-ezoic-wrapid]`).forEach(markWrapFilledIfNeeded);
|
|
655
|
+
} catch (e) {}
|
|
656
|
+
}
|
|
657
|
+
|
|
560
658
|
function findWrap(kindClass, afterPos) {
|
|
561
659
|
return document.querySelector(`.${WRAP_CLASS}.${kindClass}[data-ezoic-after="${afterPos}"]`);
|
|
562
660
|
}
|
|
@@ -679,7 +777,15 @@ function startShow(id) {
|
|
|
679
777
|
try {
|
|
680
778
|
if (isBlocked()) return;
|
|
681
779
|
|
|
682
|
-
|
|
780
|
+
// Ensure placeholder is armed (arm-on-load). On some NodeBB transitions the
|
|
781
|
+
// wrapper may exist but the placeholder id is not yet assigned.
|
|
782
|
+
const wrap = findWrapById(id);
|
|
783
|
+
if (wrap && wrap.isConnected) {
|
|
784
|
+
try { armPlaceholder(wrap, id); } catch (e) {}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const domId = `${PLACEHOLDER_PREFIX}${id}`;
|
|
788
|
+
const ph = document.getElementById(domId);
|
|
683
789
|
if (!ph || !ph.isConnected) return;
|
|
684
790
|
|
|
685
791
|
const now2 = Date.now();
|
|
@@ -691,6 +797,15 @@ function startShow(id) {
|
|
|
691
797
|
const ez = window.ezstandalone;
|
|
692
798
|
|
|
693
799
|
const doShow = () => {
|
|
800
|
+
// Re-check right before showing: the placeholder can disappear between
|
|
801
|
+
// scheduling and execution (ajaxify/infinite scroll DOM churn).
|
|
802
|
+
const phNow = document.getElementById(domId);
|
|
803
|
+
if (!phNow || !phNow.isConnected) {
|
|
804
|
+
try { clearTimeout(hardTimer); } catch (e) {}
|
|
805
|
+
release();
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
|
|
694
809
|
try {
|
|
695
810
|
if (state.usedOnce && state.usedOnce.has(id)) {
|
|
696
811
|
safeDestroyById(id);
|
|
@@ -1022,6 +1137,7 @@ function startShow(id) {
|
|
|
1022
1137
|
warmUpNetwork();
|
|
1023
1138
|
patchShowAds();
|
|
1024
1139
|
ensureTightenObserver();
|
|
1140
|
+
ensureFillObserver();
|
|
1025
1141
|
ensurePreloadObserver();
|
|
1026
1142
|
ensureDomObserver();
|
|
1027
1143
|
|
|
@@ -1045,6 +1161,7 @@ function startShow(id) {
|
|
|
1045
1161
|
warmUpNetwork();
|
|
1046
1162
|
patchShowAds();
|
|
1047
1163
|
ensureTightenObserver();
|
|
1164
|
+
ensureFillObserver();
|
|
1048
1165
|
ensurePreloadObserver();
|
|
1049
1166
|
ensureDomObserver();
|
|
1050
1167
|
bindNodeBB();
|
package/public/style.css
CHANGED
|
@@ -9,3 +9,26 @@
|
|
|
9
9
|
clear: both;
|
|
10
10
|
position: relative;
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
UX: collapse injected wrapper slots until the creative is actually filled.
|
|
15
|
+
This avoids showing a large empty placeholder when ad fill is slow.
|
|
16
|
+
Only applies to our injected wrapper DIVs (not Ezoic's internal SPANs).
|
|
17
|
+
*/
|
|
18
|
+
div.ezoic-ad.ez-pending {
|
|
19
|
+
height: 1px !important;
|
|
20
|
+
min-height: 1px !important;
|
|
21
|
+
overflow: hidden !important;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
div.ezoic-ad.ez-ready {
|
|
25
|
+
height: auto !important;
|
|
26
|
+
overflow: visible !important;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Remove baseline gaps under iframes inside Ezoic creatives */
|
|
30
|
+
span.ezoic-ad iframe,
|
|
31
|
+
span.ezoic-ad div[id$="__container__"] iframe {
|
|
32
|
+
display: block !important;
|
|
33
|
+
vertical-align: top !important;
|
|
34
|
+
}
|