nodebb-plugin-ezoic-infinite 1.7.92 → 1.7.93
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 +121 -104
- package/public/style.css +14 -0
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -79,8 +79,10 @@
|
|
|
79
79
|
|
|
80
80
|
const MIN_PRUNE_AGE_MS = 8_000; // délai de grâce avant pruning (stabilisation DOM)
|
|
81
81
|
const MAX_INSERTS_RUN = 6; // max insertions par appel runCore
|
|
82
|
-
const MAX_INFLIGHT =
|
|
83
|
-
const SHOW_THROTTLE_MS =
|
|
82
|
+
const MAX_INFLIGHT = 2; // max showAds() simultanés (réduit le churn/unused)
|
|
83
|
+
const SHOW_THROTTLE_MS = 1200; // anti-spam showAds() par id
|
|
84
|
+
const UNUSED_COOLDOWN_MS = 15_000; // cooldown après tentative sans fill (unused probable)
|
|
85
|
+
const PENDING_TIMEOUT_MS = 12_000; // au-delà, tentative considérée obsolète
|
|
84
86
|
const BURST_COOLDOWN_MS = 200; // délai min entre deux déclenchements de burst
|
|
85
87
|
|
|
86
88
|
// Marges IO larges et fixes — observer créé une seule fois au boot
|
|
@@ -123,17 +125,19 @@
|
|
|
123
125
|
domObs: null,
|
|
124
126
|
mutGuard: 0, // >0 : mutations DOM en cours (MutationObserver ignoré)
|
|
125
127
|
inflight: 0, // showAds() en cours
|
|
126
|
-
pending: [], // ids en attente de slot inflight
|
|
128
|
+
pending: [], // ids en attente de slot inflight (triés par priorité)
|
|
127
129
|
pendingSet: new Set(),
|
|
130
|
+
pendingShows: new Set(), // ids avec tentative show en cours
|
|
131
|
+
cooldownUntil:new Map(), // id -> ts de fin de cooldown (unused probable)
|
|
132
|
+
fillObsById: new Map(), // id -> MutationObserver placeholder fill
|
|
128
133
|
wrapByKey: new Map(), // anchorKey → wrap DOM node
|
|
129
|
-
fillObsById: new Map(), // id -> MutationObserver
|
|
130
134
|
runQueued: false,
|
|
131
135
|
burstActive: false,
|
|
132
136
|
burstDeadline: 0,
|
|
133
137
|
burstCount: 0,
|
|
134
138
|
lastBurstTs: 0,
|
|
135
139
|
lastScrollY: 0,
|
|
136
|
-
scrollDir: 1,
|
|
140
|
+
scrollDir: 1,
|
|
137
141
|
};
|
|
138
142
|
|
|
139
143
|
let blockedUntil = 0;
|
|
@@ -143,34 +147,70 @@
|
|
|
143
147
|
const isMobile = () => { try { return window.innerWidth < 768; } catch (_) { return false; } };
|
|
144
148
|
const normBool = v => v === true || v === 'true' || v === 1 || v === '1' || v === 'on';
|
|
145
149
|
const isFilled = n => !!(n?.querySelector?.('iframe, ins, img, video, [data-google-container-id]'));
|
|
150
|
+
const isCooling = id => (S.cooldownUntil.get(id) || 0) > ts();
|
|
146
151
|
|
|
152
|
+
function viewportDistance(el) {
|
|
153
|
+
try {
|
|
154
|
+
const r = el?.getBoundingClientRect?.();
|
|
155
|
+
if (!r) return Infinity;
|
|
156
|
+
const vh = window.innerHeight || 800;
|
|
157
|
+
if (r.bottom >= 0 && r.top <= vh) return 0;
|
|
158
|
+
if (r.top > vh) return Math.max(0, r.top - vh);
|
|
159
|
+
return Math.max(0, -r.bottom);
|
|
160
|
+
} catch (_) { return Infinity; }
|
|
161
|
+
}
|
|
147
162
|
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
|
|
163
|
+
function pendingEnqueue(id) {
|
|
164
|
+
if (S.pendingSet.has(id)) return;
|
|
165
|
+
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
166
|
+
const prio = viewportDistance(ph?.closest?.(`.${WRAP_CLASS}`) || ph);
|
|
167
|
+
const item = { id, prio, at: ts() };
|
|
168
|
+
let i = S.pending.length;
|
|
169
|
+
while (i > 0 && S.pending[i - 1].prio > prio) i--;
|
|
170
|
+
S.pending.splice(i, 0, item);
|
|
171
|
+
S.pendingSet.add(id);
|
|
151
172
|
}
|
|
152
173
|
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
174
|
+
function markUnusedProbable(id) {
|
|
175
|
+
if (!id) return;
|
|
176
|
+
S.pendingShows.delete(id);
|
|
177
|
+
S.cooldownUntil.set(id, ts() + UNUSED_COOLDOWN_MS);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function clearCooldownIfFilled(id) {
|
|
181
|
+
if (!id) return;
|
|
182
|
+
S.cooldownUntil.delete(id);
|
|
183
|
+
S.pendingShows.delete(id);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function unobserveFill(id) {
|
|
187
|
+
const obs = S.fillObsById.get(id);
|
|
188
|
+
if (obs) { try { obs.disconnect(); } catch (_) {} }
|
|
189
|
+
S.fillObsById.delete(id);
|
|
161
190
|
}
|
|
162
191
|
|
|
163
192
|
function watchPlaceholderFill(id) {
|
|
164
193
|
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
165
|
-
if (!ph?.isConnected
|
|
194
|
+
if (!ph?.isConnected) return;
|
|
195
|
+
unobserveFill(id);
|
|
196
|
+
const onMut = () => {
|
|
197
|
+
try {
|
|
198
|
+
if (!ph.isConnected) return unobserveFill(id);
|
|
199
|
+
if (!isFilled(ph)) return;
|
|
200
|
+
clearCooldownIfFilled(id);
|
|
201
|
+
const wrap = ph.closest?.(`.${WRAP_CLASS}`);
|
|
202
|
+
if (wrap) wrap.classList.remove('is-empty');
|
|
203
|
+
} catch (_) {}
|
|
204
|
+
};
|
|
166
205
|
try {
|
|
167
|
-
const obs = new MutationObserver(
|
|
206
|
+
const obs = new MutationObserver(onMut);
|
|
168
207
|
obs.observe(ph, { childList: true, subtree: true, attributes: true });
|
|
169
208
|
S.fillObsById.set(id, obs);
|
|
170
|
-
uncollapseIfFilled(ph);
|
|
171
209
|
} catch (_) {}
|
|
210
|
+
onMut();
|
|
172
211
|
}
|
|
173
212
|
|
|
213
|
+
|
|
174
214
|
function mutate(fn) {
|
|
175
215
|
S.mutGuard++;
|
|
176
216
|
try { fn(); } finally { S.mutGuard--; }
|
|
@@ -312,23 +352,18 @@
|
|
|
312
352
|
}
|
|
313
353
|
|
|
314
354
|
function sweepDeadWraps() {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const id = parseInt(
|
|
319
|
-
if (Number.isFinite(id)
|
|
355
|
+
for (const [key, w] of Array.from(S.wrapByKey.entries())) {
|
|
356
|
+
if (w?.isConnected) continue;
|
|
357
|
+
S.wrapByKey.delete(key);
|
|
358
|
+
const id = parseInt(w?.getAttribute?.(A_WRAPID), 10);
|
|
359
|
+
if (Number.isFinite(id)) {
|
|
320
360
|
S.mountedIds.delete(id);
|
|
321
|
-
unwatchPlaceholderFillById(id);
|
|
322
|
-
S.lastShow.delete(id);
|
|
323
361
|
S.pendingSet.delete(id);
|
|
362
|
+
S.pending = S.pending.filter(it => (it?.id ?? it) !== id);
|
|
363
|
+
S.pendingShows.delete(id);
|
|
364
|
+
unobserveFill(id);
|
|
324
365
|
}
|
|
325
|
-
S.wrapByKey.delete(key);
|
|
326
|
-
freed++;
|
|
327
|
-
}
|
|
328
|
-
if (freed && S.pending.length) {
|
|
329
|
-
S.pending = S.pending.filter(id => !S.mountedIds.has(id));
|
|
330
366
|
}
|
|
331
|
-
return freed;
|
|
332
367
|
}
|
|
333
368
|
|
|
334
369
|
// ── Pool ───────────────────────────────────────────────────────────────────
|
|
@@ -363,57 +398,39 @@
|
|
|
363
398
|
typeof ez?.displayMore !== 'function') return null;
|
|
364
399
|
|
|
365
400
|
const vh = window.innerHeight || 800;
|
|
366
|
-
const targetRect = targetEl?.getBoundingClientRect?.()
|
|
367
|
-
|
|
368
|
-
// Recyclage bidirectionnel :
|
|
369
|
-
// - scroll vers le bas -> recycle préférentiellement des wraps loin au-dessus
|
|
370
|
-
// - scroll vers le haut -> recycle préférentiellement des wraps loin en-dessous
|
|
371
|
-
// Fallback sur l'autre côté si aucun candidat.
|
|
372
|
-
const aboveThreshold = -vh; // wrap entièrement/suffisamment au-dessus
|
|
373
|
-
const belowThreshold = vh * 2; // wrap loin sous le viewport
|
|
401
|
+
const targetRect = targetEl?.getBoundingClientRect?.();
|
|
402
|
+
const dir = S.scrollDir >= 0 ? 1 : -1;
|
|
374
403
|
|
|
375
|
-
let
|
|
376
|
-
let
|
|
377
|
-
let belowEmpty = null, belowEmptyTop = -Infinity;
|
|
378
|
-
let belowFilled = null, belowFilledTop = -Infinity;
|
|
404
|
+
let best = null, bestScore = Infinity;
|
|
405
|
+
let fallback = null, fallbackScore = Infinity;
|
|
379
406
|
|
|
380
407
|
for (const wrap of S.wrapByKey.values()) {
|
|
381
|
-
if (!wrap?.classList?.contains?.(klass)) continue;
|
|
382
408
|
try {
|
|
383
|
-
if (!wrap?.isConnected) continue;
|
|
409
|
+
if (!wrap?.isConnected || !wrap.classList?.contains(WRAP_CLASS) || !wrap.classList.contains(klass)) continue;
|
|
384
410
|
const rect = wrap.getBoundingClientRect();
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if (
|
|
396
|
-
|
|
397
|
-
if (rect.top > belowEmptyTop) { belowEmptyTop = rect.top; belowEmpty = wrap; }
|
|
398
|
-
} else {
|
|
399
|
-
if (rect.top > belowFilledTop) { belowFilledTop = rect.top; belowFilled = wrap; }
|
|
400
|
-
}
|
|
411
|
+
const farAbove = rect.bottom < -vh;
|
|
412
|
+
const farBelow = rect.top > (window.innerHeight || 800) + vh;
|
|
413
|
+
if (!farAbove && !farBelow) continue;
|
|
414
|
+
|
|
415
|
+
const preferredSide = dir > 0 ? farAbove : farBelow;
|
|
416
|
+
const dist = targetRect ? Math.abs(rect.top - targetRect.top) : Math.abs(rect.top);
|
|
417
|
+
const score = (isFilled(wrap) ? 10000000 : 0) + dist;
|
|
418
|
+
|
|
419
|
+
if (preferredSide) {
|
|
420
|
+
if (score < bestScore) { best = wrap; bestScore = score; }
|
|
421
|
+
} else if (score < fallbackScore) {
|
|
422
|
+
fallback = wrap; fallbackScore = score;
|
|
401
423
|
}
|
|
402
424
|
} catch (_) {}
|
|
403
425
|
}
|
|
404
426
|
|
|
405
|
-
|
|
406
|
-
const pickAbove = () => aboveEmpty ?? aboveFilled;
|
|
407
|
-
const pickBelow = () => belowEmpty ?? belowFilled;
|
|
408
|
-
const best = preferBelow ? (pickBelow() ?? pickAbove()) : (pickAbove() ?? pickBelow());
|
|
427
|
+
best = best || fallback;
|
|
409
428
|
if (!best) return null;
|
|
410
429
|
const id = parseInt(best.getAttribute(A_WRAPID), 10);
|
|
411
430
|
if (!Number.isFinite(id)) return null;
|
|
412
431
|
|
|
413
432
|
const oldKey = best.getAttribute(A_ANCHOR);
|
|
414
|
-
|
|
415
|
-
// parasite si le nœud était encore dans la zone IO_MARGIN.
|
|
416
|
-
try { const ph = best.querySelector(`#${PH_PREFIX}${id}`); if (ph) S.io?.unobserve(ph); } catch (_) {}
|
|
433
|
+
try { const ph = best.querySelector(`#${PH_PREFIX}${id}`); if (ph) { S.io?.unobserve(ph); unobserveFill(id); } } catch (_) {}
|
|
417
434
|
mutate(() => {
|
|
418
435
|
best.setAttribute(A_ANCHOR, newKey);
|
|
419
436
|
best.setAttribute(A_CREATED, String(ts()));
|
|
@@ -426,10 +443,9 @@
|
|
|
426
443
|
if (oldKey && S.wrapByKey.get(oldKey) === best) S.wrapByKey.delete(oldKey);
|
|
427
444
|
S.wrapByKey.set(newKey, best);
|
|
428
445
|
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
const
|
|
432
|
-
const doDisplay = () => { try { ez.displayMore([id]); } catch (_) {} };
|
|
446
|
+
const doDestroy = () => { try { ez.destroyPlaceholders([id]); } catch (_) {} setTimeout(doDefine, 300); };
|
|
447
|
+
const doDefine = () => { try { ez.define([id]); } catch (_) {} setTimeout(doDisplay, 300); };
|
|
448
|
+
const doDisplay = () => { try { ez.displayMore([id]); } catch (_) {} };
|
|
433
449
|
try { (typeof ez.cmd?.push === 'function') ? ez.cmd.push(doDestroy) : doDestroy(); } catch (_) {}
|
|
434
450
|
|
|
435
451
|
return { id, wrap: best };
|
|
@@ -461,7 +477,6 @@
|
|
|
461
477
|
mutate(() => el.insertAdjacentElement('afterend', w));
|
|
462
478
|
S.mountedIds.add(id);
|
|
463
479
|
S.wrapByKey.set(key, w);
|
|
464
|
-
watchPlaceholderFill(id);
|
|
465
480
|
return w;
|
|
466
481
|
}
|
|
467
482
|
|
|
@@ -470,7 +485,8 @@
|
|
|
470
485
|
const ph = w.querySelector(`[id^="${PH_PREFIX}"]`);
|
|
471
486
|
if (ph instanceof Element) S.io?.unobserve(ph);
|
|
472
487
|
const id = parseInt(w.getAttribute(A_WRAPID), 10);
|
|
473
|
-
if (Number.isFinite(id))
|
|
488
|
+
if (Number.isFinite(id)) unobserveFill(id);
|
|
489
|
+
if (Number.isFinite(id)) S.mountedIds.delete(id);
|
|
474
490
|
const key = w.getAttribute(A_ANCHOR);
|
|
475
491
|
if (key && S.wrapByKey.get(key) === w) S.wrapByKey.delete(key);
|
|
476
492
|
w.remove();
|
|
@@ -533,6 +549,7 @@
|
|
|
533
549
|
function injectBetween(klass, items, interval, showFirst, poolKey) {
|
|
534
550
|
if (!items.length) return 0;
|
|
535
551
|
let inserted = 0;
|
|
552
|
+
sweepDeadWraps();
|
|
536
553
|
|
|
537
554
|
for (const el of items) {
|
|
538
555
|
if (inserted >= MAX_INSERTS_RUN) break;
|
|
@@ -553,6 +570,7 @@
|
|
|
553
570
|
} else {
|
|
554
571
|
const recycled = recycleAndMove(klass, el, key);
|
|
555
572
|
if (!recycled) break;
|
|
573
|
+
observePh(recycled.id);
|
|
556
574
|
inserted++;
|
|
557
575
|
}
|
|
558
576
|
}
|
|
@@ -578,49 +596,54 @@
|
|
|
578
596
|
|
|
579
597
|
function observePh(id) {
|
|
580
598
|
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
581
|
-
if (
|
|
582
|
-
|
|
583
|
-
|
|
599
|
+
if (ph?.isConnected) {
|
|
600
|
+
try { getIO()?.observe(ph); } catch (_) {}
|
|
601
|
+
watchPlaceholderFill(id);
|
|
602
|
+
}
|
|
584
603
|
}
|
|
585
604
|
|
|
586
605
|
function enqueueShow(id) {
|
|
587
606
|
if (!id || isBlocked()) return;
|
|
607
|
+
if (isCooling(id) || S.pendingShows.has(id)) return;
|
|
588
608
|
if (ts() - (S.lastShow.get(id) ?? 0) < SHOW_THROTTLE_MS) return;
|
|
589
|
-
if (S.inflight >= MAX_INFLIGHT) {
|
|
590
|
-
if (!S.pendingSet.has(id)) { S.pending.push(id); S.pendingSet.add(id); }
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
609
|
+
if (S.inflight >= MAX_INFLIGHT) { pendingEnqueue(id); return; }
|
|
593
610
|
startShow(id);
|
|
594
611
|
}
|
|
595
612
|
|
|
596
613
|
function drainQueue() {
|
|
597
614
|
if (isBlocked()) return;
|
|
598
615
|
while (S.inflight < MAX_INFLIGHT && S.pending.length) {
|
|
599
|
-
const
|
|
616
|
+
const item = S.pending.shift();
|
|
617
|
+
const id = item?.id ?? item;
|
|
600
618
|
S.pendingSet.delete(id);
|
|
619
|
+
if (!id || isCooling(id) || S.pendingShows.has(id)) continue;
|
|
620
|
+
if (!document.getElementById(`${PH_PREFIX}${id}`)?.isConnected) continue;
|
|
621
|
+
if (ts() - (S.lastShow.get(id) ?? 0) < SHOW_THROTTLE_MS) continue;
|
|
601
622
|
startShow(id);
|
|
602
623
|
}
|
|
603
624
|
}
|
|
604
625
|
|
|
605
626
|
function startShow(id) {
|
|
606
627
|
if (!id || isBlocked()) return;
|
|
628
|
+
if (S.pendingShows.has(id) || isCooling(id)) return;
|
|
629
|
+
S.pendingShows.add(id);
|
|
607
630
|
S.inflight++;
|
|
608
631
|
let done = false;
|
|
609
632
|
const release = () => {
|
|
610
633
|
if (done) return;
|
|
611
634
|
done = true;
|
|
635
|
+
S.pendingShows.delete(id);
|
|
612
636
|
S.inflight = Math.max(0, S.inflight - 1);
|
|
613
637
|
drainQueue();
|
|
614
638
|
};
|
|
615
|
-
const timer = setTimeout(release,
|
|
639
|
+
const timer = setTimeout(() => { markUnusedProbable(id); release(); }, PENDING_TIMEOUT_MS);
|
|
616
640
|
|
|
617
641
|
requestAnimationFrame(() => {
|
|
618
642
|
try {
|
|
619
643
|
if (isBlocked()) { clearTimeout(timer); return release(); }
|
|
620
644
|
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
621
|
-
if (!ph?.isConnected
|
|
622
|
-
|
|
623
|
-
try { ph.closest?.(`.${WRAP_CLASS}`)?.classList.remove('is-empty'); } catch (_) {}
|
|
645
|
+
if (!ph?.isConnected) { clearTimeout(timer); markUnusedProbable(id); return release(); }
|
|
646
|
+
if (isFilled(ph)) { clearTimeout(timer); clearCooldownIfFilled(id); return release(); }
|
|
624
647
|
|
|
625
648
|
const t = ts();
|
|
626
649
|
if (t - (S.lastShow.get(id) ?? 0) < SHOW_THROTTLE_MS) { clearTimeout(timer); return release(); }
|
|
@@ -765,11 +788,12 @@
|
|
|
765
788
|
S.mountedIds.clear();
|
|
766
789
|
S.lastShow.clear();
|
|
767
790
|
S.wrapByKey.clear();
|
|
768
|
-
for (const obs of S.fillObsById.values()) { try { obs.disconnect(); } catch (_) {} }
|
|
769
|
-
S.fillObsById.clear();
|
|
770
791
|
S.inflight = 0;
|
|
771
792
|
S.pending = [];
|
|
772
793
|
S.pendingSet.clear();
|
|
794
|
+
S.pendingShows.clear();
|
|
795
|
+
S.cooldownUntil.clear();
|
|
796
|
+
for (const id of Array.from(S.fillObsById.keys())) unobserveFill(id);
|
|
773
797
|
S.burstActive = false;
|
|
774
798
|
S.runQueued = false;
|
|
775
799
|
}
|
|
@@ -781,16 +805,12 @@
|
|
|
781
805
|
const allSel = [SEL.post, SEL.topic, SEL.category];
|
|
782
806
|
S.domObs = new MutationObserver(muts => {
|
|
783
807
|
if (S.mutGuard > 0 || isBlocked()) return;
|
|
808
|
+
let sawWrapRemoval = false;
|
|
784
809
|
for (const m of muts) {
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
if (n.
|
|
788
|
-
try {
|
|
789
|
-
if (n.matches?.(`.${WRAP_CLASS}`) || n.querySelector?.(`.${WRAP_CLASS}`)) { sawWrapRemoval = true; }
|
|
790
|
-
} catch (_) {}
|
|
810
|
+
for (const n of (m.removedNodes || [])) {
|
|
811
|
+
if (n?.nodeType !== 1) continue;
|
|
812
|
+
try { if ((n.classList && n.classList.contains(WRAP_CLASS)) || n.querySelector?.(`.${WRAP_CLASS}`)) sawWrapRemoval = true; } catch (_) {}
|
|
791
813
|
}
|
|
792
|
-
if (sawWrapRemoval) sweepDeadWraps();
|
|
793
|
-
|
|
794
814
|
for (const n of m.addedNodes) {
|
|
795
815
|
if (n.nodeType !== 1) continue;
|
|
796
816
|
// matches() d'abord (O(1)), querySelector() seulement si nécessaire
|
|
@@ -800,6 +820,7 @@
|
|
|
800
820
|
}
|
|
801
821
|
}
|
|
802
822
|
}
|
|
823
|
+
if (sawWrapRemoval) sweepDeadWraps();
|
|
803
824
|
});
|
|
804
825
|
try { S.domObs.observe(document.body, { childList: true, subtree: true }); } catch (_) {}
|
|
805
826
|
}
|
|
@@ -901,14 +922,11 @@
|
|
|
901
922
|
function bindScroll() {
|
|
902
923
|
let ticking = false;
|
|
903
924
|
window.addEventListener('scroll', () => {
|
|
904
|
-
try {
|
|
905
|
-
const y = window.scrollY || window.pageYOffset || 0;
|
|
906
|
-
const dy = y - (S.lastScrollY || 0);
|
|
907
|
-
if (Math.abs(dy) > 2) S.scrollDir = dy > 0 ? 1 : -1;
|
|
908
|
-
S.lastScrollY = y;
|
|
909
|
-
} catch (_) {}
|
|
910
925
|
if (ticking) return;
|
|
911
926
|
ticking = true;
|
|
927
|
+
const y = window.scrollY || window.pageYOffset || 0;
|
|
928
|
+
S.scrollDir = y >= (S.lastScrollY || 0) ? 1 : -1;
|
|
929
|
+
S.lastScrollY = y;
|
|
912
930
|
requestAnimationFrame(() => { ticking = false; requestBurst(); });
|
|
913
931
|
}, { passive: true });
|
|
914
932
|
}
|
|
@@ -916,7 +934,6 @@
|
|
|
916
934
|
// ── Boot ───────────────────────────────────────────────────────────────────
|
|
917
935
|
|
|
918
936
|
S.pageKey = pageKey();
|
|
919
|
-
try { S.lastScrollY = window.scrollY || window.pageYOffset || 0; } catch (_) { S.lastScrollY = 0; }
|
|
920
937
|
muteConsole();
|
|
921
938
|
ensureTcfLocator();
|
|
922
939
|
warmNetwork();
|
package/public/style.css
CHANGED
|
@@ -56,6 +56,20 @@
|
|
|
56
56
|
top: auto !important;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/* ── État vide ────────────────────────────────────────────────────────────── */
|
|
60
|
+
/*
|
|
61
|
+
Ajouté 20s après showAds si aucun fill détecté.
|
|
62
|
+
Collapse à 1px (pas 0) : reste observable par l'IO si le fill arrive tard.
|
|
63
|
+
*/
|
|
64
|
+
.nodebb-ezoic-wrap.is-empty {
|
|
65
|
+
display: block !important;
|
|
66
|
+
height: 1px !important;
|
|
67
|
+
min-height: 1px !important;
|
|
68
|
+
max-height: 1px !important;
|
|
69
|
+
margin: 0 !important;
|
|
70
|
+
padding: 0 !important;
|
|
71
|
+
overflow: hidden !important;
|
|
72
|
+
}
|
|
59
73
|
|
|
60
74
|
/* ── Ezoic global (hors de nos wraps) ────────────────────────────────────── */
|
|
61
75
|
.ezoic-ad {
|