nodebb-plugin-ezoic-infinite 1.7.20 → 1.7.21

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/client.js +21 -69
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.7.20",
3
+ "version": "1.7.21",
4
4
  "description": "Production-ready Ezoic infinite ads integration for NodeBB 4.x",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * NodeBB Ezoic Infinite Ads — client.js v26
2
+ * NodeBB Ezoic Infinite Ads — client.js v28
3
3
  *
4
4
  * Historique des corrections majeures
5
5
  * ────────────────────────────────────
@@ -18,15 +18,21 @@
18
18
  *
19
19
  * v25 Table KIND unifiée avec baseTag + ordinalAttr.
20
20
  * Fix scroll-up / virtualisation NodeBB :
21
- * – pruneOrphans : PRUNE_STABLE_MS = 45 s, isFilled guard en premier.
22
21
  * – decluster : isFilled en premier, A_CREATED grace period (FILL_GRACE_MS).
23
22
  * Tentative recyclage d'id (v25) → cause exactement le même bug (wraps
24
23
  * déplacés laissent les positions originales libres → réinjection en haut).
25
24
  *
26
25
  * v26 Suppression définitive du recyclage d'id.
27
- * Pool épuisé = on attend que pruneOrphans libère des ids (> 45 s hors DOM).
28
- * Suppression de scrollDir, pickRecyclableWrap, moveWrapAfter.
29
- * KIND simplifié : retrait du flag recyclable inutile.
26
+ * KIND simplifié.
27
+ *
28
+ * v27 pruneOrphans supprimé (faux-orphelins sur virtualisation NodeBB).
29
+ *
30
+ * v28 decluster supprimé.
31
+ * Analyse : decluster appelait dropWrap() qui faisait S.mountedIds.delete(id).
32
+ * Un wrap vide adjacent à un autre wrap → supprimé → id libéré → réinjecté
33
+ * en haut au prochain scroll. Exactement le bug observé.
34
+ * Les deux mécanismes de nettoyage actif (pruneOrphans + decluster) sont
35
+ * maintenant retirés. Seul cleanup() à la navigation peut supprimer des wraps.
30
36
  */
31
37
  (function () {
32
38
  'use strict';
@@ -40,8 +46,6 @@
40
46
  const A_CREATED = 'data-ezoic-created'; // timestamp création ms
41
47
  const A_SHOWN = 'data-ezoic-shown'; // timestamp dernier showAds ms
42
48
 
43
- const PRUNE_STABLE_MS = 45_000; // délai avant pruning (évite faux-orphelins scroll-up)
44
- const FILL_GRACE_MS = 25_000; // fenêtre fill async Ezoic (SSP auction)
45
49
  const EMPTY_CHECK_MS = 20_000; // délai collapse wrap vide post-show
46
50
  const MAX_INSERTS_RUN = 6;
47
51
  const MAX_INFLIGHT = 4;
@@ -255,66 +259,16 @@
255
259
  } catch (_) {}
256
260
  }
257
261
 
258
- // ── Prune ──────────────────────────────────────────────────────────────────
262
+ // ── Prune : désactivé ─────────────────────────────────────────────────────
263
+ //
264
+ // pruneOrphans() a été supprimé car il causait le bug "pubs en haut".
265
+ // NodeBB virtualise les posts hors viewport → les ancres disparaissent du DOM
266
+ // temporairement → pruneOrphans supprimait les wraps → scroll retour → les
267
+ // ancres revenaient → injectBetween réinjectait tout en haut.
268
+ //
269
+ // Les wraps ne sont supprimés que par cleanup() à chaque navigation ajaxify.
270
+ // decluster() et pruneOrphans() sont désactivés — voir v28.
259
271
 
260
- /**
261
- * Supprime les wraps VIDES dont l'ancre a disparu du DOM.
262
- *
263
- * isFilled en premier : un wrap rempli n'est JAMAIS supprimé.
264
- * PRUNE_STABLE_MS (45 s) : pendant cette fenêtre une ancre absente est
265
- * considérée comme virtualisée par NodeBB (scroll up), pas comme orphelin réel.
266
- */
267
- function pruneOrphans(klass) {
268
- const meta = KIND[klass];
269
- if (!meta) return;
270
-
271
- for (const w of document.querySelectorAll(`.${WRAP_CLASS}.${klass}`)) {
272
- if (isFilled(w)) continue;
273
- if (ts() - parseInt(w.getAttribute(A_CREATED) || '0', 10) < PRUNE_STABLE_MS) continue;
274
-
275
- const key = w.getAttribute(A_ANCHOR) ?? '';
276
- const sid = key.slice(klass.length + 1);
277
- if (!sid) { mutate(() => dropWrap(w)); continue; }
278
-
279
- const sel = `${meta.baseTag}[${meta.anchorAttr}="${sid.replace(/"/g, '\\"')}"]`;
280
- const anchorEl = document.querySelector(sel);
281
- if (!anchorEl || !anchorEl.isConnected) mutate(() => dropWrap(w));
282
- }
283
- }
284
-
285
- // ── Decluster ──────────────────────────────────────────────────────────────
286
-
287
- /**
288
- * Deux wraps adjacents → supprimer le courant s'il est vide et hors grâce.
289
- * Guards dans l'ordre :
290
- * 1. isFilled(w) → jamais toucher un wrap rempli
291
- * 2. A_CREATED < FILL_GRACE → wrap trop récent (pas encore showAds'd)
292
- * 3. A_SHOWN grace → fill en cours
293
- * 4. isFilled(prev) → voisin rempli, intouchable → break
294
- * 5. A_CREATED prev grace → voisin trop récent → break
295
- * 6. A_SHOWN prev grace → break
296
- * → les deux vides et hors grâce : supprimer le courant
297
- */
298
- function decluster(klass) {
299
- for (const w of document.querySelectorAll(`.${WRAP_CLASS}.${klass}`)) {
300
- if (isFilled(w)) continue;
301
- if (ts() - parseInt(w.getAttribute(A_CREATED) || '0', 10) < FILL_GRACE_MS) continue;
302
- const wShown = parseInt(w.getAttribute(A_SHOWN) || '0', 10);
303
- if (wShown && ts() - wShown < FILL_GRACE_MS) continue;
304
-
305
- let prev = w.previousElementSibling, steps = 0;
306
- while (prev && steps++ < 3) {
307
- if (!prev.classList?.contains(WRAP_CLASS)) { prev = prev.previousElementSibling; continue; }
308
- if (isFilled(prev)) break;
309
- if (ts() - parseInt(prev.getAttribute(A_CREATED) || '0', 10) < FILL_GRACE_MS) break;
310
- const pShown = parseInt(prev.getAttribute(A_SHOWN) || '0', 10);
311
- if (pShown && ts() - pShown < FILL_GRACE_MS) break;
312
-
313
- mutate(() => dropWrap(w));
314
- break;
315
- }
316
- }
317
- }
318
272
 
319
273
  // ── Injection ──────────────────────────────────────────────────────────────
320
274
 
@@ -354,7 +308,7 @@
354
308
  if (findWrap(key)) continue;
355
309
 
356
310
  const id = pickId(poolKey);
357
- if (!id) continue; // pool épuisé : on attend que pruneOrphans libère des ids
311
+ if (!id) continue; // pool épuisé : tous les ids sont montés, on passe au suivant
358
312
 
359
313
  const w = insertAfter(el, id, klass, key);
360
314
  if (w) { observePh(id); inserted++; }
@@ -498,9 +452,7 @@
498
452
  const exec = (klass, getItems, cfgEnable, cfgInterval, cfgShowFirst, poolKey) => {
499
453
  if (!normBool(cfgEnable)) return 0;
500
454
  const interval = Math.max(1, parseInt(cfgInterval, 10) || 3);
501
- pruneOrphans(klass);
502
455
  const n = injectBetween(klass, getItems(), interval, normBool(cfgShowFirst), poolKey);
503
- if (n) decluster(klass);
504
456
  return n;
505
457
  };
506
458