nodebb-plugin-ezoic-infinite 1.7.23 → 1.7.24
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 +59 -7
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* NodeBB Ezoic Infinite Ads — client.js
|
|
2
|
+
* NodeBB Ezoic Infinite Ads — client.js v30
|
|
3
3
|
*
|
|
4
4
|
* Historique des corrections majeures
|
|
5
5
|
* ────────────────────────────────────
|
|
@@ -30,6 +30,14 @@
|
|
|
30
30
|
* v28 decluster supprimé. pruneOrphans supprimé (v27). Wraps persistants sur session.
|
|
31
31
|
*
|
|
32
32
|
* v29 Fix ancrage topics : data-index → data-tid.
|
|
33
|
+
*
|
|
34
|
+
* v30 Fix adjacentWrap : ne compte plus les wraps orphelins (ancre hors DOM).
|
|
35
|
+
* Quand NodeBB virtualise et retire des topics du DOM, les wraps restent
|
|
36
|
+
* en place (div dans le ul). adjacentWrap(el) retournait true sur ces
|
|
37
|
+
* wraps orphelins → injection bloquée sur les topics suivants.
|
|
38
|
+
* Fix : adjacentWrap vérifie que le wrap voisin a son ancre dans le DOM.
|
|
39
|
+
* recycleOrphanId() : quand le pool est épuisé, recycle les wraps orphelins
|
|
40
|
+
* non remplis qui sont loin au-dessus du viewport.
|
|
33
41
|
* data-index = position relative dans le batch NodeBB, pas un ID stable.
|
|
34
42
|
* Lors du scroll infini, les nouveaux batches démarrent à data-index=0,
|
|
35
43
|
* ce qui créait des collisions de clés d'ancrage → mauvaise déduplication
|
|
@@ -184,11 +192,23 @@
|
|
|
184
192
|
const getTopics = () => Array.from(document.querySelectorAll(SEL.topic));
|
|
185
193
|
const getCategories = () => Array.from(document.querySelectorAll(SEL.category));
|
|
186
194
|
|
|
195
|
+
function wrapIsLive(wrap) {
|
|
196
|
+
if (!wrap?.classList?.contains(WRAP_CLASS)) return false;
|
|
197
|
+
const key = wrap.getAttribute(A_ANCHOR);
|
|
198
|
+
if (!key) return false;
|
|
199
|
+
const colonIdx = key.indexOf(':');
|
|
200
|
+
const klass = key.slice(0, colonIdx);
|
|
201
|
+
const anchorId = key.slice(colonIdx + 1);
|
|
202
|
+
const cfg = KIND[klass];
|
|
203
|
+
if (!cfg) return false;
|
|
204
|
+
try {
|
|
205
|
+
const found = document.querySelector(`${cfg.sel}[${cfg.anchorAttr}="${anchorId}"]`);
|
|
206
|
+
return !!(found?.isConnected);
|
|
207
|
+
} catch (_) { return false; }
|
|
208
|
+
}
|
|
209
|
+
|
|
187
210
|
function adjacentWrap(el) {
|
|
188
|
-
return
|
|
189
|
-
el.nextElementSibling?.classList?.contains(WRAP_CLASS) ||
|
|
190
|
-
el.previousElementSibling?.classList?.contains(WRAP_CLASS)
|
|
191
|
-
);
|
|
211
|
+
return wrapIsLive(el.nextElementSibling) || wrapIsLive(el.previousElementSibling);
|
|
192
212
|
}
|
|
193
213
|
|
|
194
214
|
// ── Ancres stables ─────────────────────────────────────────────────────────
|
|
@@ -230,6 +250,38 @@
|
|
|
230
250
|
return null;
|
|
231
251
|
}
|
|
232
252
|
|
|
253
|
+
function recycleOrphanId(klass) {
|
|
254
|
+
// Quand le pool est épuisé : cherche un wrap orphelin (ancre hors DOM, non rempli)
|
|
255
|
+
// loin au-dessus du viewport et libère son ID.
|
|
256
|
+
const vh = window.innerHeight || 800;
|
|
257
|
+
const threshold = -vh * 3;
|
|
258
|
+
let best = null, bestBottom = Infinity;
|
|
259
|
+
document.querySelectorAll(`.${WRAP_CLASS}.${klass}`).forEach(wrap => {
|
|
260
|
+
if (wrap.getAttribute(A_CREATED) === null) return;
|
|
261
|
+
if (isFilled(wrap)) return;
|
|
262
|
+
const key = wrap.getAttribute(A_ANCHOR);
|
|
263
|
+
if (!key) return;
|
|
264
|
+
const colonIdx = key.indexOf(':');
|
|
265
|
+
const anchorId = key.slice(colonIdx + 1);
|
|
266
|
+
const cfg = KIND[klass];
|
|
267
|
+
if (!cfg) return;
|
|
268
|
+
try {
|
|
269
|
+
const found = document.querySelector(`${cfg.sel}[${cfg.anchorAttr}="${anchorId}"]`);
|
|
270
|
+
if (found?.isConnected) return; // ancre encore dans le DOM, pas orphelin
|
|
271
|
+
} catch (_) { return; }
|
|
272
|
+
try {
|
|
273
|
+
const rect = wrap.getBoundingClientRect();
|
|
274
|
+
if (rect.bottom > threshold) return;
|
|
275
|
+
if (rect.bottom < bestBottom) { bestBottom = rect.bottom; best = wrap; }
|
|
276
|
+
} catch (_) {}
|
|
277
|
+
});
|
|
278
|
+
if (!best) return null;
|
|
279
|
+
const id = parseInt(best.getAttribute(A_WRAPID), 10);
|
|
280
|
+
if (!Number.isFinite(id)) return null;
|
|
281
|
+
mutate(() => dropWrap(best));
|
|
282
|
+
return id;
|
|
283
|
+
}
|
|
284
|
+
|
|
233
285
|
// ── Wraps DOM ──────────────────────────────────────────────────────────────
|
|
234
286
|
|
|
235
287
|
function makeWrap(id, klass, key) {
|
|
@@ -315,8 +367,8 @@
|
|
|
315
367
|
const key = anchorKey(klass, el);
|
|
316
368
|
if (findWrap(key)) continue;
|
|
317
369
|
|
|
318
|
-
|
|
319
|
-
if (!id)
|
|
370
|
+
let id = pickId(poolKey);
|
|
371
|
+
if (!id) { id = recycleOrphanId(klass); if (!id) continue; }
|
|
320
372
|
|
|
321
373
|
const w = insertAfter(el, id, klass, key);
|
|
322
374
|
if (w) { observePh(id); inserted++; }
|