nodebb-plugin-ezoic-infinite 1.7.1 → 1.7.3
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 +96 -13
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -261,11 +261,45 @@
|
|
|
261
261
|
return w;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Retire proprement un wrap du DOM.
|
|
266
|
+
*
|
|
267
|
+
* Sans précaution, retirer un wrap contenant un player vidéo Ezoic (wyvern.js)
|
|
268
|
+
* déclenche des erreurs async sur des nœuds détachés :
|
|
269
|
+
* "Cannot read properties of null (reading 'paused')"
|
|
270
|
+
* "Cannot read properties of null (reading 'offsetWidth')"
|
|
271
|
+
* "Invalid target for null#trigger / null#on"
|
|
272
|
+
*
|
|
273
|
+
* On pause les media et on tente de notifier l'API wyvern avant remove().
|
|
274
|
+
*/
|
|
264
275
|
function dropWrap(w) {
|
|
265
276
|
try {
|
|
277
|
+
// 1. Pause tous les media actifs avant détachement
|
|
278
|
+
try {
|
|
279
|
+
w.querySelectorAll('video, audio').forEach(m => {
|
|
280
|
+
try { if (!m.paused) m.pause(); } catch (_) {}
|
|
281
|
+
});
|
|
282
|
+
} catch (_) {}
|
|
283
|
+
|
|
284
|
+
// 2. Notifier l'API wyvern si disponible
|
|
285
|
+
try {
|
|
286
|
+
if (window.wyvern && typeof window.wyvern === 'object') {
|
|
287
|
+
w.querySelectorAll('[id^="ezoic-"],[class*="wyvern"],[class*="ezoic-video"]')
|
|
288
|
+
.forEach(n => { try { window.wyvern.destroy?.(n); } catch (_) {} });
|
|
289
|
+
}
|
|
290
|
+
} catch (_) {}
|
|
291
|
+
|
|
292
|
+
// 3. Unobserve (guard instanceof Element — unobserve(null) corrompt l'IO)
|
|
293
|
+
try {
|
|
294
|
+
const ph = w.querySelector(`[id^="${PH_PREFIX}"]`);
|
|
295
|
+
if (ph instanceof Element) S.io?.unobserve(ph);
|
|
296
|
+
} catch (_) {}
|
|
297
|
+
|
|
298
|
+
// 4. Libérer l'id du pool
|
|
266
299
|
const id = parseInt(w.getAttribute(A_WRAPID), 10);
|
|
267
300
|
if (Number.isFinite(id)) S.mountedIds.delete(id);
|
|
268
|
-
|
|
301
|
+
|
|
302
|
+
// 5. Retrait DOM
|
|
269
303
|
w.remove();
|
|
270
304
|
} catch (_) {}
|
|
271
305
|
}
|
|
@@ -283,17 +317,31 @@
|
|
|
283
317
|
*
|
|
284
318
|
* On ne prune pas les wraps < MIN_PRUNE_AGE_MS (DOM pas encore stabilisé).
|
|
285
319
|
*/
|
|
320
|
+
/**
|
|
321
|
+
* Supprime les wraps dont l'élément-ancre n'est plus dans le DOM.
|
|
322
|
+
*
|
|
323
|
+
* Règle stricte : on ne supprime JAMAIS un wrap rempli (filled).
|
|
324
|
+
* - Il peut contenir un player wyvern actif → .remove() déclenche des
|
|
325
|
+
* erreurs async ("Cannot read 'paused'", "offsetWidth", "getChild"…).
|
|
326
|
+
* - Le post-ancre peut être temporairement virtualisé par NodeBB puis
|
|
327
|
+
* revenir — dans ce cas le wrap filled doit rester en place.
|
|
328
|
+
*
|
|
329
|
+
* Seuls les wraps VIDES dont l'ancre a disparu sont supprimés.
|
|
330
|
+
*/
|
|
286
331
|
function pruneOrphans(klass) {
|
|
287
332
|
const meta = KIND[klass];
|
|
288
333
|
if (!meta) return;
|
|
289
334
|
|
|
290
|
-
const baseTag = meta.sel.split('[')[0];
|
|
335
|
+
const baseTag = meta.sel.split('[')[0];
|
|
291
336
|
|
|
292
337
|
document.querySelectorAll(`.${WRAP_CLASS}.${klass}`).forEach(w => {
|
|
293
338
|
if (ts() - parseInt(w.getAttribute(A_CREATED) || '0', 10) < MIN_PRUNE_AGE_MS) return;
|
|
294
339
|
|
|
340
|
+
// Ne jamais retirer un wrap qui contient du contenu (player potentiellement actif)
|
|
341
|
+
if (isFilled(w)) return;
|
|
342
|
+
|
|
295
343
|
const key = w.getAttribute(A_ANCHOR) ?? '';
|
|
296
|
-
const sid = key.slice(klass.length + 1);
|
|
344
|
+
const sid = key.slice(klass.length + 1);
|
|
297
345
|
if (!sid) { mutate(() => dropWrap(w)); return; }
|
|
298
346
|
|
|
299
347
|
const anchorEl = document.querySelector(
|
|
@@ -308,11 +356,12 @@
|
|
|
308
356
|
/**
|
|
309
357
|
* Deux wraps adjacents = situation anormale → supprimer le moins prioritaire.
|
|
310
358
|
* Priorité : filled > en grâce (fill en cours) > vide.
|
|
311
|
-
*
|
|
359
|
+
*
|
|
360
|
+
* Règle de sécurité wyvern : on ne supprime JAMAIS un wrap rempli.
|
|
361
|
+
* Si les deux wraps adjacents sont remplis, on laisse en place (edge case rare).
|
|
312
362
|
*/
|
|
313
363
|
function decluster(klass) {
|
|
314
364
|
for (const w of document.querySelectorAll(`.${WRAP_CLASS}.${klass}`)) {
|
|
315
|
-
// Grace sur le wrap courant : on le saute entièrement
|
|
316
365
|
const wShown = parseInt(w.getAttribute(A_SHOWN) || '0', 10);
|
|
317
366
|
if (wShown && ts() - wShown < FILL_GRACE_MS) continue;
|
|
318
367
|
|
|
@@ -321,10 +370,16 @@
|
|
|
321
370
|
if (!prev.classList?.contains(WRAP_CLASS)) { prev = prev.previousElementSibling; continue; }
|
|
322
371
|
|
|
323
372
|
const pShown = parseInt(prev.getAttribute(A_SHOWN) || '0', 10);
|
|
324
|
-
if (pShown && ts() - pShown < FILL_GRACE_MS) break;
|
|
373
|
+
if (pShown && ts() - pShown < FILL_GRACE_MS) break;
|
|
374
|
+
|
|
375
|
+
const wFilled = isFilled(w);
|
|
376
|
+
const pFilled = isFilled(prev);
|
|
325
377
|
|
|
326
|
-
|
|
327
|
-
|
|
378
|
+
// Ne jamais retirer un wrap rempli (player actif potentiel)
|
|
379
|
+
if (!wFilled && !pFilled) mutate(() => dropWrap(w)); // les deux vides → retirer le courant
|
|
380
|
+
else if (!wFilled && pFilled) mutate(() => dropWrap(w)); // courant vide, précédent rempli → retirer le courant
|
|
381
|
+
else if ( wFilled && !pFilled) mutate(() => dropWrap(prev)); // courant rempli, précédent vide → retirer le précédent
|
|
382
|
+
// les deux remplis → rien (on ne touche pas)
|
|
328
383
|
break;
|
|
329
384
|
}
|
|
330
385
|
}
|
|
@@ -389,7 +444,7 @@
|
|
|
389
444
|
S.io = new IntersectionObserver(entries => {
|
|
390
445
|
for (const e of entries) {
|
|
391
446
|
if (!e.isIntersecting) continue;
|
|
392
|
-
S.io?.unobserve(e.target);
|
|
447
|
+
if (e.target instanceof Element) S.io?.unobserve(e.target);
|
|
393
448
|
const id = parseInt(e.target.getAttribute('data-ezoic-id'), 10);
|
|
394
449
|
if (Number.isFinite(id) && id > 0) enqueueShow(id);
|
|
395
450
|
}
|
|
@@ -586,6 +641,16 @@
|
|
|
586
641
|
|
|
587
642
|
function cleanup() {
|
|
588
643
|
blockedUntil = ts() + 1500;
|
|
644
|
+
|
|
645
|
+
// Pause tous les media dans nos wraps AVANT de les retirer du DOM.
|
|
646
|
+
// Empêche wyvern.js de continuer d'exécuter ses callbacks async sur des
|
|
647
|
+
// nœuds détachés (erreurs "Cannot read 'paused'", "offsetWidth"…).
|
|
648
|
+
try {
|
|
649
|
+
document.querySelectorAll(`.${WRAP_CLASS} video, .${WRAP_CLASS} audio`).forEach(m => {
|
|
650
|
+
try { if (!m.paused) m.pause(); } catch (_) {}
|
|
651
|
+
});
|
|
652
|
+
} catch (_) {}
|
|
653
|
+
|
|
589
654
|
mutate(() => document.querySelectorAll(`.${WRAP_CLASS}`).forEach(dropWrap));
|
|
590
655
|
S.cfg = null;
|
|
591
656
|
S.pools = { topics: [], posts: [], categories: [] };
|
|
@@ -637,12 +702,30 @@
|
|
|
637
702
|
}
|
|
638
703
|
|
|
639
704
|
function ensureTcfLocator() {
|
|
705
|
+
// Le CMP utilise une iframe nommée __tcfapiLocator pour router les
|
|
706
|
+
// postMessage TCF. En navigation ajaxify, NodeBB peut retirer cette
|
|
707
|
+
// iframe du DOM (vidage partiel du body), ce qui provoque :
|
|
708
|
+
// "Cannot read properties of null (reading 'postMessage')"
|
|
709
|
+
// "Cannot set properties of null (setting 'addtlConsent')"
|
|
710
|
+
// Solution : la recrée immédiatement si elle disparaît, via un observer.
|
|
640
711
|
try {
|
|
641
712
|
if (!window.__tcfapi && !window.__cmp) return;
|
|
642
|
-
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
|
|
713
|
+
|
|
714
|
+
const inject = () => {
|
|
715
|
+
if (document.getElementById('__tcfapiLocator')) return;
|
|
716
|
+
const f = document.createElement('iframe');
|
|
717
|
+
f.style.display = 'none'; f.id = f.name = '__tcfapiLocator';
|
|
718
|
+
(document.body || document.documentElement).appendChild(f);
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
inject();
|
|
722
|
+
|
|
723
|
+
// Observer dédié — si quelqu'un retire l'iframe, on la remet.
|
|
724
|
+
if (!window.__nbbTcfObs) {
|
|
725
|
+
window.__nbbTcfObs = new MutationObserver(() => inject());
|
|
726
|
+
window.__nbbTcfObs.observe(document.documentElement,
|
|
727
|
+
{ childList: true, subtree: true });
|
|
728
|
+
}
|
|
646
729
|
} catch (_) {}
|
|
647
730
|
}
|
|
648
731
|
|