nodebb-plugin-ezoic-infinite 1.7.86 → 1.7.88

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.7.86",
3
+ "version": "1.7.88",
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
@@ -76,14 +76,8 @@
76
76
  const A_WRAPID = 'data-ezoic-wrapid'; // id Ezoic
77
77
  const A_CREATED = 'data-ezoic-created'; // timestamp création ms
78
78
  const A_SHOWN = 'data-ezoic-shown'; // timestamp dernier showAds ms
79
- const A_FILLED = 'data-ezoic-filled'; // timestamp premier fill réel
80
- const A_LAST_H = 'data-ezoic-lasth'; // dernière hauteur pub vue
81
79
 
82
80
  const EMPTY_CHECK_MS = 20_000; // délai avant collapse d'un wrap vide post-show
83
- const MIN_LIVE_AFTER_SHOW_MS = 15_000;
84
- const MIN_LIVE_AFTER_FILL_MS = 25_000;
85
- const KEEP_SHELL_AFTER_UNUSED_MS = 90_000;
86
- const MIN_SHELL_HEIGHT = 120;
87
81
  const MIN_PRUNE_AGE_MS = 8_000; // délai de grâce avant pruning (stabilisation DOM)
88
82
  const MAX_INSERTS_RUN = 6; // max insertions par appel runCore
89
83
  const MAX_INFLIGHT = 4; // max showAds() simultanés
@@ -133,8 +127,6 @@
133
127
  pending: [], // ids en attente de slot inflight
134
128
  pendingSet: new Set(),
135
129
  wrapByKey: new Map(), // anchorKey → wrap DOM node
136
- emptyChecks: new Map(), // id → [timerIds]
137
- fillObsById: new Map(), // id → MutationObserver
138
130
  runQueued: false,
139
131
  burstActive: false,
140
132
  burstDeadline: 0,
@@ -150,86 +142,6 @@
150
142
  const normBool = v => v === true || v === 'true' || v === 1 || v === '1' || v === 'on';
151
143
  const isFilled = n => !!(n?.querySelector?.('iframe, ins, img, video, [data-google-container-id]'));
152
144
 
153
-
154
- function clearEmptyChecks(id) {
155
- const timers = S.emptyChecks.get(id);
156
- if (!timers) return;
157
- for (const t of timers) { try { clearTimeout(t); } catch (_) {} }
158
- S.emptyChecks.delete(id);
159
- }
160
- function queueEmptyCheck(id, timerId) {
161
- const arr = S.emptyChecks.get(id) || [];
162
- arr.push(timerId);
163
- S.emptyChecks.set(id, arr);
164
- }
165
- function markFilledOnce(ph) {
166
- try {
167
- const wrap = ph?.closest?.(`.${WRAP_CLASS}`);
168
- if (!wrap || !isFilled(ph)) return;
169
- if (!wrap.getAttribute(A_FILLED)) wrap.setAttribute(A_FILLED, String(ts()));
170
- const h = Math.round(ph.getBoundingClientRect?.().height || ph.offsetHeight || 0);
171
- if (h > 0) wrap.setAttribute(A_LAST_H, String(h));
172
- } catch (_) {}
173
- }
174
- function shouldKeepShellAfterUnused(wrap) {
175
- try {
176
- const filledTs = parseInt(wrap?.getAttribute?.(A_FILLED) || '0', 10) || 0;
177
- return !!filledTs && (ts() - filledTs) < KEEP_SHELL_AFTER_UNUSED_MS;
178
- } catch (_) { return false; }
179
- }
180
- function applyUnusedShell(wrap, ph) {
181
- try {
182
- const lastH = parseInt(wrap.getAttribute(A_LAST_H) || '0', 10) || 0;
183
- const h = Math.max(MIN_SHELL_HEIGHT, lastH || 0);
184
- wrap.classList.remove('is-empty');
185
- wrap.classList.add('is-unused-shell');
186
- wrap.style.minHeight = `${h}px`;
187
- if (ph) ph.style.minHeight = `${h}px`;
188
- } catch (_) {}
189
- }
190
- function clearUnusedShell(wrap, ph) {
191
- try {
192
- wrap?.classList?.remove('is-unused-shell');
193
- wrap?.style?.removeProperty('min-height');
194
- ph?.style?.removeProperty('min-height');
195
- } catch (_) {}
196
- }
197
- function isProtectedFromDrop(wrap) {
198
- try {
199
- const now = ts();
200
- const shownTs = parseInt(wrap?.getAttribute?.(A_SHOWN) || '0', 10) || 0;
201
- const filledTs = parseInt(wrap?.getAttribute?.(A_FILLED) || '0', 10) || 0;
202
- if (shownTs && (now - shownTs) < MIN_LIVE_AFTER_SHOW_MS) return true;
203
- if (filledTs && (now - filledTs) < MIN_LIVE_AFTER_FILL_MS) return true;
204
- return false;
205
- } catch (_) { return false; }
206
- }
207
- function uncollapseIfFilled(ph) {
208
- try {
209
- const wrap = ph?.closest?.(`.${WRAP_CLASS}`);
210
- if (!wrap) return;
211
- if (isFilled(ph)) {
212
- wrap.classList.remove('is-empty');
213
- clearUnusedShell(wrap, ph);
214
- markFilledOnce(ph);
215
- }
216
- } catch (_) {}
217
- }
218
- function unwatchPlaceholderFill(id) {
219
- const obs = S.fillObsById.get(id);
220
- if (obs) { try { obs.disconnect(); } catch (_) {} S.fillObsById.delete(id); }
221
- }
222
- function watchPlaceholderFill(id) {
223
- try {
224
- const ph = document.getElementById(`${PH_PREFIX}${id}`);
225
- if (!ph || !ph.isConnected || S.fillObsById.has(id)) return;
226
- const obs = new MutationObserver(() => uncollapseIfFilled(ph));
227
- obs.observe(ph, { childList: true, subtree: true, attributes: true });
228
- S.fillObsById.set(id, obs);
229
- uncollapseIfFilled(ph);
230
- } catch (_) {}
231
- }
232
-
233
145
  function mutate(fn) {
234
146
  S.mutGuard++;
235
147
  try { fn(); } finally { S.mutGuard--; }
@@ -434,11 +346,8 @@ function watchPlaceholderFill(id) {
434
346
  best.setAttribute(A_CREATED, String(ts()));
435
347
  best.setAttribute(A_SHOWN, '0');
436
348
  best.classList.remove('is-empty');
437
- best.classList.remove('is-unused-shell');
438
- best.removeAttribute(A_FILLED);
439
- best.removeAttribute(A_LAST_H);
440
349
  const ph = best.querySelector(`#${PH_PREFIX}${id}`);
441
- if (ph) { ph.innerHTML = ''; ph.style.removeProperty('min-height'); }
350
+ if (ph) ph.innerHTML = '';
442
351
  targetEl.insertAdjacentElement('afterend', best);
443
352
  });
444
353
  if (oldKey && S.wrapByKey.get(oldKey) === best) S.wrapByKey.delete(oldKey);
@@ -479,24 +388,15 @@ function watchPlaceholderFill(id) {
479
388
  mutate(() => el.insertAdjacentElement('afterend', w));
480
389
  S.mountedIds.add(id);
481
390
  S.wrapByKey.set(key, w);
482
- watchPlaceholderFill(id);
483
391
  return w;
484
392
  }
485
393
 
486
394
  function dropWrap(w) {
487
395
  try {
488
396
  const ph = w.querySelector(`[id^="${PH_PREFIX}"]`);
489
- const id = parseInt(w.getAttribute(A_WRAPID), 10);
490
- if (isProtectedFromDrop(w) || shouldKeepShellAfterUnused(w)) {
491
- if (ph) applyUnusedShell(w, ph);
492
- return;
493
- }
494
397
  if (ph instanceof Element) S.io?.unobserve(ph);
495
- if (Number.isFinite(id)) {
496
- clearEmptyChecks(id);
497
- unwatchPlaceholderFill(id);
498
- S.mountedIds.delete(id);
499
- }
398
+ const id = parseInt(w.getAttribute(A_WRAPID), 10);
399
+ if (Number.isFinite(id)) S.mountedIds.delete(id);
500
400
  const key = w.getAttribute(A_ANCHOR);
501
401
  if (key && S.wrapByKey.get(key) === w) S.wrapByKey.delete(key);
502
402
  w.remove();
@@ -603,10 +503,7 @@ function watchPlaceholderFill(id) {
603
503
 
604
504
  function observePh(id) {
605
505
  const ph = document.getElementById(`${PH_PREFIX}${id}`);
606
- if (ph?.isConnected) {
607
- watchPlaceholderFill(id);
608
- try { getIO()?.observe(ph); } catch (_) {}
609
- }
506
+ if (ph?.isConnected) try { getIO()?.observe(ph); } catch (_) {}
610
507
  }
611
508
 
612
509
  function enqueueShow(id) {
@@ -645,8 +542,6 @@ function watchPlaceholderFill(id) {
645
542
  if (isBlocked()) { clearTimeout(timer); return release(); }
646
543
  const ph = document.getElementById(`${PH_PREFIX}${id}`);
647
544
  if (!ph?.isConnected || isFilled(ph)) { clearTimeout(timer); return release(); }
648
- clearEmptyChecks(id);
649
- try { const wrap = ph.closest?.(`.${WRAP_CLASS}`); wrap?.classList?.remove('is-empty'); clearUnusedShell(wrap, ph); } catch (_) {}
650
545
 
651
546
  const t = ts();
652
547
  if (t - (S.lastShow.get(id) ?? 0) < SHOW_THROTTLE_MS) { clearTimeout(timer); return release(); }
@@ -667,35 +562,15 @@ function watchPlaceholderFill(id) {
667
562
  }
668
563
 
669
564
  function scheduleEmptyCheck(id, showTs) {
670
- const runCheck = () => {
565
+ setTimeout(() => {
671
566
  try {
672
567
  const ph = document.getElementById(`${PH_PREFIX}${id}`);
673
568
  const wrap = ph?.closest?.(`.${WRAP_CLASS}`);
674
569
  if (!wrap || !ph?.isConnected) return;
675
570
  if (parseInt(wrap.getAttribute(A_SHOWN) || '0', 10) > showTs) return;
676
-
677
- if (isFilled(ph)) {
678
- wrap.classList.remove('is-empty');
679
- clearUnusedShell(wrap, ph);
680
- markFilledOnce(ph);
681
- return;
682
- }
683
-
684
- if (shouldKeepShellAfterUnused(wrap)) {
685
- applyUnusedShell(wrap, ph);
686
- return;
687
- }
688
-
689
- clearUnusedShell(wrap, ph);
690
- wrap.classList.add('is-empty');
571
+ wrap.classList.toggle('is-empty', !isFilled(ph));
691
572
  } catch (_) {}
692
- };
693
-
694
- clearEmptyChecks(id);
695
- [EMPTY_CHECK_MS, EMPTY_CHECK_MS + 5000, EMPTY_CHECK_MS + 15000].forEach(delay => {
696
- const t = setTimeout(runCheck, delay);
697
- queueEmptyCheck(id, t);
698
- });
573
+ }, EMPTY_CHECK_MS);
699
574
  }
700
575
 
701
576
  // ── Patch Ezoic showAds ────────────────────────────────────────────────────
@@ -821,8 +696,6 @@ function watchPlaceholderFill(id) {
821
696
  S.cursors = { topics: 0, posts: 0, categories: 0 };
822
697
  S.mountedIds.clear();
823
698
  S.lastShow.clear();
824
- for (const id of Array.from(S.emptyChecks.keys())) clearEmptyChecks(id);
825
- for (const id of Array.from(S.fillObsById.keys())) unwatchPlaceholderFill(id);
826
699
  S.wrapByKey.clear();
827
700
  S.inflight = 0;
828
701
  S.pending = [];
package/public/style.css CHANGED
@@ -8,7 +8,7 @@
8
8
  width: 100%;
9
9
  margin: 0 !important;
10
10
  padding: 0 !important;
11
- overflow: visible;
11
+ overflow: hidden;
12
12
  contain: layout style;
13
13
  }
14
14
 
@@ -76,40 +76,3 @@
76
76
  margin: 0 !important;
77
77
  padding: 0 !important;
78
78
  }
79
-
80
-
81
- /* Filet de sécurité : si un fill arrive tard alors que .is-empty est resté, ne pas écraser */
82
- .nodebb-ezoic-wrap.is-empty:has(iframe, ins, img, video, [data-google-container-id]) {
83
- height: auto !important;
84
- min-height: 1px !important;
85
- max-height: none !important;
86
- overflow: visible !important;
87
- }
88
-
89
- /* Shell conservé quand Ezoic repasse en "unused" après un affichage initial */
90
- .nodebb-ezoic-wrap.is-unused-shell {
91
- overflow: hidden !important;
92
- }
93
- .nodebb-ezoic-wrap.is-unused-shell > [id^="ezoic-pub-ad-placeholder-"] {
94
- display: block;
95
- width: 100%;
96
- }
97
-
98
- /* Responsive hardening conservatif (sans scale JS) */
99
- .nodebb-ezoic-wrap,
100
- .nodebb-ezoic-wrap > [id^="ezoic-pub-ad-placeholder-"] {
101
- max-width: 100%;
102
- }
103
- .nodebb-ezoic-wrap .ezoic-ad,
104
- .nodebb-ezoic-wrap span.ezoic-ad {
105
- max-width: 100% !important;
106
- min-width: 0 !important;
107
- box-sizing: border-box !important;
108
- }
109
-
110
- /* Neutralisation sticky plus défensive dans les wraps du plugin */
111
- .nodebb-ezoic-wrap .ezads-sticky-intradiv,
112
- .nodebb-ezoic-wrap [style*="position: sticky"] {
113
- position: static !important;
114
- top: auto !important;
115
- }