nodebb-plugin-ezoic-infinite 1.5.49 → 1.5.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 +68 -4
- package/public/style.css +6 -0
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -96,6 +96,48 @@
|
|
|
96
96
|
|
|
97
97
|
const insertingIds = new Set();
|
|
98
98
|
|
|
99
|
+
// ---------- lightweight "fade-in" for ad iframes ----------
|
|
100
|
+
// This reduces the perception of "flashing" when a slot appears empty then fills.
|
|
101
|
+
// We avoid scroll listeners and only react to DOM insertions.
|
|
102
|
+
const _faded = new WeakSet();
|
|
103
|
+
function _fadeInIframe(iframe) {
|
|
104
|
+
try {
|
|
105
|
+
if (!iframe || _faded.has(iframe)) return;
|
|
106
|
+
_faded.add(iframe);
|
|
107
|
+
iframe.style.opacity = '0';
|
|
108
|
+
iframe.style.transition = 'opacity 140ms ease';
|
|
109
|
+
// Next frame: show
|
|
110
|
+
requestAnimationFrame(() => {
|
|
111
|
+
try { iframe.style.opacity = '1'; } catch (e) {}
|
|
112
|
+
});
|
|
113
|
+
} catch (e) {}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const _adFillObserver = new MutationObserver((muts) => {
|
|
117
|
+
try {
|
|
118
|
+
for (const m of muts) {
|
|
119
|
+
if (m.addedNodes && m.addedNodes.length) {
|
|
120
|
+
for (const n of m.addedNodes) {
|
|
121
|
+
if (!n || n.nodeType !== 1) continue;
|
|
122
|
+
if (n.tagName === 'IFRAME') {
|
|
123
|
+
const w = n.closest && n.closest(`.${WRAP_CLASS}`);
|
|
124
|
+
if (w) _fadeInIframe(n);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
// If a subtree is added, look for iframes inside ad wrappers only.
|
|
128
|
+
const ifs = n.querySelectorAll ? n.querySelectorAll(`.${WRAP_CLASS} iframe`) : null;
|
|
129
|
+
if (ifs && ifs.length) ifs.forEach(_fadeInIframe);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
_adFillObserver.observe(document.documentElement, { subtree: true, childList: true });
|
|
138
|
+
} catch (e) {}
|
|
139
|
+
|
|
140
|
+
|
|
99
141
|
|
|
100
142
|
function markEmptyWrapper(id) {
|
|
101
143
|
try {
|
|
@@ -205,6 +247,18 @@
|
|
|
205
247
|
['dns-prefetch', 'https://g.ezoic.net', false],
|
|
206
248
|
['preconnect', 'https://go.ezoic.net', true],
|
|
207
249
|
['dns-prefetch', 'https://go.ezoic.net', false],
|
|
250
|
+
|
|
251
|
+
// Google ad stack (helps Safeframe/GPT warm up)
|
|
252
|
+
['preconnect', 'https://pagead2.googlesyndication.com', true],
|
|
253
|
+
['dns-prefetch', 'https://pagead2.googlesyndication.com', false],
|
|
254
|
+
['preconnect', 'https://securepubads.g.doubleclick.net', true],
|
|
255
|
+
['dns-prefetch', 'https://securepubads.g.doubleclick.net', false],
|
|
256
|
+
['preconnect', 'https://tpc.googlesyndication.com', true],
|
|
257
|
+
['dns-prefetch', 'https://tpc.googlesyndication.com', false],
|
|
258
|
+
['preconnect', 'https://googleads.g.doubleclick.net', true],
|
|
259
|
+
['dns-prefetch', 'https://googleads.g.doubleclick.net', false],
|
|
260
|
+
['preconnect', 'https://static.doubleclick.net', true],
|
|
261
|
+
['dns-prefetch', 'https://static.doubleclick.net', false],
|
|
208
262
|
];
|
|
209
263
|
for (const [rel, href, cors] of links) {
|
|
210
264
|
const key = `${rel}|${href}`;
|
|
@@ -385,6 +439,7 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
385
439
|
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
386
440
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
387
441
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
442
|
+
wrap.setAttribute('data-ezoic-ts', String(Date.now()));
|
|
388
443
|
wrap.style.width = '100%';
|
|
389
444
|
|
|
390
445
|
const ph = document.createElement('div');
|
|
@@ -441,14 +496,23 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
441
496
|
const wraps = Array.from(document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`));
|
|
442
497
|
if (!wraps.length) return false;
|
|
443
498
|
|
|
444
|
-
|
|
499
|
+
const now = Date.now();
|
|
500
|
+
|
|
501
|
+
// Only recycle wraps that are clearly out of view AND old enough.
|
|
502
|
+
// This avoids the "unstable" feeling where ads disappear when the user scrolls back a bit.
|
|
503
|
+
const MIN_AGE_MS = 20000; // 20s
|
|
504
|
+
const OFFSCREEN_PX = -5000; // far above viewport
|
|
505
|
+
|
|
445
506
|
let victim = null;
|
|
446
507
|
for (const w of wraps) {
|
|
447
508
|
const r = w.getBoundingClientRect();
|
|
448
|
-
|
|
509
|
+
const ts = parseInt(w.getAttribute('data-ezoic-ts') || '0', 10);
|
|
510
|
+
const ageOk = !ts || (now - ts) >= MIN_AGE_MS;
|
|
511
|
+
if (ageOk && r.bottom < OFFSCREEN_PX) { victim = w; break; }
|
|
449
512
|
}
|
|
450
|
-
|
|
451
|
-
|
|
513
|
+
|
|
514
|
+
// If nothing is eligible, do not recycle. We'll simply skip inserting new ads this run.
|
|
515
|
+
if (!victim) return false;
|
|
452
516
|
|
|
453
517
|
// Unobserve placeholder if still observed
|
|
454
518
|
try {
|
package/public/style.css
CHANGED