nodebb-plugin-ezoic-infinite 1.5.58 → 1.5.60

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 -41
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.5.58",
3
+ "version": "1.5.60",
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
@@ -13,9 +13,24 @@
13
13
  // and eliminates "HTML element with id ... does not exist" noise.
14
14
  const POOL_ID = 'ezoic-placeholder-pool';
15
15
 
16
+ function isInPool(el) {
17
+ try { return !!(el && el.closest && el.closest('#' + POOL_ID)); } catch (e) { return false; }
18
+ }
19
+
20
+ function isPlaceholderInUse(ph) {
21
+ // In use = connected AND not parked in our offscreen pool.
22
+ try { return !!(ph && ph.isConnected && !isInPool(ph)); } catch (e) { return false; }
23
+ }
24
+
16
25
  function ensurePool() {
17
26
  let pool = document.getElementById(POOL_ID);
18
- if (pool) return pool;
27
+ if (pool) {
28
+ // In rare cases (aggressive SPA navigation), the pool may get detached.
29
+ if (!pool.isConnected) {
30
+ try { (document.documentElement || document.body).appendChild(pool); } catch (e) {}
31
+ }
32
+ return pool;
33
+ }
19
34
  pool = document.createElement('div');
20
35
  pool.id = POOL_ID;
21
36
  pool.style.position = 'absolute';
@@ -44,24 +59,9 @@ function primePool(ids) {
44
59
  try {
45
60
  if (!ids || !ids.length) return;
46
61
  const pool = ensurePool();
47
-
48
- // Normalize and compute bounds
49
- const arr = [];
50
62
  for (const v of ids) {
51
- const n = parseInt(v, 10);
52
- if (Number.isFinite(n) && n > 0) arr.push(n);
53
- }
54
- if (!arr.length) return;
55
-
56
- let min = arr[0], max = arr[0];
57
- for (const n of arr) { if (n < min) min = n; if (n > max) max = n; }
58
-
59
- // Ezoic sometimes touches ids in a contiguous range. If the configured list
60
- // looks like a range, pre-create all ids between min..max (bounded).
61
- const RANGE_LIMIT = 2000;
62
- const useRange = (max - min) <= RANGE_LIMIT;
63
-
64
- const ensureOne = (id) => {
63
+ const id = parseInt(v, 10);
64
+ if (!Number.isFinite(id) || id <= 0) continue;
65
65
  const domId = `${PLACEHOLDER_PREFIX}${id}`;
66
66
  let ph = document.getElementById(domId);
67
67
  if (!ph) {
@@ -72,12 +72,6 @@ function primePool(ids) {
72
72
  } else if (!ph.isConnected) {
73
73
  pool.appendChild(ph);
74
74
  }
75
- };
76
-
77
- if (useRange) {
78
- for (let id = min; id <= max; id++) ensureOne(id);
79
- } else {
80
- for (const id of arr) ensureOne(id);
81
75
  }
82
76
  } catch (e) {}
83
77
  }
@@ -90,14 +84,14 @@ function acquirePlaceholder(id) {
90
84
  ph.id = domId;
91
85
  ph.setAttribute('data-ezoic-id', String(id));
92
86
  ensurePool().appendChild(ph);
93
- }
94
- // Detach from wherever it currently is (pool or a previous wrap)
95
- try { if (ph.parentNode) ph.parentNode.removeChild(ph); } catch (e) {}
87
+ }
88
+ // Note: appendChild will automatically move the node, no manual detach (avoids race gaps).
96
89
  // Clear request/defined flags when reusing
97
90
  try {
98
91
  if (ph.dataset) {
99
92
  ph.dataset.ezRequested = '0';
100
93
  ph.dataset.ezDefined = '0';
94
+ ph.dataset.ezActive = '0';
101
95
  }
102
96
  } catch (e) {}
103
97
  return ph;
@@ -108,6 +102,7 @@ function parkPlaceholderFromWrap(wrap) {
108
102
  const ph = wrap && wrap.querySelector ? wrap.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`) : null;
109
103
  if (!ph) return;
110
104
  try { if (state && state.io) state.io.unobserve(ph); } catch (e) {}
105
+ try { if (ph.dataset) ph.dataset.ezActive = '0'; } catch (e) {}
111
106
  ensurePool().appendChild(ph);
112
107
  } catch (e) {}
113
108
  }
@@ -246,10 +241,11 @@ function parkPlaceholderFromWrap(wrap) {
246
241
 
247
242
  function parsePool(raw) {
248
243
  if (!raw) return [];
249
- // Accept newline, comma, semicolon, space separated lists.
250
- // Some ACP inputs may be stored as "611,612,613" in a single line.
251
- const tokens = String(raw).match(/\d+/g) || [];
252
- return uniqInts(tokens);
244
+ const lines = String(raw)
245
+ .split(/\r?\n/)
246
+ .map(s => s.trim())
247
+ .filter(Boolean);
248
+ return uniqInts(lines);
253
249
  }
254
250
 
255
251
  function getPageKey() {
@@ -342,8 +338,24 @@ function parkPlaceholderFromWrap(wrap) {
342
338
  if (!Number.isFinite(id) || id <= 0 || seen.has(id)) continue;
343
339
 
344
340
  const domId = `${PLACEHOLDER_PREFIX}${id}`;
345
- const ph = document.getElementById(domId);
346
- if (!ph || !ph.isConnected) continue;
341
+ let ph = document.getElementById(domId);
342
+ if (!ph || !ph.isConnected) {
343
+ // If Ezoic (or another script) tries to show an id we haven't injected yet,
344
+ // create the placeholder in the offscreen pool so it exists, but don't load.
345
+ try {
346
+ ph = document.createElement('div');
347
+ ph.id = domId;
348
+ ph.setAttribute('data-ezoic-id', String(id));
349
+ if (ph.dataset) ph.dataset.ezActive = '0';
350
+ ensurePool().appendChild(ph);
351
+ } catch (e) {}
352
+ continue;
353
+ }
354
+
355
+ // Only allow loads for placeholders actively injected into the page (not parked in the pool)
356
+ // and currently marked active.
357
+ if (!isPlaceholderInUse(ph)) continue;
358
+ if (ph.dataset && ph.dataset.ezActive !== '1') continue;
347
359
 
348
360
  // Prevent repeated "define" attempts on the same placeholder while it remains in DOM.
349
361
  if (ph.dataset && (ph.dataset.ezRequested === '1' || ph.dataset.ezDefined === '1')) continue;
@@ -566,6 +578,7 @@ function buildWrap(id, kindClass, afterPos) {
566
578
  wrap.style.width = '100%';
567
579
 
568
580
  const ph = acquirePlaceholder(id);
581
+ try { if (ph.dataset) ph.dataset.ezActive = '1'; } catch (e) {}
569
582
  wrap.appendChild(ph);
570
583
 
571
584
  return wrap;
@@ -581,7 +594,7 @@ function buildWrap(id, kindClass, afterPos) {
581
594
  if (insertingIds.has(id)) return null;
582
595
 
583
596
  const existingPh = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
584
- if (existingPh && existingPh.isConnected) return null;
597
+ if (existingPh && isPlaceholderInUse(existingPh)) return null;
585
598
 
586
599
  insertingIds.add(id);
587
600
  try {
@@ -604,7 +617,7 @@ function buildWrap(id, kindClass, afterPos) {
604
617
 
605
618
  const id = allIds[idx];
606
619
  const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
607
- if (ph && ph.isConnected) continue;
620
+ if (ph && isPlaceholderInUse(ph)) continue;
608
621
 
609
622
  return id;
610
623
  }
@@ -709,16 +722,14 @@ function startShow(id) {
709
722
  const ph = document.getElementById(domId);
710
723
  if (!ph || !ph.isConnected) return;
711
724
 
712
- // If this id was used before, destroy it first to avoid "already defined".
713
- // Then schedule showAds on the next tick (gives Ezoic time to clear state).
725
+ // If this id was used before, destroy safely using full DOM id.
714
726
  if (state.usedOnce && state.usedOnce.has(id)) {
715
727
  safeDestroyById(id);
716
- setTimeout(() => { try { ez.showAds(id); } catch (e) {} }, 0);
717
- } else {
718
- // First time: show immediately
719
- ez.showAds(id);
720
728
  }
721
729
 
730
+ // showAds is patched to ignore missing placeholders and repeated defines.
731
+ ez.showAds(id);
732
+
722
733
  try { state.usedOnce && state.usedOnce.add(id); } catch (e) {}
723
734
  } catch (e) {}
724
735
  finally {