nodebb-plugin-ezoic-infinite 1.7.19 → 1.7.20
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 +17 -85
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* NodeBB Ezoic Infinite Ads — client.js
|
|
2
|
+
* NodeBB Ezoic Infinite Ads — client.js v26
|
|
3
3
|
*
|
|
4
4
|
* Historique des corrections majeures
|
|
5
5
|
* ────────────────────────────────────
|
|
6
6
|
* v18 Ancrage stable par data-pid/data-index au lieu d'ordinalMap fragile.
|
|
7
|
-
* Suppression du recyclage de wraps
|
|
7
|
+
* Suppression du recyclage de wraps. Cleanup complet navigation.
|
|
8
8
|
*
|
|
9
9
|
* v19 Intervalle global basé sur l'ordinal absolu (data-index) et non sur
|
|
10
10
|
* la position dans le batch courant.
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
* Fix unobserve(null) → corruption IO → pubads error au scroll retour.
|
|
17
17
|
* Fix TCF locator : MutationObserver recrée l'iframe si ajaxify la retire.
|
|
18
18
|
*
|
|
19
|
-
* v25
|
|
20
|
-
*
|
|
19
|
+
* v25 Table KIND unifiée avec baseTag + ordinalAttr.
|
|
20
|
+
* Fix scroll-up / virtualisation NodeBB :
|
|
21
21
|
* – pruneOrphans : PRUNE_STABLE_MS = 45 s, isFilled guard en premier.
|
|
22
22
|
* – decluster : isFilled en premier, A_CREATED grace period (FILL_GRACE_MS).
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
23
|
+
* Tentative recyclage d'id (v25) → cause exactement le même bug (wraps
|
|
24
|
+
* déplacés laissent les positions originales libres → réinjection en haut).
|
|
25
|
+
*
|
|
26
|
+
* 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.
|
|
30
30
|
*/
|
|
31
31
|
(function () {
|
|
32
32
|
'use strict';
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
const PRUNE_STABLE_MS = 45_000; // délai avant pruning (évite faux-orphelins scroll-up)
|
|
44
44
|
const FILL_GRACE_MS = 25_000; // fenêtre fill async Ezoic (SSP auction)
|
|
45
45
|
const EMPTY_CHECK_MS = 20_000; // délai collapse wrap vide post-show
|
|
46
|
-
const RECYCLE_THRESHOLD = 6; // nb de viewports au-dessus du seuil de recyclage
|
|
47
46
|
const MAX_INSERTS_RUN = 6;
|
|
48
47
|
const MAX_INFLIGHT = 4;
|
|
49
48
|
const SHOW_THROTTLE_MS = 900;
|
|
@@ -69,13 +68,11 @@
|
|
|
69
68
|
* data-pid posts / data-index topics / data-cid catégories
|
|
70
69
|
* ordinalAttr: attribut 0-based pour calcul de l'intervalle
|
|
71
70
|
* null → fallback positionnel (catégories)
|
|
72
|
-
* recyclable : autoriser le recyclage d'id quand le pool est épuisé
|
|
73
|
-
* false pour ezoic-ad-message (sauts visuels indésirables)
|
|
74
71
|
*/
|
|
75
72
|
const KIND = {
|
|
76
|
-
'ezoic-ad-message': { sel: SEL.post, baseTag: '', anchorAttr: 'data-pid', ordinalAttr: 'data-index'
|
|
77
|
-
'ezoic-ad-between': { sel: SEL.topic, baseTag: 'li', anchorAttr: 'data-index', ordinalAttr: 'data-index'
|
|
78
|
-
'ezoic-ad-categories': { sel: SEL.category, baseTag: 'li', anchorAttr: 'data-cid', ordinalAttr: null
|
|
73
|
+
'ezoic-ad-message': { sel: SEL.post, baseTag: '', anchorAttr: 'data-pid', ordinalAttr: 'data-index' },
|
|
74
|
+
'ezoic-ad-between': { sel: SEL.topic, baseTag: 'li', anchorAttr: 'data-index', ordinalAttr: 'data-index' },
|
|
75
|
+
'ezoic-ad-categories': { sel: SEL.category, baseTag: 'li', anchorAttr: 'data-cid', ordinalAttr: null },
|
|
79
76
|
};
|
|
80
77
|
|
|
81
78
|
// ── État ───────────────────────────────────────────────────────────────────
|
|
@@ -98,8 +95,6 @@
|
|
|
98
95
|
burstDeadline: 0,
|
|
99
96
|
burstCount: 0,
|
|
100
97
|
lastBurstTs: 0,
|
|
101
|
-
scrollDir: 1, // 1 = down, -1 = up
|
|
102
|
-
lastScrollY: 0,
|
|
103
98
|
};
|
|
104
99
|
|
|
105
100
|
let blockedUntil = 0;
|
|
@@ -223,49 +218,6 @@
|
|
|
223
218
|
return null;
|
|
224
219
|
}
|
|
225
220
|
|
|
226
|
-
// ── Recyclage d'id ─────────────────────────────────────────────────────────
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Sélectionne le wrap vide le plus éloigné au-dessus du viewport.
|
|
230
|
-
* Conditions : kindClass.recyclable = true, scroll vers le bas,
|
|
231
|
-
* wrap vide (non filled), rect.bottom < -(RECYCLE_THRESHOLD × vh).
|
|
232
|
-
*/
|
|
233
|
-
function pickRecyclableWrap(klass) {
|
|
234
|
-
if (!KIND[klass]?.recyclable) return null;
|
|
235
|
-
if (S.scrollDir < 0) return null;
|
|
236
|
-
|
|
237
|
-
const vh = Math.max(300, window.innerHeight || 800);
|
|
238
|
-
const threshold = -(vh * RECYCLE_THRESHOLD);
|
|
239
|
-
let best = null, bestBottom = Infinity;
|
|
240
|
-
|
|
241
|
-
for (const w of document.querySelectorAll(`.${WRAP_CLASS}.${klass}`)) {
|
|
242
|
-
if (!w.isConnected || isFilled(w)) continue;
|
|
243
|
-
try {
|
|
244
|
-
const rect = w.getBoundingClientRect();
|
|
245
|
-
if (rect.bottom < threshold && rect.bottom < bestBottom) {
|
|
246
|
-
bestBottom = rect.bottom;
|
|
247
|
-
best = w;
|
|
248
|
-
}
|
|
249
|
-
} catch (_) {}
|
|
250
|
-
}
|
|
251
|
-
return best;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Déplace un wrap recyclé vers sa nouvelle ancre el.
|
|
256
|
-
* Réinitialise A_ANCHOR, A_CREATED, supprime A_SHOWN.
|
|
257
|
-
*/
|
|
258
|
-
function moveWrapAfter(el, wrap, newKey) {
|
|
259
|
-
try {
|
|
260
|
-
if (!el || !wrap?.isConnected) return null;
|
|
261
|
-
wrap.setAttribute(A_ANCHOR, newKey);
|
|
262
|
-
wrap.setAttribute(A_CREATED, String(ts()));
|
|
263
|
-
wrap.removeAttribute(A_SHOWN);
|
|
264
|
-
mutate(() => el.insertAdjacentElement('afterend', wrap));
|
|
265
|
-
return wrap;
|
|
266
|
-
} catch (_) { return null; }
|
|
267
|
-
}
|
|
268
|
-
|
|
269
221
|
// ── Wraps DOM ──────────────────────────────────────────────────────────────
|
|
270
222
|
|
|
271
223
|
function makeWrap(id, klass, key) {
|
|
@@ -401,23 +353,11 @@
|
|
|
401
353
|
const key = anchorKey(klass, el);
|
|
402
354
|
if (findWrap(key)) continue;
|
|
403
355
|
|
|
404
|
-
// 1. Tentative pool normal
|
|
405
356
|
const id = pickId(poolKey);
|
|
406
|
-
if (id)
|
|
407
|
-
const w = insertAfter(el, id, klass, key);
|
|
408
|
-
if (w) { observePh(id); inserted++; }
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
357
|
+
if (!id) continue; // pool épuisé : on attend que pruneOrphans libère des ids
|
|
411
358
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (recyclable) {
|
|
415
|
-
const rid = parseInt(recyclable.getAttribute(A_WRAPID), 10);
|
|
416
|
-
const w = moveWrapAfter(el, recyclable, key);
|
|
417
|
-
if (w && Number.isFinite(rid)) { observePh(rid); inserted++; }
|
|
418
|
-
}
|
|
419
|
-
// Pool épuisé et pas de recyclage : on continue (items suivants peuvent
|
|
420
|
-
// avoir un wrap existant via findWrap, on ne break pas)
|
|
359
|
+
const w = insertAfter(el, id, klass, key);
|
|
360
|
+
if (w) { observePh(id); inserted++; }
|
|
421
361
|
}
|
|
422
362
|
return inserted;
|
|
423
363
|
}
|
|
@@ -743,13 +683,6 @@
|
|
|
743
683
|
function bindScroll() {
|
|
744
684
|
let ticking = false;
|
|
745
685
|
window.addEventListener('scroll', () => {
|
|
746
|
-
// Suivi direction du scroll (nécessaire pour le recyclage conditionnel)
|
|
747
|
-
try {
|
|
748
|
-
const y = window.scrollY || window.pageYOffset || 0;
|
|
749
|
-
const d = y - S.lastScrollY;
|
|
750
|
-
if (Math.abs(d) > 4) { S.scrollDir = d > 0 ? 1 : -1; S.lastScrollY = y; }
|
|
751
|
-
} catch (_) {}
|
|
752
|
-
|
|
753
686
|
if (ticking) return;
|
|
754
687
|
ticking = true;
|
|
755
688
|
requestAnimationFrame(() => { ticking = false; requestBurst(); });
|
|
@@ -759,7 +692,6 @@
|
|
|
759
692
|
// ── Boot ───────────────────────────────────────────────────────────────────
|
|
760
693
|
|
|
761
694
|
S.pageKey = pageKey();
|
|
762
|
-
try { S.lastScrollY = window.scrollY || window.pageYOffset || 0; } catch (_) {}
|
|
763
695
|
muteConsole();
|
|
764
696
|
ensureTcfLocator();
|
|
765
697
|
warmNetwork();
|