nodebb-plugin-ezoic-infinite 1.4.7 → 1.4.8

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 +59 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.4.7",
3
+ "version": "1.4.8",
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
@@ -189,6 +189,8 @@
189
189
  function resetPlaceholderInWrap(wrap, id) {
190
190
  try {
191
191
  if (!wrap) return;
192
+ try { wrap.removeAttribute('data-ezoic-filled'); } catch (e) {}
193
+ try { if (wrap.__ezoicFillObs) { wrap.__ezoicFillObs.disconnect(); wrap.__ezoicFillObs = null; } } catch (e) {}
192
194
  const old = wrap.querySelector && wrap.querySelector(`#${PLACEHOLDER_PREFIX}${id}`);
193
195
  if (old) old.remove();
194
196
  // Remove any leftover markup inside wrapper
@@ -234,6 +236,7 @@
234
236
  if (findWrap(kindClass, afterPos)) return null;
235
237
  const wrap = buildWrap(id, kindClass, afterPos);
236
238
  target.insertAdjacentElement('afterend', wrap);
239
+ attachFillObserver(wrap, id);
237
240
  return wrap;
238
241
  }
239
242
 
@@ -285,17 +288,59 @@
285
288
  }
286
289
 
287
290
 
291
+
292
+ function markFilled(wrap) {
293
+ try {
294
+ if (!wrap) return;
295
+ try { wrap.removeAttribute('data-ezoic-filled'); } catch (e) {}
296
+ try { if (wrap.__ezoicFillObs) { wrap.__ezoicFillObs.disconnect(); wrap.__ezoicFillObs = null; } } catch (e) {}
297
+ wrap.setAttribute('data-ezoic-filled', '1');
298
+ } catch (e) {}
299
+ }
300
+
301
+ function isWrapMarkedFilled(wrap) {
302
+ try { return wrap && wrap.getAttribute && wrap.getAttribute('data-ezoic-filled') === '1'; } catch (e) { return false; }
303
+ }
304
+
305
+ function attachFillObserver(wrap, id) {
306
+ try {
307
+ const ph = wrap && wrap.querySelector && wrap.querySelector(`#${PLACEHOLDER_PREFIX}${id}`);
308
+ if (!ph) return;
309
+ // Already filled?
310
+ if (ph.childNodes && ph.childNodes.length > 0) {
311
+ markFilled(wrap);
312
+ state.definedIds && state.definedIds.add(id);
313
+ return;
314
+ }
315
+ const obs = new MutationObserver(() => {
316
+ if (ph.childNodes && ph.childNodes.length > 0) {
317
+ markFilled(wrap);
318
+ try { state.definedIds && state.definedIds.add(id); } catch (e) {}
319
+ try { obs.disconnect(); } catch (e) {}
320
+ }
321
+ });
322
+ obs.observe(ph, { childList: true, subtree: true });
323
+ // Keep a weak reference on the wrapper so we can disconnect on recycle/remove
324
+ wrap.__ezoicFillObs = obs;
325
+ } catch (e) {}
326
+ }
327
+
288
328
  function isPlaceholderFilled(id) {
289
329
  const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
290
330
  if (!ph || !ph.isConnected) return false;
291
331
 
292
- // Ezoic typically injects content inside the placeholder node.
332
+ const wrap = ph.parentElement;
333
+ if (wrap && isWrapMarkedFilled(wrap)) return true;
334
+
293
335
  const filled = !!(ph.childNodes && ph.childNodes.length > 0);
294
336
  if (filled) {
295
337
  try { state.definedIds && state.definedIds.add(id); } catch (e) {}
338
+ try { markFilled(wrap); } catch (e) {}
296
339
  }
297
340
  return filled;
298
341
  }
342
+ return filled;
343
+ }
299
344
 
300
345
  function scheduleRefill(delay = 350) {
301
346
  clearTimeout(state.retryTimer);
@@ -329,12 +374,12 @@
329
374
  const phNow = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
330
375
  const wrapNow = phNow && phNow.parentElement;
331
376
  // If Ezoic already defined this id earlier but the placeholder is empty now, we must destroy+reset before re-showAds.
332
- if (wrapNow && wrapNow.isConnected && state.definedIds && state.definedIds.has(id) && !isPlaceholderFilled(id)) {
377
+ if (wrapNow && wrapNow.isConnected && state.definedIds && state.definedIds.has(id) && !isPlaceholderFilled(id) && !isWrapMarkedFilled(wrapNow)) {
333
378
  destroyPlaceholderIds([id]);
334
379
  resetPlaceholderInWrap(wrapNow, id);
335
380
  }
336
381
  // If this id was previously attempted and still empty, force a full reset before re-requesting.
337
- if (attempts > 0 && wrapNow && wrapNow.isConnected && !isPlaceholderFilled(id)) {
382
+ if (attempts > 0 && wrapNow && wrapNow.isConnected && !isPlaceholderFilled(id) && !isWrapMarkedFilled(wrapNow)) {
338
383
  destroyPlaceholderIds([id]);
339
384
  resetPlaceholderInWrap(wrapNow, id);
340
385
  }
@@ -360,6 +405,11 @@
360
405
  state.retryById.delete(id);
361
406
  continue;
362
407
  }
408
+ // If wrapper was marked filled, don't try to refill even if placeholder temporarily appears empty.
409
+ if (isWrapMarkedFilled(wrap)) {
410
+ state.retryById.delete(id);
411
+ continue;
412
+ }
363
413
 
364
414
  const tries = (state.retryById.get(id) || 0);
365
415
  if (tries >= 8) { state.badIds && state.badIds.add(id); continue; }
@@ -512,9 +562,12 @@
512
562
  if (state.definedIds && state.definedIds.has(id)) {
513
563
  destroyPlaceholderIds([id]);
514
564
  }
515
- wrap = pick.recycled.wrap;
516
- if (!moveWrapAfter(wrap, el, kindClass, afterPos)) continue;
517
- resetPlaceholderInWrap(wrap, id);
565
+ // Remove the old wrapper entirely, then create a fresh wrapper at the new position (same id)
566
+ const oldWrap = pick.recycled.wrap;
567
+ try { if (oldWrap && oldWrap.__ezoicFillObs) { oldWrap.__ezoicFillObs.disconnect(); } } catch (e) {}
568
+ try { oldWrap && oldWrap.remove(); } catch (e) {}
569
+ wrap = insertAfter(el, id, kindClass, afterPos);
570
+ if (!wrap) continue;
518
571
  setTimeout(() => { enqueueRetry(id); }, 450);
519
572
  } else {
520
573
  usedSet.add(id);