nodebb-plugin-ezoic-infinite 1.5.67 → 1.5.68

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 +52 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.5.67",
3
+ "version": "1.5.68",
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
@@ -100,6 +100,36 @@
100
100
 
101
101
  const insertingIds = new Set();
102
102
 
103
+ // Hidden pool where we keep placeholder DIVs when they are not currently
104
+ // mounted in the content flow. This avoids:
105
+ // - Ezoic trying to act on ids that were removed from the DOM ("does not exist")
106
+ // - re-defining placeholders (we re-use the same node)
107
+ const POOL_ID = 'nodebb-ezoic-placeholder-pool';
108
+ function getPoolEl() {
109
+ let el = document.getElementById(POOL_ID);
110
+ if (el) return el;
111
+ el = document.createElement('div');
112
+ el.id = POOL_ID;
113
+ el.style.cssText = 'position:fixed;left:-99999px;top:-99999px;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none;';
114
+ (document.body || document.documentElement).appendChild(el);
115
+ return el;
116
+ }
117
+
118
+ function isInPool(ph) {
119
+ try { return !!(ph && ph.closest && ph.closest('#' + POOL_ID)); } catch (e) { return false; }
120
+ }
121
+
122
+ function releaseWrapNode(wrap) {
123
+ try {
124
+ const ph = wrap && wrap.querySelector && wrap.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`);
125
+ if (ph) {
126
+ try { getPoolEl().appendChild(ph); } catch (e) {}
127
+ try { if (state.io) state.io.unobserve(ph); } catch (e) {}
128
+ }
129
+ } catch (e) {}
130
+ try { wrap && wrap.remove && wrap.remove(); } catch (e) {}
131
+ }
132
+
103
133
 
104
134
  function markEmptyWrapper(id) {
105
135
  try {
@@ -354,11 +384,11 @@ function withInternalDomChange(fn) {
354
384
  }
355
385
 
356
386
  if (!ok) {
357
- const id = getWrapIdFromWrap(wrap);
358
387
  withInternalDomChange(() => {
359
388
  try {
360
- if (id) safeDestroyById(id);
361
- wrap.remove();
389
+ // Do not destroy placeholders; move them back to the pool so
390
+ // Ezoic won't log "does not exist" and we can reuse them later.
391
+ releaseWrapNode(wrap);
362
392
  } catch (e) {}
363
393
  });
364
394
  removed++;
@@ -384,17 +414,19 @@ function withInternalDomChange(fn) {
384
414
  }, 3500);
385
415
  }
386
416
 
387
- function buildWrap(id, kindClass, afterPos) {
417
+ function buildWrap(id, kindClass, afterPos, createPlaceholder) {
388
418
  const wrap = document.createElement('div');
389
419
  wrap.className = `${WRAP_CLASS} ${kindClass}`;
390
420
  wrap.setAttribute('data-ezoic-after', String(afterPos));
391
421
  wrap.setAttribute('data-ezoic-wrapid', String(id));
392
422
  wrap.style.width = '100%';
393
423
 
394
- const ph = document.createElement('div');
395
- ph.id = `${PLACEHOLDER_PREFIX}${id}`;
396
- ph.setAttribute('data-ezoic-id', String(id));
397
- wrap.appendChild(ph);
424
+ if (createPlaceholder) {
425
+ const ph = document.createElement('div');
426
+ ph.id = `${PLACEHOLDER_PREFIX}${id}`;
427
+ ph.setAttribute('data-ezoic-id', String(id));
428
+ wrap.appendChild(ph);
429
+ }
398
430
 
399
431
  return wrap;
400
432
  }
@@ -412,16 +444,20 @@ function buildWrap(id, kindClass, afterPos) {
412
444
 
413
445
  insertingIds.add(id);
414
446
  try {
415
- const wrap = buildWrap(id, kindClass, afterPos);
447
+ // If a placeholder already exists (either in content or in our pool),
448
+ // do NOT create a new DOM node with the same id even temporarily.
449
+ const wrap = buildWrap(id, kindClass, afterPos, !existingPh);
416
450
  target.insertAdjacentElement('afterend', wrap);
417
451
 
418
452
  // If a placeholder with this id already exists elsewhere (some Ezoic flows
419
453
  // pre-create placeholders), move it into our wrapper instead of aborting.
420
454
  // replaceChild moves the node atomically (no detach window).
421
- if (existingPh && existingPh !== wrap.firstElementChild) {
455
+ if (existingPh) {
422
456
  try {
423
457
  existingPh.setAttribute('data-ezoic-id', String(id));
424
- wrap.replaceChild(existingPh, wrap.firstElementChild);
458
+ // If we didn't create a placeholder, just append.
459
+ if (!wrap.firstElementChild) wrap.appendChild(existingPh);
460
+ else wrap.replaceChild(existingPh, wrap.firstElementChild);
425
461
  } catch (e) {
426
462
  // Keep the new placeholder if replace fails.
427
463
  }
@@ -443,7 +479,9 @@ function buildWrap(id, kindClass, afterPos) {
443
479
 
444
480
  const id = allIds[idx];
445
481
  const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
446
- if (ph && ph.isConnected) continue;
482
+ // If placeholder is currently mounted in the content flow, skip.
483
+ // If it's in our hidden pool, it's available for reuse.
484
+ if (ph && ph.isConnected && !isInPool(ph)) continue;
447
485
 
448
486
  return id;
449
487
  }
@@ -465,13 +503,7 @@ function buildWrap(id, kindClass, afterPos) {
465
503
  // Otherwise remove the earliest one in the document
466
504
  if (!victim) victim = wraps[0];
467
505
 
468
- // Unobserve placeholder if still observed
469
- try {
470
- const ph = victim.querySelector && victim.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`);
471
- if (ph && state.io) state.io.unobserve(ph);
472
- } catch (e) {}
473
-
474
- victim.remove();
506
+ releaseWrapNode(victim);
475
507
  return true;
476
508
  } catch (e) {
477
509
  return false;
@@ -625,15 +657,6 @@ function startShow(id) {
625
657
  if (i % interval === 0) out.push(i);
626
658
  }
627
659
  return Array.from(new Set(out)).sort((a, b) => a - b);
628
- // If we inserted the maximum batch, likely there are more targets.
629
- // Schedule a follow-up pass (bounded via scheduleRun coalescing).
630
- const maxInserts = MAX_INSERTS_PER_RUN + (isBoosted() ? 1 : 0);
631
- if (insertedThisRun >= maxInserts) {
632
- scheduleRun(120);
633
- scheduleRun(420);
634
- }
635
- }
636
-
637
660
  }
638
661
 
639
662
  function injectBetween(kindClass, items, interval, showFirst, allIds, cursorKey) {
@@ -838,7 +861,7 @@ function startShow(id) {
838
861
  // remove all wrappers
839
862
  try {
840
863
  document.querySelectorAll(`.${WRAP_CLASS}`).forEach((el) => {
841
- try { el.remove(); } catch (e) {}
864
+ releaseWrapNode(el);
842
865
  });
843
866
  } catch (e) {}
844
867