nodebb-plugin-ezoic-infinite 1.7.94 → 1.7.96
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 +92 -150
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -77,17 +77,17 @@
|
|
|
77
77
|
const A_CREATED = 'data-ezoic-created'; // timestamp création ms
|
|
78
78
|
const A_SHOWN = 'data-ezoic-shown'; // timestamp dernier showAds ms
|
|
79
79
|
|
|
80
|
+
const EMPTY_CHECK_MS = 20_000; // délai avant collapse d'un wrap vide post-show
|
|
80
81
|
const MIN_PRUNE_AGE_MS = 8_000; // délai de grâce avant pruning (stabilisation DOM)
|
|
81
|
-
const MAX_INSERTS_RUN =
|
|
82
|
-
const MAX_INFLIGHT =
|
|
83
|
-
const SHOW_THROTTLE_MS =
|
|
84
|
-
const
|
|
85
|
-
const PENDING_TIMEOUT_MS = 12_000; // au-delà, tentative considérée obsolète
|
|
86
|
-
const BURST_COOLDOWN_MS = 200; // délai min entre deux déclenchements de burst
|
|
82
|
+
const MAX_INSERTS_RUN = 10; // max insertions par appel runCore (plus réactif)
|
|
83
|
+
const MAX_INFLIGHT = 4; // max showAds() simultanés
|
|
84
|
+
const SHOW_THROTTLE_MS = 600; // anti-spam showAds() par id (plus réactif)
|
|
85
|
+
const BURST_COOLDOWN_MS = 120; // délai min entre deux déclenchements de burst
|
|
87
86
|
|
|
88
87
|
// Marges IO larges et fixes — observer créé une seule fois au boot
|
|
89
88
|
const IO_MARGIN_DESKTOP = '2500px 0px 2500px 0px';
|
|
90
89
|
const IO_MARGIN_MOBILE = '3500px 0px 3500px 0px';
|
|
90
|
+
const FAST_SHOW_MARGIN_PX = 900; // show immédiat si slot déjà proche viewport
|
|
91
91
|
|
|
92
92
|
const SEL = {
|
|
93
93
|
post: '[component="post"][data-pid]',
|
|
@@ -125,19 +125,16 @@
|
|
|
125
125
|
domObs: null,
|
|
126
126
|
mutGuard: 0, // >0 : mutations DOM en cours (MutationObserver ignoré)
|
|
127
127
|
inflight: 0, // showAds() en cours
|
|
128
|
-
pending: [], // ids en attente de slot inflight
|
|
128
|
+
pending: [], // ids en attente de slot inflight
|
|
129
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
|
|
133
130
|
wrapByKey: new Map(), // anchorKey → wrap DOM node
|
|
134
131
|
runQueued: false,
|
|
135
132
|
burstActive: false,
|
|
136
133
|
burstDeadline: 0,
|
|
137
134
|
burstCount: 0,
|
|
138
135
|
lastBurstTs: 0,
|
|
139
|
-
lastScrollY: 0,
|
|
140
136
|
scrollDir: 1,
|
|
137
|
+
lastScrollY: 0,
|
|
141
138
|
};
|
|
142
139
|
|
|
143
140
|
let blockedUntil = 0;
|
|
@@ -147,69 +144,6 @@
|
|
|
147
144
|
const isMobile = () => { try { return window.innerWidth < 768; } catch (_) { return false; } };
|
|
148
145
|
const normBool = v => v === true || v === 'true' || v === 1 || v === '1' || v === 'on';
|
|
149
146
|
const isFilled = n => !!(n?.querySelector?.('iframe, ins, img, video, [data-google-container-id]'));
|
|
150
|
-
const isCooling = id => (S.cooldownUntil.get(id) || 0) > ts();
|
|
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
|
-
}
|
|
162
|
-
|
|
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);
|
|
172
|
-
}
|
|
173
|
-
|
|
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);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function watchPlaceholderFill(id) {
|
|
193
|
-
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
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
|
-
};
|
|
205
|
-
try {
|
|
206
|
-
const obs = new MutationObserver(onMut);
|
|
207
|
-
obs.observe(ph, { childList: true, subtree: true, attributes: true });
|
|
208
|
-
S.fillObsById.set(id, obs);
|
|
209
|
-
} catch (_) {}
|
|
210
|
-
onMut();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
147
|
|
|
214
148
|
function mutate(fn) {
|
|
215
149
|
S.mutGuard++;
|
|
@@ -351,21 +285,6 @@
|
|
|
351
285
|
return (w?.isConnected) ? w : null;
|
|
352
286
|
}
|
|
353
287
|
|
|
354
|
-
function sweepDeadWraps() {
|
|
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)) {
|
|
360
|
-
S.mountedIds.delete(id);
|
|
361
|
-
S.pendingSet.delete(id);
|
|
362
|
-
S.pending = S.pending.filter(it => (it?.id ?? it) !== id);
|
|
363
|
-
S.pendingShows.delete(id);
|
|
364
|
-
unobserveFill(id);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
288
|
// ── Pool ───────────────────────────────────────────────────────────────────
|
|
370
289
|
|
|
371
290
|
/**
|
|
@@ -397,40 +316,59 @@
|
|
|
397
316
|
typeof ez?.define !== 'function' ||
|
|
398
317
|
typeof ez?.displayMore !== 'function') return null;
|
|
399
318
|
|
|
400
|
-
const vh
|
|
401
|
-
const
|
|
402
|
-
const
|
|
319
|
+
const vh = window.innerHeight || 800;
|
|
320
|
+
const dir = S.scrollDir >= 0 ? 1 : -1;
|
|
321
|
+
const farTopThreshold = -vh; // hors écran au-dessus
|
|
322
|
+
const farBottomThreshold = vh * 2; // loin sous le viewport (scroll-up)
|
|
403
323
|
|
|
404
|
-
let
|
|
405
|
-
let
|
|
324
|
+
let prefEmpty = null, prefEmptyScore = -Infinity;
|
|
325
|
+
let prefFilled = null, prefFilledScore = -Infinity;
|
|
326
|
+
let altEmpty = null, altEmptyScore = -Infinity;
|
|
327
|
+
let altFilled = null, altFilledScore = -Infinity;
|
|
406
328
|
|
|
407
329
|
for (const wrap of S.wrapByKey.values()) {
|
|
330
|
+
if (!wrap?.isConnected) continue;
|
|
331
|
+
if (!wrap.classList?.contains(WRAP_CLASS) || !wrap.classList.contains(klass)) continue;
|
|
408
332
|
try {
|
|
409
|
-
if (!wrap?.isConnected || !wrap.classList?.contains(WRAP_CLASS) || !wrap.classList.contains(klass)) continue;
|
|
410
333
|
const rect = wrap.getBoundingClientRect();
|
|
411
|
-
const farAbove = rect.bottom
|
|
412
|
-
const farBelow = rect.top
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
if (
|
|
421
|
-
|
|
422
|
-
|
|
334
|
+
const farAbove = rect.bottom <= farTopThreshold;
|
|
335
|
+
const farBelow = rect.top >= farBottomThreshold;
|
|
336
|
+
let preferred = false;
|
|
337
|
+
let score = -Infinity;
|
|
338
|
+
|
|
339
|
+
if (dir >= 0) {
|
|
340
|
+
if (farAbove) { preferred = true; score = Math.abs(rect.bottom); }
|
|
341
|
+
else if (farBelow) { score = Math.abs(rect.top); }
|
|
342
|
+
} else {
|
|
343
|
+
if (farBelow) { preferred = true; score = Math.abs(rect.top); }
|
|
344
|
+
else if (farAbove) { score = Math.abs(rect.bottom); }
|
|
345
|
+
}
|
|
346
|
+
if (score === -Infinity) continue;
|
|
347
|
+
|
|
348
|
+
const filled = isFilled(wrap);
|
|
349
|
+
if (preferred) {
|
|
350
|
+
if (!filled) {
|
|
351
|
+
if (score > prefEmptyScore) { prefEmptyScore = score; prefEmpty = wrap; }
|
|
352
|
+
} else {
|
|
353
|
+
if (score > prefFilledScore) { prefFilledScore = score; prefFilled = wrap; }
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
if (!filled) {
|
|
357
|
+
if (score > altEmptyScore) { altEmptyScore = score; altEmpty = wrap; }
|
|
358
|
+
} else {
|
|
359
|
+
if (score > altFilledScore) { altFilledScore = score; altFilled = wrap; }
|
|
360
|
+
}
|
|
423
361
|
}
|
|
424
362
|
} catch (_) {}
|
|
425
363
|
}
|
|
426
364
|
|
|
427
|
-
best =
|
|
365
|
+
const best = prefEmpty ?? prefFilled ?? altEmpty ?? altFilled;
|
|
428
366
|
if (!best) return null;
|
|
429
367
|
const id = parseInt(best.getAttribute(A_WRAPID), 10);
|
|
430
368
|
if (!Number.isFinite(id)) return null;
|
|
431
369
|
|
|
432
370
|
const oldKey = best.getAttribute(A_ANCHOR);
|
|
433
|
-
try { const ph = best.querySelector(`#${PH_PREFIX}${id}`); if (ph)
|
|
371
|
+
try { const ph = best.querySelector(`#${PH_PREFIX}${id}`); if (ph) S.io?.unobserve(ph); } catch (_) {}
|
|
434
372
|
mutate(() => {
|
|
435
373
|
best.setAttribute(A_ANCHOR, newKey);
|
|
436
374
|
best.setAttribute(A_CREATED, String(ts()));
|
|
@@ -443,9 +381,9 @@
|
|
|
443
381
|
if (oldKey && S.wrapByKey.get(oldKey) === best) S.wrapByKey.delete(oldKey);
|
|
444
382
|
S.wrapByKey.set(newKey, best);
|
|
445
383
|
|
|
446
|
-
const doDestroy = () => { try { ez.destroyPlaceholders([id]); } catch (_) {} setTimeout(doDefine,
|
|
447
|
-
const doDefine = () => { try { ez.define([id]);
|
|
448
|
-
const doDisplay = () => { try { ez.displayMore([id]);
|
|
384
|
+
const doDestroy = () => { try { ez.destroyPlaceholders([id]); } catch (_) {} setTimeout(doDefine, 220); };
|
|
385
|
+
const doDefine = () => { try { ez.define([id]); } catch (_) {} setTimeout(doDisplay,220); };
|
|
386
|
+
const doDisplay = () => { try { ez.displayMore([id]); } catch (_) {} };
|
|
449
387
|
try { (typeof ez.cmd?.push === 'function') ? ez.cmd.push(doDestroy) : doDestroy(); } catch (_) {}
|
|
450
388
|
|
|
451
389
|
return { id, wrap: best };
|
|
@@ -485,7 +423,6 @@
|
|
|
485
423
|
const ph = w.querySelector(`[id^="${PH_PREFIX}"]`);
|
|
486
424
|
if (ph instanceof Element) S.io?.unobserve(ph);
|
|
487
425
|
const id = parseInt(w.getAttribute(A_WRAPID), 10);
|
|
488
|
-
if (Number.isFinite(id)) unobserveFill(id);
|
|
489
426
|
if (Number.isFinite(id)) S.mountedIds.delete(id);
|
|
490
427
|
const key = w.getAttribute(A_ANCHOR);
|
|
491
428
|
if (key && S.wrapByKey.get(key) === w) S.wrapByKey.delete(key);
|
|
@@ -549,7 +486,6 @@
|
|
|
549
486
|
function injectBetween(klass, items, interval, showFirst, poolKey) {
|
|
550
487
|
if (!items.length) return 0;
|
|
551
488
|
let inserted = 0;
|
|
552
|
-
sweepDeadWraps();
|
|
553
489
|
|
|
554
490
|
for (const el of items) {
|
|
555
491
|
if (inserted >= MAX_INSERTS_RUN) break;
|
|
@@ -562,15 +498,13 @@
|
|
|
562
498
|
const key = anchorKey(klass, el);
|
|
563
499
|
if (findWrap(key)) continue;
|
|
564
500
|
|
|
565
|
-
|
|
566
|
-
if (!id) { sweepDeadWraps(); id = pickId(poolKey); }
|
|
501
|
+
const id = pickId(poolKey);
|
|
567
502
|
if (id) {
|
|
568
503
|
const w = insertAfter(el, id, klass, key);
|
|
569
504
|
if (w) { observePh(id); inserted++; }
|
|
570
505
|
} else {
|
|
571
506
|
const recycled = recycleAndMove(klass, el, key);
|
|
572
507
|
if (!recycled) break;
|
|
573
|
-
observePh(recycled.id);
|
|
574
508
|
inserted++;
|
|
575
509
|
}
|
|
576
510
|
}
|
|
@@ -596,54 +530,54 @@
|
|
|
596
530
|
|
|
597
531
|
function observePh(id) {
|
|
598
532
|
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
599
|
-
if (ph?.isConnected)
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
533
|
+
if (!ph?.isConnected) return;
|
|
534
|
+
try { getIO()?.observe(ph); } catch (_) {}
|
|
535
|
+
try {
|
|
536
|
+
const r = ph.getBoundingClientRect();
|
|
537
|
+
const vh = window.innerHeight || 800;
|
|
538
|
+
if (r.bottom >= -FAST_SHOW_MARGIN_PX && r.top <= vh + FAST_SHOW_MARGIN_PX) {
|
|
539
|
+
const pid = parseInt(ph.getAttribute('data-ezoic-id'), 10);
|
|
540
|
+
if (Number.isFinite(pid) && pid > 0) enqueueShow(pid);
|
|
541
|
+
}
|
|
542
|
+
} catch (_) {}
|
|
603
543
|
}
|
|
604
544
|
|
|
605
545
|
function enqueueShow(id) {
|
|
606
546
|
if (!id || isBlocked()) return;
|
|
607
|
-
if (isCooling(id) || S.pendingShows.has(id)) return;
|
|
608
547
|
if (ts() - (S.lastShow.get(id) ?? 0) < SHOW_THROTTLE_MS) return;
|
|
609
|
-
if (S.inflight >= MAX_INFLIGHT) {
|
|
548
|
+
if (S.inflight >= MAX_INFLIGHT) {
|
|
549
|
+
if (!S.pendingSet.has(id)) { S.pending.push(id); S.pendingSet.add(id); }
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
610
552
|
startShow(id);
|
|
611
553
|
}
|
|
612
554
|
|
|
613
555
|
function drainQueue() {
|
|
614
556
|
if (isBlocked()) return;
|
|
615
557
|
while (S.inflight < MAX_INFLIGHT && S.pending.length) {
|
|
616
|
-
const
|
|
617
|
-
const id = item?.id ?? item;
|
|
558
|
+
const id = S.pending.shift();
|
|
618
559
|
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;
|
|
622
560
|
startShow(id);
|
|
623
561
|
}
|
|
624
562
|
}
|
|
625
563
|
|
|
626
564
|
function startShow(id) {
|
|
627
565
|
if (!id || isBlocked()) return;
|
|
628
|
-
if (S.pendingShows.has(id) || isCooling(id)) return;
|
|
629
|
-
S.pendingShows.add(id);
|
|
630
566
|
S.inflight++;
|
|
631
567
|
let done = false;
|
|
632
568
|
const release = () => {
|
|
633
569
|
if (done) return;
|
|
634
570
|
done = true;
|
|
635
|
-
S.pendingShows.delete(id);
|
|
636
571
|
S.inflight = Math.max(0, S.inflight - 1);
|
|
637
572
|
drainQueue();
|
|
638
573
|
};
|
|
639
|
-
const timer = setTimeout(
|
|
574
|
+
const timer = setTimeout(release, 7000);
|
|
640
575
|
|
|
641
576
|
requestAnimationFrame(() => {
|
|
642
577
|
try {
|
|
643
578
|
if (isBlocked()) { clearTimeout(timer); return release(); }
|
|
644
579
|
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
645
|
-
if (!ph?.isConnected) { clearTimeout(timer);
|
|
646
|
-
if (isFilled(ph)) { clearTimeout(timer); clearCooldownIfFilled(id); return release(); }
|
|
580
|
+
if (!ph?.isConnected || isFilled(ph)) { clearTimeout(timer); return release(); }
|
|
647
581
|
|
|
648
582
|
const t = ts();
|
|
649
583
|
if (t - (S.lastShow.get(id) ?? 0) < SHOW_THROTTLE_MS) { clearTimeout(timer); return release(); }
|
|
@@ -655,13 +589,25 @@
|
|
|
655
589
|
const ez = window.ezstandalone;
|
|
656
590
|
const doShow = () => {
|
|
657
591
|
try { ez.showAds(id); } catch (_) {}
|
|
658
|
-
|
|
592
|
+
scheduleEmptyCheck(id, t);
|
|
593
|
+
setTimeout(() => { clearTimeout(timer); release(); }, 350);
|
|
659
594
|
};
|
|
660
595
|
Array.isArray(ez.cmd) ? ez.cmd.push(doShow) : doShow();
|
|
661
596
|
} catch (_) { clearTimeout(timer); release(); }
|
|
662
597
|
});
|
|
663
598
|
}
|
|
664
599
|
|
|
600
|
+
function scheduleEmptyCheck(id, showTs) {
|
|
601
|
+
setTimeout(() => {
|
|
602
|
+
try {
|
|
603
|
+
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
604
|
+
const wrap = ph?.closest?.(`.${WRAP_CLASS}`);
|
|
605
|
+
if (!wrap || !ph?.isConnected) return;
|
|
606
|
+
if (parseInt(wrap.getAttribute(A_SHOWN) || '0', 10) > showTs) return;
|
|
607
|
+
wrap.classList.toggle('is-empty', !isFilled(ph));
|
|
608
|
+
} catch (_) {}
|
|
609
|
+
}, EMPTY_CHECK_MS);
|
|
610
|
+
}
|
|
665
611
|
|
|
666
612
|
// ── Patch Ezoic showAds ────────────────────────────────────────────────────
|
|
667
613
|
//
|
|
@@ -703,7 +649,6 @@
|
|
|
703
649
|
async function runCore() {
|
|
704
650
|
if (isBlocked()) return 0;
|
|
705
651
|
patchShowAds();
|
|
706
|
-
sweepDeadWraps();
|
|
707
652
|
|
|
708
653
|
const cfg = await fetchConfig();
|
|
709
654
|
if (!cfg || cfg.excluded) return 0;
|
|
@@ -770,7 +715,7 @@
|
|
|
770
715
|
S.burstCount++;
|
|
771
716
|
scheduleRun(n => {
|
|
772
717
|
if (!n && !S.pending.length) { S.burstActive = false; return; }
|
|
773
|
-
setTimeout(step, n > 0 ?
|
|
718
|
+
setTimeout(step, n > 0 ? 80 : 180);
|
|
774
719
|
});
|
|
775
720
|
};
|
|
776
721
|
step();
|
|
@@ -791,9 +736,6 @@
|
|
|
791
736
|
S.inflight = 0;
|
|
792
737
|
S.pending = [];
|
|
793
738
|
S.pendingSet.clear();
|
|
794
|
-
S.pendingShows.clear();
|
|
795
|
-
S.cooldownUntil.clear();
|
|
796
|
-
for (const id of Array.from(S.fillObsById.keys())) unobserveFill(id);
|
|
797
739
|
S.burstActive = false;
|
|
798
740
|
S.runQueued = false;
|
|
799
741
|
}
|
|
@@ -805,12 +747,7 @@
|
|
|
805
747
|
const allSel = [SEL.post, SEL.topic, SEL.category];
|
|
806
748
|
S.domObs = new MutationObserver(muts => {
|
|
807
749
|
if (S.mutGuard > 0 || isBlocked()) return;
|
|
808
|
-
let sawWrapRemoval = false;
|
|
809
750
|
for (const m of muts) {
|
|
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 (_) {}
|
|
813
|
-
}
|
|
814
751
|
for (const n of m.addedNodes) {
|
|
815
752
|
if (n.nodeType !== 1) continue;
|
|
816
753
|
// matches() d'abord (O(1)), querySelector() seulement si nécessaire
|
|
@@ -820,7 +757,6 @@
|
|
|
820
757
|
}
|
|
821
758
|
}
|
|
822
759
|
}
|
|
823
|
-
if (sawWrapRemoval) sweepDeadWraps();
|
|
824
760
|
});
|
|
825
761
|
try { S.domObs.observe(document.body, { childList: true, subtree: true }); } catch (_) {}
|
|
826
762
|
}
|
|
@@ -924,16 +860,22 @@
|
|
|
924
860
|
window.addEventListener('scroll', () => {
|
|
925
861
|
if (ticking) return;
|
|
926
862
|
ticking = true;
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
863
|
+
requestAnimationFrame(() => {
|
|
864
|
+
ticking = false;
|
|
865
|
+
try {
|
|
866
|
+
const y = window.scrollY || window.pageYOffset || 0;
|
|
867
|
+
S.scrollDir = (y < (S.lastScrollY || 0)) ? -1 : 1;
|
|
868
|
+
S.lastScrollY = y;
|
|
869
|
+
} catch (_) {}
|
|
870
|
+
requestBurst();
|
|
871
|
+
});
|
|
931
872
|
}, { passive: true });
|
|
932
873
|
}
|
|
933
874
|
|
|
934
875
|
// ── Boot ───────────────────────────────────────────────────────────────────
|
|
935
876
|
|
|
936
877
|
S.pageKey = pageKey();
|
|
878
|
+
try { S.lastScrollY = window.scrollY || window.pageYOffset || 0; } catch (_) {}
|
|
937
879
|
muteConsole();
|
|
938
880
|
ensureTcfLocator();
|
|
939
881
|
warmNetwork();
|