nodebb-plugin-ezoic-infinite 1.7.87 → 1.7.89
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 +46 -75
- package/public/style.css +0 -8
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -77,9 +77,7 @@
|
|
|
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
|
-
|
|
81
|
-
const EMPTY_CHECK_PASSES = [20_000, 25_000, 35_000];
|
|
82
|
-
|
|
80
|
+
const EMPTY_CHECK_MS = 20_000; // délai avant collapse d'un wrap vide post-show
|
|
83
81
|
const MIN_PRUNE_AGE_MS = 8_000; // délai de grâce avant pruning (stabilisation DOM)
|
|
84
82
|
const MAX_INSERTS_RUN = 6; // max insertions par appel runCore
|
|
85
83
|
const MAX_INFLIGHT = 4; // max showAds() simultanés
|
|
@@ -122,8 +120,6 @@
|
|
|
122
120
|
cursors: { topics: 0, posts: 0, categories: 0 },
|
|
123
121
|
mountedIds: new Set(),
|
|
124
122
|
lastShow: new Map(),
|
|
125
|
-
emptyChecks: new Map(), // id -> [timerIds] checks is-empty multi-pass
|
|
126
|
-
fillObs: new Map(), // id -> MutationObserver placeholder fill tardif
|
|
127
123
|
io: null,
|
|
128
124
|
domObs: null,
|
|
129
125
|
mutGuard: 0, // >0 : mutations DOM en cours (MutationObserver ignoré)
|
|
@@ -146,48 +142,6 @@
|
|
|
146
142
|
const normBool = v => v === true || v === 'true' || v === 1 || v === '1' || v === 'on';
|
|
147
143
|
const isFilled = n => !!(n?.querySelector?.('iframe, ins, img, video, [data-google-container-id]'));
|
|
148
144
|
|
|
149
|
-
function clearEmptyChecks(id) {
|
|
150
|
-
const arr = S.emptyChecks.get(id);
|
|
151
|
-
if (arr) {
|
|
152
|
-
for (const t of arr) clearTimeout(t);
|
|
153
|
-
S.emptyChecks.delete(id);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function queueEmptyCheck(id, timerId) {
|
|
158
|
-
const arr = S.emptyChecks.get(id) || [];
|
|
159
|
-
arr.push(timerId);
|
|
160
|
-
S.emptyChecks.set(id, arr);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function uncollapseIfFilled(ph) {
|
|
164
|
-
try {
|
|
165
|
-
const wrap = ph?.closest?.(`.${WRAP_CLASS}`);
|
|
166
|
-
if (!wrap) return;
|
|
167
|
-
if (isFilled(ph)) wrap.classList.remove('is-empty');
|
|
168
|
-
} catch (_) {}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function watchPlaceholderFill(id) {
|
|
172
|
-
try {
|
|
173
|
-
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
174
|
-
if (!ph?.isConnected) return;
|
|
175
|
-
if (S.fillObs.has(id)) return;
|
|
176
|
-
const obs = new MutationObserver(() => uncollapseIfFilled(ph));
|
|
177
|
-
obs.observe(ph, { childList: true, subtree: true, attributes: true });
|
|
178
|
-
S.fillObs.set(id, obs);
|
|
179
|
-
uncollapseIfFilled(ph);
|
|
180
|
-
} catch (_) {}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function unwatchPlaceholderFill(id) {
|
|
184
|
-
const obs = S.fillObs.get(id);
|
|
185
|
-
if (obs) {
|
|
186
|
-
try { obs.disconnect(); } catch (_) {}
|
|
187
|
-
S.fillObs.delete(id);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
145
|
function mutate(fn) {
|
|
192
146
|
S.mutGuard++;
|
|
193
147
|
try { fn(); } finally { S.mutGuard--; }
|
|
@@ -328,6 +282,28 @@
|
|
|
328
282
|
return (w?.isConnected) ? w : null;
|
|
329
283
|
}
|
|
330
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Libère les ids de wraps supprimés du DOM (virtualisation NodeBB / rerender).
|
|
287
|
+
* Sans ce sweep, mountedIds peut conserver des ids fantômes → pool épuisé
|
|
288
|
+
* après long scroll alors qu'aucun wrap recyclable n'existe encore en DOM.
|
|
289
|
+
*/
|
|
290
|
+
function sweepDeadWraps() {
|
|
291
|
+
for (const [key, w] of S.wrapByKey.entries()) {
|
|
292
|
+
if (w?.isConnected) continue;
|
|
293
|
+
const id = parseInt(w?.getAttribute?.(A_WRAPID), 10);
|
|
294
|
+
if (Number.isFinite(id)) {
|
|
295
|
+
S.mountedIds.delete(id);
|
|
296
|
+
S.lastShow.delete(id);
|
|
297
|
+
S.pendingSet.delete(id);
|
|
298
|
+
}
|
|
299
|
+
S.wrapByKey.delete(key);
|
|
300
|
+
}
|
|
301
|
+
if (S.pending.length) {
|
|
302
|
+
S.pending = S.pending.filter(id => !S.pendingSet.has(id) || document.getElementById(`${PH_PREFIX}${id}`)?.isConnected);
|
|
303
|
+
S.pendingSet = new Set(S.pending);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
331
307
|
// ── Pool ───────────────────────────────────────────────────────────────────
|
|
332
308
|
|
|
333
309
|
/**
|
|
@@ -387,7 +363,6 @@
|
|
|
387
363
|
// Neutraliser l'IO sur ce wrap avant déplacement — évite un showAds
|
|
388
364
|
// parasite si le nœud était encore dans la zone IO_MARGIN.
|
|
389
365
|
try { const ph = best.querySelector(`#${PH_PREFIX}${id}`); if (ph) S.io?.unobserve(ph); } catch (_) {}
|
|
390
|
-
if (Number.isFinite(id)) clearEmptyChecks(id);
|
|
391
366
|
mutate(() => {
|
|
392
367
|
best.setAttribute(A_ANCHOR, newKey);
|
|
393
368
|
best.setAttribute(A_CREATED, String(ts()));
|
|
@@ -399,7 +374,6 @@
|
|
|
399
374
|
});
|
|
400
375
|
if (oldKey && S.wrapByKey.get(oldKey) === best) S.wrapByKey.delete(oldKey);
|
|
401
376
|
S.wrapByKey.set(newKey, best);
|
|
402
|
-
observePh(id);
|
|
403
377
|
|
|
404
378
|
// Délais requis : destroyPlaceholders est asynchrone en interne
|
|
405
379
|
const doDestroy = () => { try { ez.destroyPlaceholders([id]); } catch (_) {} setTimeout(doDefine, 300); };
|
|
@@ -444,7 +418,6 @@
|
|
|
444
418
|
const ph = w.querySelector(`[id^="${PH_PREFIX}"]`);
|
|
445
419
|
if (ph instanceof Element) S.io?.unobserve(ph);
|
|
446
420
|
const id = parseInt(w.getAttribute(A_WRAPID), 10);
|
|
447
|
-
if (Number.isFinite(id)) { clearEmptyChecks(id); unwatchPlaceholderFill(id); }
|
|
448
421
|
if (Number.isFinite(id)) S.mountedIds.delete(id);
|
|
449
422
|
const key = w.getAttribute(A_ANCHOR);
|
|
450
423
|
if (key && S.wrapByKey.get(key) === w) S.wrapByKey.delete(key);
|
|
@@ -520,7 +493,13 @@
|
|
|
520
493
|
const key = anchorKey(klass, el);
|
|
521
494
|
if (findWrap(key)) continue;
|
|
522
495
|
|
|
523
|
-
|
|
496
|
+
let id = pickId(poolKey);
|
|
497
|
+
if (!id) {
|
|
498
|
+
// Réessaie après sweep : des wraps ont pu être retirés du DOM (virtualisation)
|
|
499
|
+
// sans passer par dropWrap, laissant des ids fantômes dans mountedIds.
|
|
500
|
+
sweepDeadWraps();
|
|
501
|
+
id = pickId(poolKey);
|
|
502
|
+
}
|
|
524
503
|
if (id) {
|
|
525
504
|
const w = insertAfter(el, id, klass, key);
|
|
526
505
|
if (w) { observePh(id); inserted++; }
|
|
@@ -552,10 +531,7 @@
|
|
|
552
531
|
|
|
553
532
|
function observePh(id) {
|
|
554
533
|
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
555
|
-
if (ph?.isConnected) {
|
|
556
|
-
try { getIO()?.observe(ph); } catch (_) {}
|
|
557
|
-
watchPlaceholderFill(id);
|
|
558
|
-
}
|
|
534
|
+
if (ph?.isConnected) try { getIO()?.observe(ph); } catch (_) {}
|
|
559
535
|
}
|
|
560
536
|
|
|
561
537
|
function enqueueShow(id) {
|
|
@@ -595,9 +571,6 @@
|
|
|
595
571
|
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
596
572
|
if (!ph?.isConnected || isFilled(ph)) { clearTimeout(timer); return release(); }
|
|
597
573
|
|
|
598
|
-
clearEmptyChecks(id);
|
|
599
|
-
try { ph.closest?.(`.${WRAP_CLASS}`)?.classList.remove('is-empty'); } catch (_) {}
|
|
600
|
-
|
|
601
574
|
const t = ts();
|
|
602
575
|
if (t - (S.lastShow.get(id) ?? 0) < SHOW_THROTTLE_MS) { clearTimeout(timer); return release(); }
|
|
603
576
|
S.lastShow.set(id, t);
|
|
@@ -617,24 +590,15 @@
|
|
|
617
590
|
}
|
|
618
591
|
|
|
619
592
|
function scheduleEmptyCheck(id, showTs) {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
const runCheck = () => {
|
|
593
|
+
setTimeout(() => {
|
|
623
594
|
try {
|
|
624
595
|
const ph = document.getElementById(`${PH_PREFIX}${id}`);
|
|
625
596
|
const wrap = ph?.closest?.(`.${WRAP_CLASS}`);
|
|
626
597
|
if (!wrap || !ph?.isConnected) return;
|
|
627
598
|
if (parseInt(wrap.getAttribute(A_SHOWN) || '0', 10) > showTs) return;
|
|
628
|
-
|
|
629
|
-
if (filled) wrap.classList.remove('is-empty');
|
|
630
|
-
else wrap.classList.add('is-empty');
|
|
599
|
+
wrap.classList.toggle('is-empty', !isFilled(ph));
|
|
631
600
|
} catch (_) {}
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
for (const delay of EMPTY_CHECK_PASSES) {
|
|
635
|
-
const tid = setTimeout(runCheck, delay);
|
|
636
|
-
queueEmptyCheck(id, tid);
|
|
637
|
-
}
|
|
601
|
+
}, EMPTY_CHECK_MS);
|
|
638
602
|
}
|
|
639
603
|
|
|
640
604
|
// ── Patch Ezoic showAds ────────────────────────────────────────────────────
|
|
@@ -677,6 +641,7 @@
|
|
|
677
641
|
async function runCore() {
|
|
678
642
|
if (isBlocked()) return 0;
|
|
679
643
|
patchShowAds();
|
|
644
|
+
sweepDeadWraps();
|
|
680
645
|
|
|
681
646
|
const cfg = await fetchConfig();
|
|
682
647
|
if (!cfg || cfg.excluded) return 0;
|
|
@@ -764,10 +729,6 @@
|
|
|
764
729
|
S.inflight = 0;
|
|
765
730
|
S.pending = [];
|
|
766
731
|
S.pendingSet.clear();
|
|
767
|
-
S.emptyChecks.forEach(arr => { try { arr.forEach(clearTimeout); } catch (_) {} });
|
|
768
|
-
S.emptyChecks.clear();
|
|
769
|
-
S.fillObs.forEach(obs => { try { obs.disconnect(); } catch (_) {} });
|
|
770
|
-
S.fillObs.clear();
|
|
771
732
|
S.burstActive = false;
|
|
772
733
|
S.runQueued = false;
|
|
773
734
|
}
|
|
@@ -779,16 +740,26 @@
|
|
|
779
740
|
const allSel = [SEL.post, SEL.topic, SEL.category];
|
|
780
741
|
S.domObs = new MutationObserver(muts => {
|
|
781
742
|
if (S.mutGuard > 0 || isBlocked()) return;
|
|
743
|
+
let sawRelevantAdd = false;
|
|
744
|
+
let sawWrapRemoval = false;
|
|
782
745
|
for (const m of muts) {
|
|
746
|
+
for (const n of m.removedNodes) {
|
|
747
|
+
if (n.nodeType !== 1) continue;
|
|
748
|
+
if ((n.matches && n.matches(`.${WRAP_CLASS}`)) || n.querySelector?.(`.${WRAP_CLASS}`)) {
|
|
749
|
+
sawWrapRemoval = true;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
783
752
|
for (const n of m.addedNodes) {
|
|
784
753
|
if (n.nodeType !== 1) continue;
|
|
785
754
|
// matches() d'abord (O(1)), querySelector() seulement si nécessaire
|
|
786
755
|
if (allSel.some(s => { try { return n.matches(s); } catch(_){return false;} }) ||
|
|
787
756
|
allSel.some(s => { try { return !!n.querySelector(s); } catch(_){return false;} })) {
|
|
788
|
-
|
|
757
|
+
sawRelevantAdd = true;
|
|
789
758
|
}
|
|
790
759
|
}
|
|
791
760
|
}
|
|
761
|
+
if (sawWrapRemoval) sweepDeadWraps();
|
|
762
|
+
if (sawRelevantAdd) requestBurst();
|
|
792
763
|
});
|
|
793
764
|
try { S.domObs.observe(document.body, { childList: true, subtree: true }); } catch (_) {}
|
|
794
765
|
}
|
package/public/style.css
CHANGED
|
@@ -71,14 +71,6 @@
|
|
|
71
71
|
overflow: hidden !important;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/* Filet de sécurité : si un fill est présent malgré is-empty, on ne collapse pas */
|
|
75
|
-
.nodebb-ezoic-wrap.is-empty:has(iframe, ins, img, video, [data-google-container-id]) {
|
|
76
|
-
height: auto !important;
|
|
77
|
-
min-height: 1px !important;
|
|
78
|
-
max-height: none !important;
|
|
79
|
-
overflow: visible !important;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
74
|
/* ── Ezoic global (hors de nos wraps) ────────────────────────────────────── */
|
|
83
75
|
.ezoic-ad {
|
|
84
76
|
margin: 0 !important;
|