nodebb-plugin-ezoic-infinite 1.7.95 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/client.js +64 -51
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.7.95",
3
+ "version": "1.7.96",
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
@@ -79,14 +79,15 @@
79
79
 
80
80
  const EMPTY_CHECK_MS = 20_000; // délai avant collapse d'un wrap vide post-show
81
81
  const MIN_PRUNE_AGE_MS = 8_000; // délai de grâce avant pruning (stabilisation DOM)
82
- const MAX_INSERTS_RUN = 6; // max insertions par appel runCore
82
+ const MAX_INSERTS_RUN = 10; // max insertions par appel runCore (plus réactif)
83
83
  const MAX_INFLIGHT = 4; // max showAds() simultanés
84
- const SHOW_THROTTLE_MS = 900; // anti-spam showAds() par id
85
- const BURST_COOLDOWN_MS = 200; // délai min entre deux déclenchements de burst
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
86
86
 
87
87
  // Marges IO larges et fixes — observer créé une seule fois au boot
88
88
  const IO_MARGIN_DESKTOP = '2500px 0px 2500px 0px';
89
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
90
91
 
91
92
  const SEL = {
92
93
  post: '[component="post"][data-pid]',
@@ -132,8 +133,8 @@
132
133
  burstDeadline: 0,
133
134
  burstCount: 0,
134
135
  lastBurstTs: 0,
136
+ scrollDir: 1,
135
137
  lastScrollY: 0,
136
- scrollDir: 1, // 1=down, -1=up
137
138
  };
138
139
 
139
140
  let blockedUntil = 0;
@@ -315,56 +316,58 @@
315
316
  typeof ez?.define !== 'function' ||
316
317
  typeof ez?.displayMore !== 'function') return null;
317
318
 
318
- const vh = window.innerHeight || 800;
319
- const targetRect = targetEl?.getBoundingClientRect?.() || { top: vh, bottom: vh };
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)
320
323
 
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
324
+ let prefEmpty = null, prefEmptyScore = -Infinity;
325
+ let prefFilled = null, prefFilledScore = -Infinity;
326
+ let altEmpty = null, altEmptyScore = -Infinity;
327
+ let altFilled = null, altFilledScore = -Infinity;
327
328
 
328
- let aboveEmpty = null, aboveEmptyBottom = Infinity;
329
- let aboveFilled = null, aboveFilledBottom = Infinity;
330
- let belowEmpty = null, belowEmptyTop = -Infinity;
331
- let belowFilled = null, belowFilledTop = -Infinity;
332
-
333
- document.querySelectorAll(`.${WRAP_CLASS}.${klass}`).forEach(wrap => {
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;
334
332
  try {
335
- if (!wrap?.isConnected) return;
336
333
  const rect = wrap.getBoundingClientRect();
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;
337
347
 
338
- if (rect.bottom < aboveThreshold) {
339
- if (!isFilled(wrap)) {
340
- if (rect.bottom < aboveEmptyBottom) { aboveEmptyBottom = rect.bottom; aboveEmpty = wrap; }
348
+ const filled = isFilled(wrap);
349
+ if (preferred) {
350
+ if (!filled) {
351
+ if (score > prefEmptyScore) { prefEmptyScore = score; prefEmpty = wrap; }
341
352
  } else {
342
- if (rect.bottom < aboveFilledBottom) { aboveFilledBottom = rect.bottom; aboveFilled = wrap; }
353
+ if (score > prefFilledScore) { prefFilledScore = score; prefFilled = wrap; }
343
354
  }
344
- return;
345
- }
346
-
347
- if (rect.top > belowThreshold) {
348
- if (!isFilled(wrap)) {
349
- if (rect.top > belowEmptyTop) { belowEmptyTop = rect.top; belowEmpty = wrap; }
355
+ } else {
356
+ if (!filled) {
357
+ if (score > altEmptyScore) { altEmptyScore = score; altEmpty = wrap; }
350
358
  } else {
351
- if (rect.top > belowFilledTop) { belowFilledTop = rect.top; belowFilled = wrap; }
359
+ if (score > altFilledScore) { altFilledScore = score; altFilled = wrap; }
352
360
  }
353
361
  }
354
362
  } catch (_) {}
355
- });
363
+ }
356
364
 
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());
365
+ const best = prefEmpty ?? prefFilled ?? altEmpty ?? altFilled;
361
366
  if (!best) return null;
362
367
  const id = parseInt(best.getAttribute(A_WRAPID), 10);
363
368
  if (!Number.isFinite(id)) return null;
364
369
 
365
370
  const oldKey = best.getAttribute(A_ANCHOR);
366
- // Neutraliser l'IO sur ce wrap avant déplacement — évite un showAds
367
- // parasite si le nœud était encore dans la zone IO_MARGIN.
368
371
  try { const ph = best.querySelector(`#${PH_PREFIX}${id}`); if (ph) S.io?.unobserve(ph); } catch (_) {}
369
372
  mutate(() => {
370
373
  best.setAttribute(A_ANCHOR, newKey);
@@ -378,9 +381,8 @@
378
381
  if (oldKey && S.wrapByKey.get(oldKey) === best) S.wrapByKey.delete(oldKey);
379
382
  S.wrapByKey.set(newKey, best);
380
383
 
381
- // Délais requis : destroyPlaceholders est asynchrone en interne
382
- const doDestroy = () => { try { ez.destroyPlaceholders([id]); } catch (_) {} setTimeout(doDefine, 300); };
383
- const doDefine = () => { try { ez.define([id]); } catch (_) {} setTimeout(doDisplay, 300); };
384
+ const doDestroy = () => { try { ez.destroyPlaceholders([id]); } catch (_) {} setTimeout(doDefine, 220); };
385
+ const doDefine = () => { try { ez.define([id]); } catch (_) {} setTimeout(doDisplay,220); };
384
386
  const doDisplay = () => { try { ez.displayMore([id]); } catch (_) {} };
385
387
  try { (typeof ez.cmd?.push === 'function') ? ez.cmd.push(doDestroy) : doDestroy(); } catch (_) {}
386
388
 
@@ -528,7 +530,16 @@
528
530
 
529
531
  function observePh(id) {
530
532
  const ph = document.getElementById(`${PH_PREFIX}${id}`);
531
- if (ph?.isConnected) try { getIO()?.observe(ph); } catch (_) {}
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 (_) {}
532
543
  }
533
544
 
534
545
  function enqueueShow(id) {
@@ -579,7 +590,7 @@
579
590
  const doShow = () => {
580
591
  try { ez.showAds(id); } catch (_) {}
581
592
  scheduleEmptyCheck(id, t);
582
- setTimeout(() => { clearTimeout(timer); release(); }, 700);
593
+ setTimeout(() => { clearTimeout(timer); release(); }, 350);
583
594
  };
584
595
  Array.isArray(ez.cmd) ? ez.cmd.push(doShow) : doShow();
585
596
  } catch (_) { clearTimeout(timer); release(); }
@@ -704,7 +715,7 @@
704
715
  S.burstCount++;
705
716
  scheduleRun(n => {
706
717
  if (!n && !S.pending.length) { S.burstActive = false; return; }
707
- setTimeout(step, n > 0 ? 150 : 300);
718
+ setTimeout(step, n > 0 ? 80 : 180);
708
719
  });
709
720
  };
710
721
  step();
@@ -847,22 +858,24 @@
847
858
  function bindScroll() {
848
859
  let ticking = false;
849
860
  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 (_) {}
856
861
  if (ticking) return;
857
862
  ticking = true;
858
- requestAnimationFrame(() => { ticking = false; requestBurst(); });
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
+ });
859
872
  }, { passive: true });
860
873
  }
861
874
 
862
875
  // ── Boot ───────────────────────────────────────────────────────────────────
863
876
 
864
877
  S.pageKey = pageKey();
865
- try { S.lastScrollY = window.scrollY || window.pageYOffset || 0; } catch (_) { S.lastScrollY = 0; }
878
+ try { S.lastScrollY = window.scrollY || window.pageYOffset || 0; } catch (_) {}
866
879
  muteConsole();
867
880
  ensureTcfLocator();
868
881
  warmNetwork();