nodebb-plugin-ezoic-infinite 1.5.55 → 1.5.56

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 +58 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.5.55",
3
+ "version": "1.5.56",
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
@@ -25,10 +25,42 @@ function ensurePool() {
25
25
  pool.style.height = '1px';
26
26
  pool.style.overflow = 'hidden';
27
27
  pool.setAttribute('aria-hidden', 'true');
28
- try { (document.body || document.documentElement).appendChild(pool); } catch (e) {}
28
+ // Attach early (documentElement exists before body), so placeholders are always connected.
29
+ try { (document.documentElement || document.body).appendChild(pool); } catch (e) {}
30
+ // If body exists later, move it under body (purely cosmetic).
31
+ try {
32
+ if (document.body && pool.parentNode !== document.body) {
33
+ document.body.appendChild(pool);
34
+ }
35
+ } catch (e) {}
29
36
  return pool;
30
37
  }
31
38
 
39
+ // Create placeholder divs for all configured ids upfront.
40
+ // Ezoic sometimes attempts to initialize/refresh a range of ids even if we
41
+ // haven't injected them yet; keeping them in the offscreen pool prevents
42
+ // "HTML element ... does not exist" spam.
43
+ function primePool(ids) {
44
+ try {
45
+ if (!ids || !ids.length) return;
46
+ const pool = ensurePool();
47
+ for (const v of ids) {
48
+ const id = parseInt(v, 10);
49
+ if (!Number.isFinite(id) || id <= 0) continue;
50
+ const domId = `${PLACEHOLDER_PREFIX}${id}`;
51
+ let ph = document.getElementById(domId);
52
+ if (!ph) {
53
+ ph = document.createElement('div');
54
+ ph.id = domId;
55
+ ph.setAttribute('data-ezoic-id', String(id));
56
+ pool.appendChild(ph);
57
+ } else if (!ph.isConnected) {
58
+ pool.appendChild(ph);
59
+ }
60
+ }
61
+ } catch (e) {}
62
+ }
63
+
32
64
  function acquirePlaceholder(id) {
33
65
  const domId = `${PLACEHOLDER_PREFIX}${id}`;
34
66
  let ph = document.getElementById(domId);
@@ -409,6 +441,12 @@ function withInternalDomChange(fn) {
409
441
  if (state.allTopics.length === 0) state.allTopics = parsePool(cfg.placeholderIds);
410
442
  if (state.allPosts.length === 0) state.allPosts = parsePool(cfg.messagePlaceholderIds);
411
443
  if (state.allCategories.length === 0) state.allCategories = parsePool(cfg.categoryPlaceholderIds);
444
+
445
+ // Keep placeholders alive even before they're injected.
446
+ // This avoids Ezoic trying to touch ids that we haven't inserted yet.
447
+ primePool(state.allTopics);
448
+ primePool(state.allPosts);
449
+ primePool(state.allCategories);
412
450
  }
413
451
 
414
452
  // ---------- insertion primitives ----------
@@ -786,6 +824,25 @@ function startShow(id) {
786
824
  inserted += 1;
787
825
  }
788
826
 
827
+ // Safety: if DOM churn results in two consecutive ad wrappers, remove the latter.
828
+ // (This can happen when NodeBB inserts/removes spacer elements around the anchor.)
829
+ try {
830
+ const wraps = Array.from(document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`));
831
+ for (const w of wraps) {
832
+ const prev = w.previousElementSibling;
833
+ if (prev && prev.classList && prev.classList.contains(WRAP_CLASS)) {
834
+ withInternalDomChange(() => {
835
+ try {
836
+ const id = getWrapIdFromWrap(w);
837
+ if (id) safeDestroyById(id);
838
+ parkPlaceholderFromWrap(w);
839
+ w.remove();
840
+ } catch (e) {}
841
+ });
842
+ }
843
+ }
844
+ } catch (e) {}
845
+
789
846
  return inserted;
790
847
  }
791
848