nodebb-plugin-ezoic-infinite 1.7.89 → 1.7.90
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 -53
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -132,6 +132,8 @@
|
|
|
132
132
|
burstDeadline: 0,
|
|
133
133
|
burstCount: 0,
|
|
134
134
|
lastBurstTs: 0,
|
|
135
|
+
lastScrollY: 0,
|
|
136
|
+
scrollDir: 1, // 1=down, -1=up
|
|
135
137
|
};
|
|
136
138
|
|
|
137
139
|
let blockedUntil = 0;
|
|
@@ -282,28 +284,6 @@
|
|
|
282
284
|
return (w?.isConnected) ? w : null;
|
|
283
285
|
}
|
|
284
286
|
|
|
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
|
-
|
|
307
287
|
// ── Pool ───────────────────────────────────────────────────────────────────
|
|
308
288
|
|
|
309
289
|
/**
|
|
@@ -335,26 +315,49 @@
|
|
|
335
315
|
typeof ez?.define !== 'function' ||
|
|
336
316
|
typeof ez?.displayMore !== 'function') return null;
|
|
337
317
|
|
|
338
|
-
const vh
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
318
|
+
const vh = window.innerHeight || 800;
|
|
319
|
+
const targetRect = targetEl?.getBoundingClientRect?.() || { top: vh, bottom: vh };
|
|
320
|
+
|
|
321
|
+
// Recyclage bidirectionnel :
|
|
322
|
+
// - scroll vers le bas -> recycle préférentiellement des wraps loin au-dessus
|
|
323
|
+
// - scroll vers le haut -> recycle préférentiellement des wraps loin en-dessous
|
|
324
|
+
// Fallback sur l'autre côté si aucun candidat.
|
|
325
|
+
const aboveThreshold = -vh; // wrap entièrement/suffisamment au-dessus
|
|
326
|
+
const belowThreshold = vh * 2; // wrap loin sous le viewport
|
|
327
|
+
|
|
328
|
+
let aboveEmpty = null, aboveEmptyBottom = Infinity;
|
|
329
|
+
let aboveFilled = null, aboveFilledBottom = Infinity;
|
|
330
|
+
let belowEmpty = null, belowEmptyTop = -Infinity;
|
|
331
|
+
let belowFilled = null, belowFilledTop = -Infinity;
|
|
344
332
|
|
|
345
333
|
document.querySelectorAll(`.${WRAP_CLASS}.${klass}`).forEach(wrap => {
|
|
346
334
|
try {
|
|
335
|
+
if (!wrap?.isConnected) return;
|
|
347
336
|
const rect = wrap.getBoundingClientRect();
|
|
348
|
-
|
|
349
|
-
if (
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
|
|
337
|
+
|
|
338
|
+
if (rect.bottom < aboveThreshold) {
|
|
339
|
+
if (!isFilled(wrap)) {
|
|
340
|
+
if (rect.bottom < aboveEmptyBottom) { aboveEmptyBottom = rect.bottom; aboveEmpty = wrap; }
|
|
341
|
+
} else {
|
|
342
|
+
if (rect.bottom < aboveFilledBottom) { aboveFilledBottom = rect.bottom; aboveFilled = wrap; }
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (rect.top > belowThreshold) {
|
|
348
|
+
if (!isFilled(wrap)) {
|
|
349
|
+
if (rect.top > belowEmptyTop) { belowEmptyTop = rect.top; belowEmpty = wrap; }
|
|
350
|
+
} else {
|
|
351
|
+
if (rect.top > belowFilledTop) { belowFilledTop = rect.top; belowFilled = wrap; }
|
|
352
|
+
}
|
|
353
353
|
}
|
|
354
354
|
} catch (_) {}
|
|
355
355
|
});
|
|
356
356
|
|
|
357
|
-
const
|
|
357
|
+
const preferBelow = (S.scrollDir < 0) || (targetRect.top < vh * 0.5);
|
|
358
|
+
const pickAbove = () => aboveEmpty ?? aboveFilled;
|
|
359
|
+
const pickBelow = () => belowEmpty ?? belowFilled;
|
|
360
|
+
const best = preferBelow ? (pickBelow() ?? pickAbove()) : (pickAbove() ?? pickBelow());
|
|
358
361
|
if (!best) return null;
|
|
359
362
|
const id = parseInt(best.getAttribute(A_WRAPID), 10);
|
|
360
363
|
if (!Number.isFinite(id)) return null;
|
|
@@ -493,13 +496,7 @@
|
|
|
493
496
|
const key = anchorKey(klass, el);
|
|
494
497
|
if (findWrap(key)) continue;
|
|
495
498
|
|
|
496
|
-
|
|
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
|
-
}
|
|
499
|
+
const id = pickId(poolKey);
|
|
503
500
|
if (id) {
|
|
504
501
|
const w = insertAfter(el, id, klass, key);
|
|
505
502
|
if (w) { observePh(id); inserted++; }
|
|
@@ -641,7 +638,6 @@
|
|
|
641
638
|
async function runCore() {
|
|
642
639
|
if (isBlocked()) return 0;
|
|
643
640
|
patchShowAds();
|
|
644
|
-
sweepDeadWraps();
|
|
645
641
|
|
|
646
642
|
const cfg = await fetchConfig();
|
|
647
643
|
if (!cfg || cfg.excluded) return 0;
|
|
@@ -740,26 +736,16 @@
|
|
|
740
736
|
const allSel = [SEL.post, SEL.topic, SEL.category];
|
|
741
737
|
S.domObs = new MutationObserver(muts => {
|
|
742
738
|
if (S.mutGuard > 0 || isBlocked()) return;
|
|
743
|
-
let sawRelevantAdd = false;
|
|
744
|
-
let sawWrapRemoval = false;
|
|
745
739
|
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
|
-
}
|
|
752
740
|
for (const n of m.addedNodes) {
|
|
753
741
|
if (n.nodeType !== 1) continue;
|
|
754
742
|
// matches() d'abord (O(1)), querySelector() seulement si nécessaire
|
|
755
743
|
if (allSel.some(s => { try { return n.matches(s); } catch(_){return false;} }) ||
|
|
756
744
|
allSel.some(s => { try { return !!n.querySelector(s); } catch(_){return false;} })) {
|
|
757
|
-
|
|
745
|
+
requestBurst(); return;
|
|
758
746
|
}
|
|
759
747
|
}
|
|
760
748
|
}
|
|
761
|
-
if (sawWrapRemoval) sweepDeadWraps();
|
|
762
|
-
if (sawRelevantAdd) requestBurst();
|
|
763
749
|
});
|
|
764
750
|
try { S.domObs.observe(document.body, { childList: true, subtree: true }); } catch (_) {}
|
|
765
751
|
}
|
|
@@ -861,6 +847,12 @@
|
|
|
861
847
|
function bindScroll() {
|
|
862
848
|
let ticking = false;
|
|
863
849
|
window.addEventListener('scroll', () => {
|
|
850
|
+
try {
|
|
851
|
+
const y = window.scrollY || window.pageYOffset || 0;
|
|
852
|
+
const dy = y - (S.lastScrollY || 0);
|
|
853
|
+
if (Math.abs(dy) > 2) S.scrollDir = dy > 0 ? 1 : -1;
|
|
854
|
+
S.lastScrollY = y;
|
|
855
|
+
} catch (_) {}
|
|
864
856
|
if (ticking) return;
|
|
865
857
|
ticking = true;
|
|
866
858
|
requestAnimationFrame(() => { ticking = false; requestBurst(); });
|
|
@@ -870,6 +862,7 @@
|
|
|
870
862
|
// ── Boot ───────────────────────────────────────────────────────────────────
|
|
871
863
|
|
|
872
864
|
S.pageKey = pageKey();
|
|
865
|
+
try { S.lastScrollY = window.scrollY || window.pageYOffset || 0; } catch (_) { S.lastScrollY = 0; }
|
|
873
866
|
muteConsole();
|
|
874
867
|
ensureTcfLocator();
|
|
875
868
|
warmNetwork();
|