nodebb-plugin-ezoic-infinite 1.5.24 → 1.5.26

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 +68 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.5.24",
3
+ "version": "1.5.26",
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
@@ -37,6 +37,9 @@
37
37
  // throttle per placeholder id
38
38
  lastShowById: new Map(),
39
39
 
40
+ // track placeholders that have been shown at least once in this pageview
41
+ usedOnce: new Set(),
42
+
40
43
  // observers / schedulers
41
44
  domObs: null,
42
45
  io: null,
@@ -48,6 +51,16 @@
48
51
 
49
52
  const insertingIds = new Set();
50
53
 
54
+ // Debug logs (enable with localStorage.ezoicInfiniteDebug = "1")
55
+ function dbg(...args) {
56
+ try {
57
+ if (window && window.localStorage && window.localStorage.getItem('ezoicInfiniteDebug') === '1') {
58
+ // eslint-disable-next-line no-console
59
+ console.log('[ezoicInfinite]', ...args);
60
+ }
61
+ } catch (e) {}
62
+ }
63
+
51
64
  // ---------- small utils ----------
52
65
 
53
66
  function normalizeBool(v) {
@@ -274,6 +287,34 @@
274
287
  return null;
275
288
  }
276
289
 
290
+
291
+ function removeOneOldWrap(kindClass) {
292
+ try {
293
+ const wraps = Array.from(document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`));
294
+ if (!wraps.length) return false;
295
+
296
+ // Prefer a wrap far above the viewport
297
+ let victim = null;
298
+ for (const w of wraps) {
299
+ const r = w.getBoundingClientRect();
300
+ if (r.bottom < -2000) { victim = w; break; }
301
+ }
302
+ // Otherwise remove the earliest one in the document
303
+ if (!victim) victim = wraps[0];
304
+
305
+ // Unobserve placeholder if still observed
306
+ try {
307
+ const ph = victim.querySelector && victim.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`);
308
+ if (ph && state.io) state.io.unobserve(ph);
309
+ } catch (e) {}
310
+
311
+ victim.remove();
312
+ return true;
313
+ } catch (e) {
314
+ return false;
315
+ }
316
+ }
317
+
277
318
  function showAd(id) {
278
319
  if (!id || EZOIC_BLOCKED) return;
279
320
 
@@ -292,7 +333,13 @@
292
333
 
293
334
  // Fast path
294
335
  if (typeof ez.showAds === 'function') {
336
+ try {
337
+ if (state.usedOnce && state.usedOnce.has(id) && typeof ez.destroyPlaceholders === 'function') {
338
+ ez.destroyPlaceholders(id);
339
+ }
340
+ } catch (e) {}
295
341
  ez.showAds(id);
342
+ try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
296
343
  return;
297
344
  }
298
345
 
@@ -305,7 +352,14 @@
305
352
  if (EZOIC_BLOCKED) return;
306
353
  const el = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
307
354
  if (!el || !el.isConnected) return;
308
- window.ezstandalone.showAds(id);
355
+ const ez2 = window.ezstandalone;
356
+ try {
357
+ if (state.usedOnce && state.usedOnce.has(id) && typeof ez2.destroyPlaceholders === 'function') {
358
+ ez2.destroyPlaceholders(id);
359
+ }
360
+ } catch (e) {}
361
+ ez2.showAds(id);
362
+ try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
309
363
  } catch (e) {}
310
364
  });
311
365
  }
@@ -373,7 +427,13 @@
373
427
  if (isAdjacentAd(el)) continue;
374
428
  if (findWrap(kindClass, afterPos)) continue;
375
429
 
376
- const id = pickIdFromAll(allIds, cursorKey);
430
+ let id = pickIdFromAll(allIds, cursorKey);
431
+ if (!id) {
432
+ // No free ids: recycle an old ad wrapper so we can reuse its placeholder id
433
+ const recycled = removeOneOldWrap(kindClass);
434
+ dbg('recycle-needed', kindClass, { recycled, ids: allIds.length });
435
+ id = pickIdFromAll(allIds, cursorKey);
436
+ }
377
437
  if (!id) break;
378
438
  const wrap = insertAfter(el, id, kindClass, afterPos);
379
439
  if (!wrap) {
@@ -390,7 +450,8 @@
390
450
  async function insertHeroAdEarly() {
391
451
  if (state.heroDoneForPage) return;
392
452
  const cfg = await fetchConfigOnce();
393
- if (!cfg || cfg.excluded) return;
453
+ if (!cfg) { dbg('no-config'); return; }
454
+ if (cfg.excluded) { dbg('excluded'); return; }
394
455
 
395
456
  initPools(cfg);
396
457
 
@@ -446,12 +507,13 @@
446
507
  }
447
508
 
448
509
  async function runCore() {
449
- if (EZOIC_BLOCKED) return;
510
+ if (EZOIC_BLOCKED) { dbg('blocked'); return; }
450
511
 
451
512
  patchShowAds();
452
513
 
453
514
  const cfg = await fetchConfigOnce();
454
- if (!cfg || cfg.excluded) return;
515
+ if (!cfg) { dbg('no-config'); return; }
516
+ if (cfg.excluded) { dbg('excluded'); return; }
455
517
  initPools(cfg);
456
518
 
457
519
  const kind = getKind();
@@ -524,6 +586,7 @@
524
586
  state.curPosts = 0;
525
587
  state.curCategories = 0;
526
588
  state.lastShowById.clear();
589
+ try { state.usedOnce && state.usedOnce.clear(); } catch (e) {}
527
590
  state.heroDoneForPage = false;
528
591
 
529
592
  // keep observers alive (MutationObserver will re-trigger after navigation)