nodebb-plugin-ezoic-infinite 1.5.50 → 1.5.52
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 +105 -117
- package/public/style.css +0 -6
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -96,48 +96,6 @@
|
|
|
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
|
-
|
|
141
99
|
|
|
142
100
|
function markEmptyWrapper(id) {
|
|
143
101
|
try {
|
|
@@ -247,18 +205,6 @@
|
|
|
247
205
|
['dns-prefetch', 'https://g.ezoic.net', false],
|
|
248
206
|
['preconnect', 'https://go.ezoic.net', true],
|
|
249
207
|
['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],
|
|
262
208
|
];
|
|
263
209
|
for (const [rel, href, cors] of links) {
|
|
264
210
|
const key = `${rel}|${href}`;
|
|
@@ -274,46 +220,76 @@
|
|
|
274
220
|
}
|
|
275
221
|
|
|
276
222
|
// Patch showAds to avoid warnings when a placeholder disappears (infinite scroll, ajaxify)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
223
|
+
|
|
224
|
+
function patchShowAds() {
|
|
225
|
+
// Robustly filter ez.showAds calls so Ezoic won't spam "placeholder does not exist"
|
|
226
|
+
// during ajaxify navigation or when some placeholders are not present on the current view.
|
|
227
|
+
try {
|
|
228
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
229
|
+
const ez = window.ezstandalone;
|
|
230
|
+
ez.cmd = ez.cmd || [];
|
|
231
|
+
|
|
232
|
+
const wrap = (orig) => {
|
|
233
|
+
if (typeof orig !== 'function') return orig;
|
|
234
|
+
if (orig.__nodebbWrapped) return orig;
|
|
235
|
+
|
|
236
|
+
const wrapped = function (...args) {
|
|
237
|
+
if (isBlocked()) return;
|
|
238
|
+
|
|
239
|
+
let ids = [];
|
|
240
|
+
if (args.length === 1 && Array.isArray(args[0])) ids = args[0];
|
|
241
|
+
else ids = args;
|
|
242
|
+
|
|
243
|
+
const filtered = [];
|
|
244
|
+
const seen = new Set();
|
|
245
|
+
for (const v of ids) {
|
|
246
|
+
const id = parseInt(v, 10);
|
|
247
|
+
if (!Number.isFinite(id) || id <= 0 || seen.has(id)) continue;
|
|
248
|
+
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
249
|
+
if (!ph || !ph.isConnected) continue;
|
|
250
|
+
seen.add(id);
|
|
251
|
+
filtered.push(id);
|
|
252
|
+
}
|
|
253
|
+
if (!filtered.length) return;
|
|
254
|
+
|
|
255
|
+
// Call original with the same shape most implementations accept
|
|
256
|
+
try { return orig.call(ez, filtered.length === 1 ? filtered[0] : filtered); } catch (e) {}
|
|
257
|
+
};
|
|
258
|
+
wrapped.__nodebbWrapped = true;
|
|
259
|
+
wrapped.__nodebbOrig = orig;
|
|
260
|
+
return wrapped;
|
|
306
261
|
};
|
|
307
262
|
|
|
308
|
-
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
313
|
-
window.ezstandalone.cmd.push(applyPatch);
|
|
314
|
-
} catch (e) {}
|
|
263
|
+
// If showAds already exists, wrap it now.
|
|
264
|
+
if (typeof ez.showAds === 'function') {
|
|
265
|
+
ez.showAds = wrap(ez.showAds);
|
|
266
|
+
return;
|
|
315
267
|
}
|
|
316
|
-
|
|
268
|
+
|
|
269
|
+
// Otherwise, define a setter hook so whenever Ezoic defines showAds, we wrap it.
|
|
270
|
+
if (!ez.__nodebbShowAdsHooked) {
|
|
271
|
+
ez.__nodebbShowAdsHooked = true;
|
|
272
|
+
let _showAds = null;
|
|
273
|
+
|
|
274
|
+
Object.defineProperty(ez, 'showAds', {
|
|
275
|
+
configurable: true,
|
|
276
|
+
enumerable: true,
|
|
277
|
+
get() { return _showAds; },
|
|
278
|
+
set(fn) { _showAds = wrap(fn); }
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// In case Ezoic sets showAds from cmd queue
|
|
282
|
+
ez.cmd.push(() => {
|
|
283
|
+
try {
|
|
284
|
+
if (typeof ez.showAds === 'function' && !ez.showAds.__nodebbWrapped) {
|
|
285
|
+
ez.showAds = wrap(ez.showAds);
|
|
286
|
+
}
|
|
287
|
+
} catch (e) {}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
} catch (e) {}
|
|
291
|
+
}
|
|
292
|
+
|
|
317
293
|
|
|
318
294
|
const RECYCLE_COOLDOWN_MS = 1500;
|
|
319
295
|
|
|
@@ -439,7 +415,6 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
439
415
|
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
440
416
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
441
417
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
442
|
-
wrap.setAttribute('data-ezoic-ts', String(Date.now()));
|
|
443
418
|
wrap.style.width = '100%';
|
|
444
419
|
|
|
445
420
|
const ph = document.createElement('div');
|
|
@@ -496,23 +471,14 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
496
471
|
const wraps = Array.from(document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`));
|
|
497
472
|
if (!wraps.length) return false;
|
|
498
473
|
|
|
499
|
-
|
|
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
|
-
|
|
474
|
+
// Prefer a wrap far above the viewport
|
|
506
475
|
let victim = null;
|
|
507
476
|
for (const w of wraps) {
|
|
508
477
|
const r = w.getBoundingClientRect();
|
|
509
|
-
|
|
510
|
-
const ageOk = !ts || (now - ts) >= MIN_AGE_MS;
|
|
511
|
-
if (ageOk && r.bottom < OFFSCREEN_PX) { victim = w; break; }
|
|
478
|
+
if (r.bottom < -2000) { victim = w; break; }
|
|
512
479
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
if (!victim) return false;
|
|
480
|
+
// Otherwise remove the earliest one in the document
|
|
481
|
+
if (!victim) victim = wraps[0];
|
|
516
482
|
|
|
517
483
|
// Unobserve placeholder if still observed
|
|
518
484
|
try {
|
|
@@ -559,6 +525,7 @@ function drainQueue() {
|
|
|
559
525
|
function startShow(id) {
|
|
560
526
|
if (!id || isBlocked()) return;
|
|
561
527
|
|
|
528
|
+
// Reserve one inflight slot for this request
|
|
562
529
|
state.inflight++;
|
|
563
530
|
let released = false;
|
|
564
531
|
const release = () => {
|
|
@@ -570,16 +537,22 @@ function startShow(id) {
|
|
|
570
537
|
|
|
571
538
|
const hardTimer = setTimeout(release, 6500);
|
|
572
539
|
|
|
540
|
+
const earlyExit = () => {
|
|
541
|
+
try { clearTimeout(hardTimer); } catch (e) {}
|
|
542
|
+
release();
|
|
543
|
+
};
|
|
544
|
+
|
|
573
545
|
requestAnimationFrame(() => {
|
|
574
546
|
try {
|
|
575
|
-
if (isBlocked()) return;
|
|
547
|
+
if (isBlocked()) return earlyExit();
|
|
576
548
|
|
|
577
|
-
const
|
|
578
|
-
|
|
549
|
+
const domId = `${PLACEHOLDER_PREFIX}${id}`;
|
|
550
|
+
const ph = document.getElementById(domId);
|
|
551
|
+
if (!ph || !ph.isConnected) return earlyExit();
|
|
579
552
|
|
|
580
553
|
const now2 = Date.now();
|
|
581
554
|
const last2 = state.lastShowById.get(id) || 0;
|
|
582
|
-
if (now2 - last2 < 900) return;
|
|
555
|
+
if (now2 - last2 < 900) return earlyExit();
|
|
583
556
|
state.lastShowById.set(id, now2);
|
|
584
557
|
|
|
585
558
|
window.ezstandalone = window.ezstandalone || {};
|
|
@@ -587,16 +560,31 @@ function startShow(id) {
|
|
|
587
560
|
|
|
588
561
|
const doShow = () => {
|
|
589
562
|
try {
|
|
563
|
+
if (isBlocked()) return earlyExit();
|
|
564
|
+
|
|
565
|
+
// Re-check at execution time (cmd queue may delay)
|
|
566
|
+
const ph2 = document.getElementById(domId);
|
|
567
|
+
if (!ph2 || !ph2.isConnected) return earlyExit();
|
|
568
|
+
|
|
569
|
+
// If this id was used before in this pageview, destroy first (Ezoic best practice on recycle)
|
|
590
570
|
if (state.usedOnce && state.usedOnce.has(id) && typeof ez.destroyPlaceholders === 'function') {
|
|
591
|
-
try { ez.destroyPlaceholders(
|
|
571
|
+
try { ez.destroyPlaceholders([domId]); } catch (e) {}
|
|
592
572
|
}
|
|
593
|
-
} catch (e) {}
|
|
594
573
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
574
|
+
// Call showAds (patched to filter missing placeholders)
|
|
575
|
+
try { ez.showAds(id); } catch (e) {}
|
|
576
|
+
|
|
577
|
+
try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
|
|
578
|
+
try { markEmptyWrapper(id); } catch (e) {} // harmless; if removed later, it no-ops
|
|
598
579
|
|
|
599
|
-
|
|
580
|
+
// Release shortly after triggering, so we can pipeline the next ones
|
|
581
|
+
setTimeout(() => {
|
|
582
|
+
try { clearTimeout(hardTimer); } catch (e) {}
|
|
583
|
+
release();
|
|
584
|
+
}, 650);
|
|
585
|
+
} catch (e) {
|
|
586
|
+
earlyExit();
|
|
587
|
+
}
|
|
600
588
|
};
|
|
601
589
|
|
|
602
590
|
if (Array.isArray(ez.cmd)) {
|
|
@@ -604,8 +592,8 @@ function startShow(id) {
|
|
|
604
592
|
} else {
|
|
605
593
|
doShow();
|
|
606
594
|
}
|
|
607
|
-
}
|
|
608
|
-
|
|
595
|
+
} catch (e) {
|
|
596
|
+
earlyExit();
|
|
609
597
|
}
|
|
610
598
|
});
|
|
611
599
|
}
|
package/public/style.css
CHANGED